From c760d61b04fc5825044b7a7801dba4bb1d61984d Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 6 Aug 2026 08:10:08 +0000 Subject: [PATCH 01/80] feat: unify the SDK with the Seam SDKs for other languages The Python, Ruby, and JavaScript SDKs share a runtime core, a common README skeleton, and a test suite aligned to the same baseline. This SDK had the codegen and release tooling but not the runtime surface. This brings it in line. Added: - Personal access token authentication, with the seam-workspace header, and token format validation that rejects client session tokens, JWTs, and publishable keys with a specific message. - from_api_key, from_personal_access_token, and from_client factories. - SeamMultiWorkspace for the endpoints that are not scoped to a workspace. - SeamWebhook, verifying incoming webhooks with svix. - SEAM_ENDPOINT support, plus the deprecated SEAM_API_URL and its warnings. - Retries, two by default with exponential backoff, via caseyamcl/guzzle_retry_middleware. A request that never reached the server is always retried; a status code is only retried for idempotent methods, since retrying a POST the server may already have processed could duplicate a write. The other Seam SDKs make the same trade. - HTTP layer configuration: guzzle_options, retries, and an injectable client. - A client level wait_for_action_attempt default, accepting a bool or a timeout and polling_interval. - A test suite covering auth, env, headers, errors, malformed responses, retries, pagination, serialization, action attempts, and webhooks, run against @seamapi/fake-seam-connect. - Psalm, wired into composer lint. Fixed: - Responses in the 3xx range were treated as successful. - The Seam error check accepted any body with a truthy error key. It now checks the content type and that error.type and error.message are strings, matching the other SDKs. - throw_http_errors let Guzzle throw before the SDK could map the error, making the whole error mapping unreachable. The option is gone. - Malformed JSON silently decoded to null and then failed on property access. - Non-Seam error responses raised an exception built from a fabricated request rather than the real one. - getRequestId returned an empty string rather than null when the header was absent, and the fallback error type was unknown rather than unknown_error. - HttpInvalidInputError never actually overrode the error code, and the action attempt errors wrote to an undeclared property. - Paginator::firstPage indexed its cache unconditionally, and the null cursor guard was unreachable. BREAKING CHANGE: The client is Seam\Seam; Seam\SeamClient remains as a deprecated alias. The constructor takes named options, so endpoint is no longer the second positional argument, and throw_http_errors is removed. Exceptions moved to the Seam\Exceptions namespace. poll_until_ready is removed in favor of wait_for_action_attempt, whose defaults change from 20s/0.4s to 10s/1s. $seam->client is a Seam\Http\SeamHttpClient rather than a Guzzle client. The $api_key property and the global LTS_VERSION constant are removed. Responses in the 3xx range are no longer treated as successful. Requests are now retried. Pagination metadata is a Seam\Pagination object. PHP 8.1 or later is required, and svix/svix is a new dependency. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- .gitattributes | 2 +- .github/workflows/check.yml | 12 +- .gitignore | 2 +- README.md | 374 +- codegen/layouts/partials/client-class.hbs | 20 +- codegen/layouts/partials/poll-until-ready.hbs | 24 - codegen/layouts/partials/route-method.hbs | 24 +- codegen/layouts/seam-client.hbs | 212 +- codegen/lib/handlebars-helpers.ts | 10 +- codegen/lib/layouts/route.ts | 115 +- codegen/lib/routes.ts | 2 +- codegen/smith.ts | 2 +- composer.json | 27 +- composer.lock | 5112 ++++++++++++++--- package-lock.json | 105 + package.json | 4 +- phpunit.xml.dist | 15 +- psalm.xml | 20 + src/Auth.php | 149 + src/Exceptions/ActionAttemptError.php | 8 +- src/Exceptions/ActionAttemptFailedError.php | 20 +- src/Exceptions/ActionAttemptTimeoutError.php | 9 +- src/Exceptions/HttpApiError.php | 28 +- src/Exceptions/HttpInvalidInputError.php | 13 +- src/Exceptions/HttpUnauthorizedError.php | 20 +- src/Exceptions/InvalidOptionsError.php | 16 + src/Exceptions/InvalidTokenError.php | 15 + src/Exceptions/SeamException.php | 9 + src/Http/ResolveActionAttempt.php | 103 + src/Http/SeamHttpClient.php | 287 + src/Options.php | 103 + src/Pagination.php | 32 + src/Paginator.php | 57 +- src/Routes/AccessCodesClient.php | 74 +- src/Routes/AccessCodesSimulateClient.php | 33 +- src/Routes/AccessCodesUnmanagedClient.php | 48 +- src/Routes/AccessGrantsClient.php | 58 +- src/Routes/AccessGrantsUnmanagedClient.php | 36 +- src/Routes/AccessMethodsClient.php | 115 +- src/Routes/AccessMethodsUnmanagedClient.php | 29 +- src/Routes/AcsAccessGroupsClient.php | 55 +- src/Routes/AcsClient.php | 29 +- src/Routes/AcsCredentialsClient.php | 62 +- src/Routes/AcsEncodersClient.php | 95 +- src/Routes/AcsEncodersSimulateClient.php | 41 +- src/Routes/AcsEntrancesClient.php | 65 +- src/Routes/AcsSystemsClient.php | 37 +- src/Routes/AcsUsersClient.php | 60 +- src/Routes/ActionAttemptsClient.php | 83 +- src/Routes/ClientSessionsClient.php | 39 +- src/Routes/ConnectWebviewsClient.php | 34 +- src/Routes/ConnectedAccountsClient.php | 45 +- .../ConnectedAccountsSimulateClient.php | 23 +- src/Routes/CustomersClient.php | 27 +- src/Routes/DevicesClient.php | 40 +- src/Routes/DevicesSimulateClient.php | 57 +- src/Routes/DevicesUnmanagedClient.php | 28 +- src/Routes/EventsClient.php | 21 +- src/Routes/InstantKeysClient.php | 27 +- src/Routes/LocksClient.php | 95 +- src/Routes/LocksSimulateClient.php | 66 +- src/Routes/NoiseSensorsClient.php | 27 +- .../NoiseSensorsNoiseThresholdsClient.php | 63 +- src/Routes/NoiseSensorsSimulateClient.php | 23 +- src/Routes/PhonesClient.php | 33 +- src/Routes/PhonesSimulateClient.php | 23 +- src/Routes/SpacesClient.php | 98 +- src/Routes/ThermostatsClient.php | 260 +- src/Routes/ThermostatsDailyProgramsClient.php | 80 +- src/Routes/ThermostatsSchedulesClient.php | 65 +- src/Routes/ThermostatsSimulateClient.php | 33 +- src/Routes/UserIdentitiesClient.php | 107 +- src/Routes/UserIdentitiesUnmanagedClient.php | 36 +- src/Routes/WebhooksClient.php | 47 +- src/Routes/WorkspacesClient.php | 48 +- src/Seam.php | 256 + src/SeamClient.php | 156 +- src/SeamMultiWorkspace.php | 91 + src/SeamWebhook.php | 53 + src/Token.php | 67 + src/WorkspacesProxy.php | 54 + tests/ApiKeyTest.php | 89 + tests/ClientTest.php | 168 + tests/EnvTest.php | 143 + tests/HeadersTest.php | 141 + tests/HttpErrorTest.php | 117 +- tests/MalformedResponseTest.php | 112 + tests/PackageVersionTest.php | 4 +- tests/PaginatorTest.php | 123 + tests/PersonalAccessTokenTest.php | 140 + tests/RetryTest.php | 139 + tests/SeamWebhookTest.php | 103 + tests/SerializationTest.php | 55 + tests/Support/FakeSeamConnect.php | 171 + tests/Support/FakeSeamConnectTestCase.php | 45 + tests/Support/RecordingClient.php | 107 + tests/WaitForActionAttemptTest.php | 283 + 97 files changed, 9326 insertions(+), 2607 deletions(-) delete mode 100644 codegen/layouts/partials/poll-until-ready.hbs create mode 100644 psalm.xml create mode 100644 src/Auth.php create mode 100644 src/Exceptions/InvalidOptionsError.php create mode 100644 src/Exceptions/InvalidTokenError.php create mode 100644 src/Exceptions/SeamException.php create mode 100644 src/Http/ResolveActionAttempt.php create mode 100644 src/Http/SeamHttpClient.php create mode 100644 src/Options.php create mode 100644 src/Pagination.php create mode 100644 src/Seam.php create mode 100644 src/SeamMultiWorkspace.php create mode 100644 src/SeamWebhook.php create mode 100644 src/Token.php create mode 100644 src/WorkspacesProxy.php create mode 100644 tests/ApiKeyTest.php create mode 100644 tests/ClientTest.php create mode 100644 tests/EnvTest.php create mode 100644 tests/HeadersTest.php create mode 100644 tests/MalformedResponseTest.php create mode 100644 tests/PaginatorTest.php create mode 100644 tests/PersonalAccessTokenTest.php create mode 100644 tests/RetryTest.php create mode 100644 tests/SeamWebhookTest.php create mode 100644 tests/SerializationTest.php create mode 100644 tests/Support/FakeSeamConnect.php create mode 100644 tests/Support/FakeSeamConnectTestCase.php create mode 100644 tests/Support/RecordingClient.php create mode 100644 tests/WaitForActionAttemptTest.php diff --git a/.gitattributes b/.gitattributes index 641e8a3b..a4e1b5c5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,6 @@ src/Resources/*.php linguist-generated src/Routes/*.php linguist-generated -src/SeamClient.php linguist-generated +src/Seam.php linguist-generated # Keep development files out of the published package. # GitHub builds the archives Composer downloads as dist with git archive, diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 95ca05d1..749b0e39 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,7 +20,10 @@ jobs: os: - ubuntu-latest php: - - '8.0' + - '8.1' + - '8.2' + - '8.3' + - '8.4' - '8.5' include: - os: ubuntu-latest @@ -44,7 +47,7 @@ jobs: fail-fast: false matrix: php: - - '8.0' + - '8.1' - '8.5' steps: - name: Checkout @@ -73,7 +76,10 @@ jobs: os: - ubuntu-latest php: - - '8.0' + - '8.1' + - '8.2' + - '8.3' + - '8.4' - '8.5' include: - os: ubuntu-latest diff --git a/.gitignore b/.gitignore index 78aaf436..b75b485f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ vendor tmp # PHPUnit result cache -.phpunit.result.cache +.phpunit.cache/ # Build directories package diff --git a/README.md b/README.md index 567a864c..35ef6120 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,167 @@ # Seam PHP SDK -Control locks, lights and other internet of things devices with Seam's simple API. +[![Packagist](https://img.shields.io/packagist/v/seamapi/seam.svg)](https://packagist.org/packages/seamapi/seam) +[![Seam LTS Version](https://img.shields.io/badge/Seam_LTS-1.0.0-blue)](https://docs.seam.co/lts) +[![GitHub Actions](https://github.com/seamapi/php/actions/workflows/check.yml/badge.svg)](https://github.com/seamapi/php/actions/workflows/check.yml) -Check out [the documentation](https://docs.seam.co) or the usage below. +PHP SDK for the Seam API. + +## Description + +[Seam] makes it easy to integrate IoT devices with your applications. +This is an official SDK for the Seam API. +Please refer to the official [Seam Docs] to get started. + +Parts of this SDK are generated from always up-to-date type information +provided by [@seamapi/types]. +This ensures all API methods, request shapes, and response shapes are +accurate and fully typed. + +The underlying HTTP client is [Guzzle]. + +[Seam]: https://www.seam.co/ +[Seam Docs]: https://docs.seam.co/latest/ +[@seamapi/types]: https://github.com/seamapi/types/ +[Guzzle]: https://docs.guzzlephp.org/ + +## Installation + +Add this as a dependency to your project using [Composer] with + +``` +$ composer require seamapi/seam +``` + +[Composer]: https://getcomposer.org/ ## Usage +> [!NOTE] +> These examples assume `SEAM_API_KEY` is set in your environment. + +### Examples + +#### List devices + ```php -$seam = new Seam\SeamClient("YOUR_API_KEY"); +$seam = new Seam\Seam(); -# Create a Connect Webview to login to a provider -$connect_webview = $seam->connect_webviews->create( - accepted_providers: ["august"] -); +$devices = $seam->devices->list(); +``` -print "Please Login at this url: " . $connect_webview->url; +#### Unlock a door -# Poll until connect webview is completed -while (true) { - $connect_webview = $seam->connect_webviews->get( - $connect_webview->connect_webview_id - ); - if ($connect_webview->status == "authorized") { - break; - } else { - sleep(1); - } -} +```php +$seam = new Seam\Seam(); -$connected_account = $seam->connected_accounts->get( - $connect_webview->connected_account_id -); +$lock = $seam->locks->get(name: "Front Door"); +$seam->locks->unlock_door($lock->device_id); +``` + +### Authentication Method + +The SDK supports two authentication mechanisms. +Configure either by passing the corresponding options to the `Seam` +constructor, or with the more ergonomic static factory methods. -print "Looks like you connected with " . - json_encode($connected_account->user_identifier); +#### API Key + +An API key is scoped to a single workspace and should only be used on the +server. Obtain one from the Seam Console. + +```php +// Set the SEAM_API_KEY environment variable +$seam = new Seam\Seam(); -$devices = $seam->devices->list( - connected_account_id: $connected_account->connected_account_id +// Pass as an option to the constructor +$seam = new Seam\Seam(api_key: "your-api-key"); + +// Use the factory method +$seam = Seam\Seam::from_api_key("your-api-key"); +``` + +#### Personal Access Token + +A Personal Access Token is scoped to a Seam Console user. +It must be used with a workspace id. + +```php +// Pass as options to the constructor +$seam = new Seam\Seam( + personal_access_token: "your-personal-access-token", + workspace_id: "your-workspace-id" ); -print "You have " . count($devices) . " devices"; +// Use the factory method +$seam = Seam\Seam::from_personal_access_token( + "your-personal-access-token", + "your-workspace-id" +); +``` + +### Action Attempts + +Some operations tell a device to do something, and the device may take time +to report back. Those endpoints return an [action attempt]. + +By default the SDK waits for the action attempt to finish: + +- It polls up to a timeout at a polling interval. +- It returns a fresh copy of the successful action attempt. +- It throws `Seam\Exceptions\ActionAttemptFailedError` if the action failed. +- It throws `Seam\Exceptions\ActionAttemptTimeoutError` if the timeout + elapses first. + +Both errors extend `Seam\Exceptions\ActionAttemptError` and expose the action +attempt with `getActionAttempt()`. -$device_id = $devices[0]->device_id; +[action attempt]: https://docs.seam.co/latest/core-concepts/action-attempts -# Lock a Door -$seam->locks->lock_door($device_id); +```php +use Seam\Exceptions\ActionAttemptFailedError; +use Seam\Exceptions\ActionAttemptTimeoutError; + +try { + $seam->locks->unlock_door($device_id); +} catch (ActionAttemptFailedError $error) { + print "Could not unlock the door: " . $error->getMessage(); + print "Error code: " . $error->getErrorCode(); +} catch (ActionAttemptTimeoutError $error) { + print "The door did not unlock in time"; + print "Action attempt: " . $error->getActionAttempt()->action_attempt_id; +} +``` + +Waiting may be disabled for the whole client: -$updated_device = $seam->devices->get($device_id); -$updated_device->properties->locked; // true +```php +$seam = new Seam\Seam(wait_for_action_attempt: false); + +$action_attempt = $seam->locks->unlock_door($device_id); +$action_attempt->status; // "pending" +``` -# Unlock a Door -$seam->locks->unlock_door($device_id); -$updated_device->properties->locked; // false +or for a single request: -# Create an access code on a device -$access_code = $seam->access_codes->create( - device_id: $device_id, - code: "1234", - name: "Test Code" +```php +$action_attempt = $seam->locks->unlock_door( + $device_id, + wait_for_action_attempt: false ); +``` + +The timeout and polling interval, both in seconds, may be configured either +on the client or per request: -# Check the status of an access code -$access_code->status; // 'setting' (it will go to 'set' when active on the device) +```php +$seam = new Seam\Seam( + wait_for_action_attempt: ["timeout" => 30.0, "polling_interval" => 2.0] +); -$seam->access_codes->delete($access_code->access_code_id); +$seam->locks->unlock_door( + $device_id, + wait_for_action_attempt: ["timeout" => 5.0] +); ``` ### Pagination @@ -153,21 +251,171 @@ $pages = $seam->createPaginator( $connectedAccounts = $pages->flattenToArray(); ``` -## Installation +### Interacting with Multiple Workspaces + +Some endpoints are not scoped to a workspace. Use `SeamMultiWorkspace` with a +personal access token to reach them. + +```php +$seam = Seam\SeamMultiWorkspace::from_personal_access_token( + "your-personal-access-token" +); + +// List workspaces authorized for this Personal Access Token +$workspaces = $seam->workspaces->list(); + +$workspace = $seam->workspaces->create( + name: "New Workspace", + connect_partner_name: "Your Company" +); +``` + +### Webhooks + +Seam delivers webhooks with [Svix]. Verify and parse an incoming request with +`SeamWebhook`, which returns the typed event. + +[Svix]: https://www.svix.com/ + +```php +$webhook = new Seam\SeamWebhook($webhook_secret); + +try { + $event = $webhook->verify($request_body, $request_headers); + print $event->event_type; +} catch (Svix\Exception\WebhookVerificationException $error) { + http_response_code(400); +} +``` + +### Advanced Usage + +#### Setting the endpoint + +The endpoint may be set with the `SEAM_ENDPOINT` environment variable, or +passed directly. + +```php +$seam = new Seam\Seam(endpoint: "https://example.com"); +``` + +#### Configuring the Guzzle client + +Pass any [Guzzle request option] with `guzzle_options`. They are merged into +the client the SDK builds, so the authorization and SDK headers are kept. + +[Guzzle request option]: https://docs.guzzlephp.org/en/stable/request-options.html -To install the latest version of the automatically generated SDK, run: +```php +$seam = new Seam\Seam( + guzzle_options: [ + "timeout" => 30, + "headers" => ["X-Custom-Header" => "value"], + "proxy" => "http://localhost:8125", + ] +); +``` + +> [!NOTE] +> Unlike the other Seam SDKs, this one sets a default request timeout of 60 +> seconds so a hung connection eventually fails. Pass `timeout` to change it, +> or `0` to disable it. + +#### Retries + +Failed requests are retried twice by default, with exponential backoff. + +A request that never reached the server, e.g. a connection failure, is always +retried. A request that did reach the server is only retried on a retryable +status when the HTTP method is idempotent. Every Seam endpoint is a `POST`, so +retrying one that the server may already have processed could duplicate a +write. The other Seam SDKs make the same trade. + +```php +// Retry more times +$seam = new Seam\Seam(retries: 5); + +// Turn retries off +$seam = new Seam\Seam(retries: 0); +``` + +#### Overriding the client + +Pass an already configured Guzzle client. It carries its own endpoint and +authorization, so no other authentication option may be given alongside it. -`composer require seamapi/seam` +```php +$client = new GuzzleHttp\Client([ + "base_uri" => "https://connect.getseam.com", + "headers" => ["authorization" => "Bearer " . $api_key], +]); + +$seam = Seam\Seam::from_client($client); +``` + +#### Errors -If you want to install our previous handwritten version, run: +Every exception the SDK raises implements `Seam\Exceptions\SeamException`. -`composer require seamapi/seam:1.1` +| Error | Raised when | +| --------------------------- | -------------------------------------------------- | +| `HttpApiError` | The API returned an error response. | +| `HttpUnauthorizedError` | The credentials were rejected. | +| `HttpInvalidInputError` | The request parameters were rejected. | +| `InvalidOptionsError` | The client options are incomplete or incompatible. | +| `InvalidTokenError` | The token is of the wrong kind or format. | +| `ActionAttemptFailedError` | An action attempt finished in the error state. | +| `ActionAttemptTimeoutError` | An action attempt did not finish in time. | + +An error response that is not shaped like a Seam error, e.g. a gateway +returning HTML, surfaces as the underlying `GuzzleHttp\Exception\ +BadResponseException` instead. Transport failures surface as the +corresponding Guzzle exception. + +```php +use Seam\Exceptions\HttpApiError; +use Seam\Exceptions\HttpInvalidInputError; + +try { + $seam->devices->get($device_id); +} catch (HttpInvalidInputError $error) { + print_r($error->getValidationErrorMessages("device_id")); +} catch (HttpApiError $error) { + print $error->getErrorCode(); + print $error->getStatusCode(); + print $error->getRequestId(); +} +``` + +## Upgrading from 3.x + +Version 4 brings this SDK in line with the Seam SDKs for other languages. + +- PHP 8.1 or later is now required. +- The client class is `Seam\Seam`. `Seam\SeamClient` still works as a + deprecated alias. +- The constructor takes named options. `$endpoint` is no longer the second + positional argument, and `$throw_http_errors` is gone; API errors always + raise a Seam exception. +- Exceptions moved to the `Seam\Exceptions` namespace, e.g. + `Seam\HttpApiError` is now `Seam\Exceptions\HttpApiError`. +- `$seam->action_attempts->poll_until_ready()` was removed. Use + `wait_for_action_attempt`, which now also accepts a `timeout` and + `polling_interval`. The defaults changed from 20s/0.4s to 10s/1s. +- `$seam->client` is a `Seam\Http\SeamHttpClient`. The Guzzle client is + available with `$seam->client->get_client()`. +- The `$seam->api_key` property and the global `LTS_VERSION` constant were + removed. Use `Seam\Seam::LTS_VERSION`. +- Responses in the 3xx range are no longer treated as successful. +- Requests are now retried; see [Retries](#retries). +- Pagination metadata is a `Seam\Pagination` object rather than a + `stdClass`. ## Development and Testing ### Quickstart -Install [PHP](https://www.php.net/) 8.0 or later, +Install [PHP](https://www.php.net/) 8.1 or later, [Composer](https://getcomposer.org/) and [Node.js](https://nodejs.org/), then run @@ -186,13 +434,13 @@ View them with $ composer run-script --list ``` -| Task | Command | -| ----------------- | ------------------ | -| Run the tests | `composer test` | -| Lint | `composer lint` | -| Format | `npm run format` | -| Build the package | `composer build` | -| Generate the SDK | `npm run generate` | +| Task | Command | +| ---------------------- | ------------------ | +| Run the tests | `composer test` | +| Lint and analyze types | `composer lint` | +| Format | `npm run format` | +| Build the package | `composer build` | +| Generate the SDK | `npm run generate` | Formatting is handled by [Prettier](https://prettier.io/) via [@prettier/plugin-php](https://github.com/prettier/plugin-php), @@ -215,10 +463,20 @@ $ composer test -- tests/MyTest.php PHPUnit is configured in `phpunit.xml.dist`. +Static analysis is handled by [Psalm](https://psalm.dev/), configured in +`psalm.xml` and run as part of `composer lint`. The generated sources under +`src/Resources` and `src/Routes` are excluded, since analyzing them would only +create pressure to change the generator. + ### Requirements -This package supports PHP 8.0 and later. -Continuous integration exercises both ends of that range, PHP 8.0 and 8.5. +This package supports PHP 8.1 and later. +Continuous integration exercises every supported version, PHP 8.1 through 8.5. + +The test suite runs against [@seamapi/fake-seam-connect], which is started +automatically for each test, so `npm install` must have been run first. + +[@seamapi/fake-seam-connect]: https://github.com/seamapi/fake-seam-connect ### Publishing diff --git a/codegen/layouts/partials/client-class.hbs b/codegen/layouts/partials/client-class.hbs index a205fc3f..79d6dd3d 100644 --- a/codegen/layouts/partials/client-class.hbs +++ b/codegen/layouts/partials/client-class.hbs @@ -1,6 +1,11 @@ class {{clientName}}Client { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; {{#if hasChildClients}} {{#each childClients}} public {{clientName}}Client ${{namespace}}; @@ -8,18 +13,19 @@ class {{clientName}}Client {{else}} {{/if}} - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; {{#each childClients}} - $this->{{namespace}} = new {{clientName}}Client($seam); + $this->{{namespace}} = new {{clientName}}Client($client, $defaults); {{/each}} } {{#each methods}} {{> route-method}} {{/each}} -{{#if isActionAttempts}} -{{> poll-until-ready}} -{{/if}} } diff --git a/codegen/layouts/partials/poll-until-ready.hbs b/codegen/layouts/partials/poll-until-ready.hbs deleted file mode 100644 index 87ce726d..00000000 --- a/codegen/layouts/partials/poll-until-ready.hbs +++ /dev/null @@ -1,24 +0,0 @@ - public function poll_until_ready(string $action_attempt_id, float $timeout = 20.0): ActionAttempt - { - $seam = $this->seam; - $time_waiting = 0.0; - $polling_interval = 0.4; - $action_attempt = $seam->action_attempts->get($action_attempt_id); - - while ($action_attempt->status == 'pending') { - $action_attempt = $seam->action_attempts->get( - $action_attempt->action_attempt_id - ); - if ($time_waiting > $timeout) { - throw new ActionAttemptTimeoutError($action_attempt, $timeout); - } - $time_waiting += $polling_interval; - usleep($polling_interval * 1000000); - } - - if ($action_attempt->status == 'error') { - throw new ActionAttemptFailedError($action_attempt); - } - - return $action_attempt; - } diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 9c73dfd9..bf344f74 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -3,14 +3,18 @@ {{#if hasParams}} $request_payload = []; -{{#each paramNames}} - if (${{this}} !== null) { - $request_payload["{{this}}"] = ${{this}}; +{{#each parameters}} +{{#if required}} + $request_payload["{{name}}"] = ${{name}}; +{{else}} + if (${{name}} !== null) { + $request_payload["{{name}}"] = ${{name}}; } +{{/if}} {{/each}} {{/if}} - {{#unless returnsVoid}}$res = {{/unless}}$this->seam->request( + {{#unless returnsVoid}}$res = {{/unless}}$this->client->request( "POST", "{{path}}", {{#if hasParams}} @@ -19,13 +23,11 @@ ); {{#if usesActionAttempt}} - if (!$wait_for_action_attempt) { - return {{returnResource}}::from_json($res->{{returnPath}}); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready($res->action_attempt->action_attempt_id); - - return $action_attempt; + return ResolveActionAttempt::resolve_action_attempt( + {{returnResource}}::from_json($res->{{returnPath}}), + $this->client, + $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"] + ); {{else}} {{#if usesOnResponse}} diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index d39a7c01..c452d625 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -5,96 +5,158 @@ namespace Seam; {{#each useStatements}} use {{this}}; {{/each}} -use Seam\Utils\PackageVersion; -use GuzzleHttp\Client as HTTPClient; -use \Exception as Exception; -use Seam\HttpApiError; -use Seam\HttpUnauthorizedError; -use Seam\HttpInvalidInputError; - -define('LTS_VERSION', '1.0.0'); - -class SeamClient +use GuzzleHttp\ClientInterface; +use Seam\Http\SeamHttpClient; + +/** + * Client for the Seam API. + * + * Authenticate with an API key, which is scoped to a single workspace, or with + * a personal access token together with the id of the workspace to act on. + * When neither is given, the SEAM_API_KEY environment variable is used. + * + * @see https://docs.seam.co/ + */ +class Seam { {{#each parentClients}} public {{clientName}}Client ${{namespace}}; {{/each}} - public string $api_key; - public HTTPClient $client; - public string $ltsVersion = LTS_VERSION; - + /** + * The long term support version of the Seam API this SDK targets. + */ + public const LTS_VERSION = SeamHttpClient::LTS_VERSION; + + public SeamHttpClient $client; + + /** + * Default request options applied to every call, currently just + * wait_for_action_attempt. + * + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + public array $defaults; + + /** + * @param bool|array{timeout?: float, polling_interval?: float}|null $wait_for_action_attempt Whether to wait for action attempts to finish, optionally with timeout and polling_interval in seconds. Defaults to true. + * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. timeout or headers. + * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. + * @param ClientInterface|null $client A preconfigured Guzzle client. It carries its own endpoint and authorization, so no other authentication option may be given with it. + */ public function __construct( - $api_key = null, - $endpoint = "https://connect.getseam.com", - $throw_http_errors = false + ?string $api_key = null, + ?string $personal_access_token = null, + ?string $workspace_id = null, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ?ClientInterface $client = null ) { - $this->api_key = $api_key ?: (getenv('SEAM_API_KEY') ?: null); - $seam_sdk_version = PackageVersion::get(); - $this->client = new HTTPClient([ - "base_uri" => $endpoint, - "timeout" => 60.0, - "headers" => [ - "Authorization" => "Bearer " . $this->api_key, - "User-Agent" => "Seam PHP Client ". $seam_sdk_version, - "seam-sdk-name" => "seamapi/php", - "seam-sdk-version" => $seam_sdk_version, - "seam-lts-version" => $this->ltsVersion - ], - "http_errors" => $throw_http_errors, - ]); + $this->defaults = [ + "wait_for_action_attempt" => $wait_for_action_attempt ?? true, + ]; + + // A client carries its own endpoint and authorization, so the + // authentication options are only read when one has to be built. + $this->client = $client !== null + ? SeamHttpClient::from_client($client) + : new SeamHttpClient( + Options::get_endpoint($endpoint), + Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), + $guzzle_options, + $retries + ); + {{#each parentClients}} - $this->{{namespace}} = new {{clientName}}Client($this); + $this->{{namespace}} = new {{clientName}}Client($this->client, $this->defaults); {{/each}} } + /** + * Creates a client authorized with an API key. + */ + public static function from_api_key( + string $api_key, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null + ): static { + return new static( + api_key: $api_key, + endpoint: $endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + guzzle_options: $guzzle_options, + retries: $retries + ); + } + + /** + * Creates a client authorized with a personal access token, scoped to the + * given workspace. + */ + public static function from_personal_access_token( + string $personal_access_token, + string $workspace_id, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null + ): static { + return new static( + personal_access_token: $personal_access_token, + workspace_id: $workspace_id, + endpoint: $endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + retries: $retries, + guzzle_options: $guzzle_options + ); + } + + /** + * Creates a client from a preconfigured Guzzle client. + */ + public static function from_client( + ClientInterface $client, + bool|array|null $wait_for_action_attempt = null + ): static { + return new static( + client: $client, + wait_for_action_attempt: $wait_for_action_attempt + ); + } + + public function lts_version(): string + { + return self::LTS_VERSION; + } + + /** + * Makes a request against the Seam API with this client's authorization. + * + * @param array|object|null $json + * @param array|null $query + */ public function request( - $method, - $path, - $json = null, - $query = null, - ) { - $options = [ - "json" => $json, - "query" => $query, - ]; - $options = array_filter($options, fn($option) => $option !== null); - - $response = $this->client->request($method, $path, $options); - $status_code = $response->getStatusCode(); - $request_id = $response->getHeaderLine("seam-request-id"); - - $res_json = null; - try { - $res_json = json_decode($response->getBody()); - } catch (Exception $ignoreError) { - } - - if ($status_code >= 400) { - if ($status_code === 401) { - throw new HttpUnauthorizedError($request_id); - } - - if (($res_json->error ?? null) != null) { - if ($res_json->error->type === 'invalid_input') { - throw new HttpInvalidInputError($res_json->error, $status_code, $request_id); - } - - throw new HttpApiError($res_json->error, $status_code, $request_id); - } - - throw \GuzzleHttp\Exception\RequestException::create( - new \GuzzleHttp\Psr7\Request($method, $path), - $response - ); - } - - return $res_json; + string $method, + string $path, + mixed $json = null, + ?array $query = null + ): mixed { + return $this->client->request($method, $path, $json, $query); } - public function createPaginator($request, $params = []) + /** + * Creates a paginator for a list endpoint. + * + * @param callable $request Invokes the list method with a params array, e.g. fn($params) => $seam->devices->list(...$params) + * @param array $params + */ + public function createPaginator(callable $request, array $params = []): Paginator { - return new Paginator($request, $params); + return new Paginator($request, $params); } } diff --git a/codegen/lib/handlebars-helpers.ts b/codegen/lib/handlebars-helpers.ts index 263be9ce..62b6574a 100644 --- a/codegen/lib/handlebars-helpers.ts +++ b/codegen/lib/handlebars-helpers.ts @@ -9,7 +9,13 @@ export interface DeprecatedPhpDocContext { export interface MethodPhpDocContext extends DeprecatedPhpDocContext { returnType: string responseDescription: string - parameters: Array<{ name: string; type: string; description: string }> + // The endpoint parameters plus the SDK level ones, e.g. + // wait_for_action_attempt, so editors surface all of them. + documentedParameters: Array<{ + name: string + type: string + description: string + }> } export const resourcePhpDoc = (context: DeprecatedPhpDocContext): string => @@ -25,7 +31,7 @@ export const methodPhpDoc = (context: MethodPhpDocContext): string => createPhpDoc( context.description, [ - ...context.parameters.map( + ...context.documentedParameters.map( (parameter) => `@param ${parameter.type} $${parameter.name}${parameter.description === '' ? '' : ` ${parameter.description}`}`, ), diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index eb32944a..0cc1ac3e 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -8,12 +8,9 @@ import { sortPhpClientMethodParameters, } from '../class-model.js' -const seamClientClass = 'Seam\\SeamClient' +const seamHttpClientClass = 'Seam\\Http\\SeamHttpClient' +const resolveActionAttemptClass = 'Seam\\Http\\ResolveActionAttempt' const resourcesNamespace = 'Seam\\Resources' -const actionAttemptErrorClasses = [ - 'Seam\\ActionAttemptFailedError', - 'Seam\\ActionAttemptTimeoutError', -] export interface MethodLayoutContext { methodName: string @@ -21,12 +18,21 @@ export interface MethodLayoutContext { responseDescription: string isDeprecated: boolean deprecationMessage: string - parameters: Array<{ name: string; type: string; description: string }> + parameters: Array<{ + name: string + type: string + description: string + required: boolean + }> + documentedParameters: Array<{ + name: string + type: string + description: string + }> path: string returnType: string hasParams: boolean signatureParams: string - paramNames: string[] usesActionAttempt: boolean usesOnResponse: boolean returnsVoid: boolean @@ -40,21 +46,34 @@ export interface ClientLayoutContext { hasChildClients: boolean childClients: Array<{ clientName: string; namespace: string }> methods: MethodLayoutContext[] - isActionAttempts: boolean } export interface RouteLayoutContext extends ClientLayoutContext { useStatements: string[] } +const waitForActionAttemptParameter = { + name: 'wait_for_action_attempt', + type: 'bool|array|null', + description: + 'Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client.', + required: false, +} + +const onResponseParameter = { + name: 'on_response', + type: 'callable|null', + description: + 'Called with the raw response envelope, used by the paginator to read the pagination metadata.', + required: false, +} + const getMethodLayoutContext = ( method: PhpClientMethod, - clientName: string, ): MethodLayoutContext => { const { methodName, path, parameters, returnResource, returnPath } = method - const usesActionAttempt = - returnResource === 'ActionAttempt' && clientName !== 'ActionAttempts' + const usesActionAttempt = returnResource === 'ActionAttempt' const usesOnResponse = parameters.some((p) => p.name === 'page_cursor') && methodName === 'list' const returnsVoid = returnResource === '' @@ -71,26 +90,42 @@ const getMethodLayoutContext = ( (p) => `${!(p.required ?? false) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${(p.required ?? false) ? '' : ' = null'}`, ) - .concat(usesActionAttempt ? ['bool $wait_for_action_attempt = true'] : []) + .concat( + usesActionAttempt + ? ['bool|array|null $wait_for_action_attempt = null'] + : [], + ) .concat(usesOnResponse ? ['?callable $on_response = null'] : []) .join(', ') + const documentedEndpointParameters = sortedParameters.map( + ({ name, type, description, required }) => ({ + name, + type, + description, + required: required ?? false, + }), + ) + return { methodName, description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, deprecationMessage: method.deprecationMessage, - parameters: sortedParameters.map(({ name, type, description }) => ({ - name, - type, - description, - })), + // The request payload is built from the endpoint parameters alone. + parameters: documentedEndpointParameters, + // The SDK level parameters are documented alongside them so editors + // surface all of them, but they never reach the payload. + documentedParameters: [ + ...documentedEndpointParameters, + ...(usesActionAttempt ? [waitForActionAttemptParameter] : []), + ...(usesOnResponse ? [onResponseParameter] : []), + ], path, returnType, hasParams: parameters.length > 0, signatureParams, - paramNames: sortedParameters.map((p) => p.name), usesActionAttempt, usesOnResponse, returnsVoid, @@ -100,44 +135,34 @@ const getMethodLayoutContext = ( } } -// Child clients live in the same namespace as their parent, so only the -// SeamClient, the resource classes returned by the methods, and the action -// attempt errors thrown by poll_until_ready need importing. -const getUseStatements = ( - client: PhpClient, - isActionAttempts: boolean, -): string[] => { +// Child clients live in the same namespace as their parent, so only the HTTP +// client, the action attempt resolver, and the resource classes returned by +// the methods need importing. +const getUseStatements = (client: PhpClient): string[] => { const resourceNames = new Set( client.methods .map((m) => m.returnResource) .filter((resourceName) => resourceName !== ''), ) - if (isActionAttempts) resourceNames.add('ActionAttempt') + const usesActionAttempt = resourceNames.has('ActionAttempt') return [ - seamClientClass, + seamHttpClientClass, + ...(usesActionAttempt ? [resolveActionAttemptClass] : []), ...[...resourceNames].map((name) => `${resourcesNamespace}\\${name}`), - ...(isActionAttempts ? actionAttemptErrorClasses : []), ].sort((a, b) => a.localeCompare(b)) } export const setRouteLayoutContext = ( client: PhpClient, -): RouteLayoutContext => { - const isActionAttempts = client.clientName === 'ActionAttempts' - - return { - useStatements: getUseStatements(client, isActionAttempts), - clientName: client.clientName, - hasChildClients: client.childClientIdentifiers.length > 0, - childClients: client.childClientIdentifiers.map((i) => ({ - clientName: i.clientName, - namespace: i.namespace, - })), - methods: client.methods.map((m) => - getMethodLayoutContext(m, client.clientName), - ), - isActionAttempts, - } -} +): RouteLayoutContext => ({ + useStatements: getUseStatements(client), + clientName: client.clientName, + hasChildClients: client.childClientIdentifiers.length > 0, + childClients: client.childClientIdentifiers.map((i) => ({ + clientName: i.clientName, + namespace: i.namespace, + })), + methods: client.methods.map(getMethodLayoutContext), +}) diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index f18f155a..e49bb63b 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -21,7 +21,7 @@ interface Metadata { const resourcesPath = 'src/Resources' const routesPath = 'src/Routes' -const seamClientPath = 'src/SeamClient.php' +const seamClientPath = 'src/Seam.php' export const routes = ( files: Metalsmith.Files, diff --git a/codegen/smith.ts b/codegen/smith.ts index 744efd43..ba1384fa 100644 --- a/codegen/smith.ts +++ b/codegen/smith.ts @@ -12,7 +12,7 @@ import { helpers, routes } from './lib/index.js' const rootDir = dirname(fileURLToPath(import.meta.url)) await Promise.all([ - deleteAsync(['./src/Resources', './src/Routes', './src/SeamClient.php']), + deleteAsync(['./src/Resources', './src/Routes', './src/Seam.php']), ]) const partials = await getHandlebarsPartials(`${rootDir}/layouts/partials`) diff --git a/composer.json b/composer.json index 6827b5ce..486452eb 100644 --- a/composer.json +++ b/composer.json @@ -25,19 +25,18 @@ "source": "https://github.com/seamapi/php" }, "require": { - "php": "^8.0", - "guzzlehttp/guzzle": "^7.5" + "php": "^8.1", + "caseyamcl/guzzle_retry_middleware": "^2.13", + "guzzlehttp/guzzle": "^7.5", + "svix/svix": "^1.40" }, "require-dev": { - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.7" + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^6.0" }, "autoload": { "psr-4": { - "Seam\\": [ - "src/", - "src/Exceptions/" - ] + "Seam\\": "src/" }, "classmap": [ "src/Resources/" @@ -58,7 +57,7 @@ "/.env.example", "/.github", "/.npmrc", - "/.phpunit.result.cache", + "/.phpunit.cache", "/.prettierignore", "/.prettierrc.json", "/.releaserc.json", @@ -69,6 +68,7 @@ "/package.json", "/phpunit.xml.dist", "/pkg", + "/psalm.xml", "/tests", "/tmp", "/tsconfig.json", @@ -81,16 +81,19 @@ "test": "phpunit", "lint": [ "@lint:composer", - "@lint:syntax" + "@lint:syntax", + "@lint:types" ], "lint:composer": "@composer validate --strict", - "lint:syntax": "! find src tests -name '*.php' -exec php -l {} \\; | grep -v '^No syntax errors detected'" + "lint:syntax": "! find src tests -name '*.php' -exec php -l {} \\; | grep -v '^No syntax errors detected'", + "lint:types": "psalm --no-cache" }, "scripts-descriptions": { "build": "Build a distributable archive of this package into pkg/.", "test": "Run the test suite.", "lint": "Run all lint checks.", "lint:composer": "Validate composer.json and composer.lock.", - "lint:syntax": "Check every PHP source file for syntax errors." + "lint:syntax": "Check every PHP source file for syntax errors.", + "lint:types": "Run static analysis with Psalm." } } diff --git a/composer.lock b/composer.lock index bc6794ae..a604302c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,38 +4,114 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "47b28c106be0ff4f75680c1fe23e931a", + "content-hash": "25d67117228a0425a94bd9f40c53f692", "packages": [ + { + "name": "caseyamcl/guzzle_retry_middleware", + "version": "v2.13.0", + "source": { + "type": "git", + "url": "https://github.com/caseyamcl/guzzle_retry_middleware.git", + "reference": "17c9299cde438b00bbeb099c6480319a81636a60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/caseyamcl/guzzle_retry_middleware/zipball/17c9299cde438b00bbeb099c6480319a81636a60", + "reference": "17c9299cde438b00bbeb099c6480319a81636a60", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.3|^7.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "jaschilz/php-coverage-badger": "^2.0", + "nesbot/carbon": "^2.0|^3.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^7.5|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^5.0|^6.0|^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleRetry\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Casey McLaughlin", + "email": "caseyamcl@gmail.com", + "homepage": "https://caseymclaughlin.com", + "role": "Developer" + } + ], + "description": "Guzzle v6+ retry middleware that handles 429/503 status codes and connection timeouts", + "homepage": "https://github.com/caseyamcl/guzzle_retry_middleware", + "keywords": [ + "Guzzle", + "back-off", + "caseyamcl", + "guzzle_retry_middleware", + "middleware", + "retry", + "retry-after" + ], + "support": { + "issues": "https://github.com/caseyamcl/guzzle_retry_middleware/issues", + "source": "https://github.com/caseyamcl/guzzle_retry_middleware/tree/v2.13.0" + }, + "funding": [ + { + "url": "https://github.com/caseyamcl", + "type": "github" + } + ], + "time": "2025-07-11T12:33:22+00:00" + }, { "name": "guzzlehttp/guzzle", - "version": "7.5.0", + "version": "7.15.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" + "reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc", + "reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.9 || ^2.4", + "guzzlehttp/promises": "^2.5.2", + "guzzlehttp/psr7": "^2.13", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-client-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "guzzle/client-integration-tests": "3.0.3", + "guzzlehttp/test-server": "^0.7", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -48,9 +124,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "7.5-dev" } }, "autoload": { @@ -116,7 +189,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.0" + "source": "https://github.com/guzzle/guzzle/tree/7.15.3" }, "funding": [ { @@ -132,38 +205,38 @@ "type": "tidelift" } ], - "time": "2022-08-28T15:39:27+00:00" + "time": "2026-08-05T19:48:21+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.5.2", + "version": "2.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" + "reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "url": "https://api.github.com/repos/guzzle/promises/zipball/2823687acff28b2dbe67b2508a6b300e2c3fa4ce", + "reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.5-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } @@ -200,7 +273,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" + "source": "https://github.com/guzzle/promises/tree/2.5.2" }, "funding": [ { @@ -216,36 +289,39 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:55:35+00:00" + "time": "2026-08-05T19:30:54+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.4.1", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379" + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/69568e4293f4fa993f3b0e51c9723e1e17c41379", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "ralouphie/getallheaders": "^3.0" + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -255,9 +331,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "2.4-dev" } }, "autoload": { @@ -319,7 +392,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.1" + "source": "https://github.com/guzzle/psr7/tree/2.13.0" }, "funding": [ { @@ -335,25 +408,25 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:45:39+00:00" + "time": "2026-07-16T22:23:49+00:00" }, { "name": "psr/http-client", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -373,7 +446,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", @@ -385,27 +458,27 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/master" + "source": "https://github.com/php-fig/http-client" }, - "time": "2020-06-29T06:28:15+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -425,10 +498,10 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -440,31 +513,31 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2019-04-30T12:38:16+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "2.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -479,7 +552,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -493,9 +566,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "ralouphie/getallheaders", @@ -541,31 +614,84 @@ }, "time": "2019-03-08T08:55:37+00:00" }, + { + "name": "svix/svix", + "version": "v1.99.1", + "source": { + "type": "git", + "url": "https://github.com/svix/svix-webhooks.git", + "reference": "6cd12c1d6d19c6d222bab889d08315f6469702c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/svix/svix-webhooks/zipball/6cd12c1d6d19c6d222bab889d08315f6469702c8", + "reference": "6cd12c1d6d19c6d222bab889d08315f6469702c8", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^7.0", + "php": ">=8.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": ">=10.0 <=12.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svix\\": "php/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Svix", + "email": "support@svix.com", + "homepage": "http://www.svix.com" + } + ], + "description": "Svix PHP Library", + "homepage": "https://www.svix.com", + "keywords": [ + "api", + "webhooks" + ], + "support": { + "issues": "https://github.com/svix/svix-webhooks/issues", + "source": "https://github.com/svix/svix-webhooks/tree/v1.99.1" + }, + "time": "2026-07-23T16:05:36+00:00" + }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.2", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -590,7 +716,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -601,47 +727,51 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" - } - ], - "packages-dev": [ + "time": "2026-06-05T06:23:12+00:00" + }, { - "name": "doctrine/instantiator", - "version": "1.4.1", + "name": "symfony/polyfill-php80", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -649,1330 +779,1819 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "constructor", - "instantiate" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" - }, + "time": "2026-04-10T16:19:22+00:00" + } + ], + "packages-dev": [ { - "name": "myclabs/deep-copy", - "version": "1.11.0", + "name": "amphp/amp", + "version": "v3.1.3", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "url": "https://github.com/amphp/amp.git", + "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/amphp/amp/zipball/73c38b323ff8d790abf0f76c56fcc892bda4111a", + "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", "autoload": { "files": [ - "src/DeepCopy/deep_copy.php" + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Amp\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.1.3" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://github.com/amphp", + "type": "github" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2026-07-19T17:59:20+00:00" }, { - "name": "nikic/php-parser", - "version": "v4.15.1", + "name": "amphp/byte-stream", + "version": "v2.1.2", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900" + "url": "https://github.com/amphp/byte-stream.git", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=7.0" + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" }, - "bin": [ - "bin/php-parse" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], "psr-4": { - "PhpParser\\": "lib/PhpParser" + "Amp\\ByteStream\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "A PHP parser written in PHP", + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", "keywords": [ - "parser", - "php" + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" ], "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" }, - "time": "2022-09-04T07:30:47+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T17:10:27+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.3", + "name": "amphp/cache", + "version": "v2.0.1", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Amp\\Cache\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Niklas Keller", + "email": "me@kelunik.com" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" }, { - "name": "phar-io/version", - "version": "3.2.1", + "name": "amphp/dns", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "url": "https://github.com/amphp/dns.git", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/process": "^2", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "ext-json": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" }, "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Chris Wright", + "email": "addr@daverandom.com" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" } ], - "description": "Library for handling version information and constraints", + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.4.0" }, - "time": "2022-02-21T01:04:05+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-19T15:43:40+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "9.2.17", + "name": "amphp/parallel", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8" + "url": "https://github.com/amphp/parallel.git", + "reference": "37f5b2754fadc229c00f9416bd68fb8d04529a81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8", - "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8", + "url": "https://api.github.com/repos/amphp/parallel/zipball/37f5b2754fadc229c00f9416bd68fb8d04529a81", + "reference": "37f5b2754fadc229c00f9416bd68fb8d04529a81", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.14", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/process": "^2", + "amphp/serialization": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" }, "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/Context/functions.php", + "src/Context/Internal/functions.php", + "src/Ipc/functions.php", + "src/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", "keywords": [ - "coverage", - "testing", - "xunit" + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17" + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v2.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2022-08-30T12:24:04+00:00" + "time": "2026-05-16T16:54:01+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "name": "amphp/parser", + "version": "v1.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Amp\\Parser\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", "keywords": [ - "filesystem", - "iterator" + "async", + "non-blocking", + "parser", + "stream" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2024-03-21T19:16:53+00:00" }, { - "name": "phpunit/php-invoker", - "version": "3.1.1", + "name": "amphp/pipeline", + "version": "v1.2.7", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "url": "https://github.com/amphp/pipeline.git", + "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17", + "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17", "shasum": "" }, "require": { - "php": ">=7.3" + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" }, "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Amp\\Pipeline\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", "keywords": [ - "process" + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.7" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2026-07-26T14:50:43+00:00" }, { - "name": "phpunit/php-text-template", - "version": "2.0.4", + "name": "amphp/process", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "url": "https://github.com/amphp/process.git", + "reference": "583959df17d00304ad7b0b32285373f985935643" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/amphp/process/zipball/583959df17d00304ad7b0b32285373f985935643", + "reference": "583959df17d00304ad7b0b32285373f985935643", "shasum": "" }, "require": { - "php": ">=7.3" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.1.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2026-05-31T15:11:55+00:00" }, { - "name": "phpunit/php-timer", - "version": "5.0.3", + "name": "amphp/serialization", + "version": "v1.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "url": "https://github.com/amphp/serialization.git", + "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/amphp/serialization/zipball/fdf2834d78cebb0205fb2672676c1b1eb84371f0", + "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "ext-json": "*", + "ext-zlib": "*", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", "keywords": [ - "timer" + "async", + "asynchronous", + "serialization", + "serialize" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/v1.1.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2026-04-05T15:59:53+00:00" }, { - "name": "phpunit/phpunit", - "version": "9.5.24", + "name": "amphp/socket", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5" + "url": "https://github.com/amphp/socket.git", + "reference": "dadb63c5d3179fd83803e29dfeac27350e619314" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0aa6097bef9fd42458a9b3c49da32c6ce6129c5", - "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5", + "url": "https://api.github.com/repos/amphp/socket/zipball/dadb63c5d3179fd83803e29dfeac27350e619314", + "reference": "dadb63c5d3179fd83803e29dfeac27350e619314", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.1", - "sebastian/version": "^3.0.2" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^7", + "league/uri-interfaces": "^7", + "php": ">=8.1", + "revolt/event-loop": "^1" }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, - "bin": [ - "phpunit" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, "autoload": { "files": [ - "src/Framework/Assert/Functions.php" + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" ], - "classmap": [ - "src/" - ] + "psr-4": { + "Amp\\Socket\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", "keywords": [ - "phpunit", - "testing", - "xunit" + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.24" + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.4.0" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2022-08-30T07:42:16+00:00" + "time": "2026-04-19T15:09:56+00:00" }, { - "name": "sebastian/cli-parser", - "version": "1.0.1", + "name": "amphp/sync", + "version": "v2.3.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", "shasum": "" }, "require": { - "php": ">=7.3" + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-08-03T19:31:26+00:00" }, { - "name": "sebastian/code-unit", - "version": "1.0.8", + "name": "composer/pcre", + "version": "3.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "url": "https://github.com/composer/pcre.git", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<2.2.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" }, "type": "library", "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "3.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Composer\\Pcre\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2026-06-07T11:47:49+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "name": "composer/semver", + "version": "3.4.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Composer\\Semver\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { - "name": "sebastian/comparator", - "version": "4.0.8", + "name": "composer/xdebug-handler", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "Restarts a process without Xdebug.", "keywords": [ - "comparator", - "compare", - "equality" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { - "name": "sebastian/complexity", - "version": "2.0.2", + "name": "danog/advanced-json-rpc", + "version": "v3.2.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "url": "https://github.com/danog/php-advanced-json-rpc.git", + "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/ae703ea7b4811797a10590b6078de05b3b33dd91", + "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" + "netresearch/jsonmapper": "^5", + "php": ">=8.1", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0 || ^6" + }, + "replace": { + "felixfbecker/php-advanced-json-rpc": "^3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "ISC" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Felix Becker", + "email": "felix.b@outlook.com" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "description": "A more advanced JSONRPC implementation", "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "issues": "https://github.com/danog/php-advanced-json-rpc/issues", + "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.3" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2026-01-12T21:07:10+00:00" }, { - "name": "sebastian/diff", - "version": "4.0.4", + "name": "daverandom/libdns", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-ctype": "*", + "php": ">=7.1" }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "suggest": { + "ext-intl": "Required for IDN support" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } + "MIT" ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "DNS protocol implementation written in pure PHP", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "dns" ], "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "time": "2024-04-12T12:12:48+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], - "time": "2020-10-26T13:10:38+00:00" + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" }, { - "name": "sebastian/environment", - "version": "5.1.4", + "name": "doctrine/deprecations", + "version": "1.1.6", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" }, "suggest": { - "ext-posix": "*" + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.3", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-master": "1.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "LanguageServerProtocol\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "ISC" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Felix Becker", + "email": "felix.b@outlook.com" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "description": "PHP classes for the Language Server Protocol", "keywords": [ - "Xdebug", - "environment", - "hhvm" + "language", + "microsoft", + "php", + "server" ], "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2024-04-30T00:40:11+00:00" }, { - "name": "sebastian/exporter", - "version": "4.0.5", + "name": "fidry/cpu-core-counter", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", + "description": "Tiny utility to get the number of CPU cores.", "keywords": [ - "export", - "exporter" + "CPU", + "core" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/theofidry", "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { - "name": "sebastian/global-state", - "version": "5.0.5", + "name": "kelunik/certificate", + "version": "v1.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "ext-openssl": "*", + "php": ">=7.0" }, "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" }, - "suggest": { - "ext-uopz": "*" + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, + { + "name": "league/uri", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "7.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "League\\Uri\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", "keywords": [ - "global state" + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" ], "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.8.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/sponsors/nyamsprod", "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2026-03-15T20:22:25+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "1.0.3", + "name": "league/uri-interfaces", + "version": "7.8.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "7.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "League\\Uri\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/sponsors/nyamsprod", "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2026-03-08T20:05:35+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "4.0.4", + "name": "myclabs/deep-copy", + "version": "1.13.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v5.0.1", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "980674efdda65913492d29a8fd51c82270dd37bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/980674efdda65913492d29a8fd51c82270dd37bb", + "reference": "980674efdda65913492d29a8fd51c82270dd37bb", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.1" + }, + "time": "2026-02-22T16:28:03+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.8.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0" + }, + "time": "2026-07-04T14:30:18+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1985,277 +2604,2783 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/theseer", "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3" + }, + "time": "2026-03-18T20:49:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fb19eedd2bb67ff8cf7a5502ad329e701d6398a3", + "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.3" + }, + "time": "2026-07-08T07:01:06+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.64", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "0e8c1d19cea35ad97d4887f363d07c78e30fbf06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e8c1d19cea35ad97d4887f363d07c78e30fbf06", + "reference": "0e8c1d19cea35ad97d4887f363d07c78e30fbf06", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-filter": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.5", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.4", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.64" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-07-06T14:50:35+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "revolt/event-loop", + "version": "v1.0.9", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "44061cf513e53c6200372fc935ac42271566295d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/44061cf513e53c6200372fc935ac42271566295d", + "reference": "44061cf513e53c6200372fc935ac42271566295d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.9" + }, + "time": "2026-05-16T17:55:38+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:25:16+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "0735b90f4da94969541dac1da743446e276defa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:09:11+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-10T07:50:56+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/88b2f3852a922dd73177a68938f8eb2ec70c7224", + "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.4.4" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-12-15T09:00:41+00:00" + }, + { + "name": "symfony/console", + "version": "v8.1.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/535e18a1b8925f6c01a55b171d157ab66c2ace15", + "reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php85": "^1.32", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.4.6|^8.0.6" + }, + "conflict": { + "symfony/dependency-injection": "<8.1", + "symfony/event-dispatcher": "<8.1" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^8.1", + "symfony/event-dispatcher": "^8.1", + "symfony/filesystem": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v8.1.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-27T13:58:19+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v8.1.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "17856b7a222664a26a5ea1cb06ee0721c2438217" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/17856b7a222664a26a5ea1cb06ee0721c2438217", + "reference": "17856b7a222664a26a5ea1cb06ee0721c2438217", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v8.1.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-22T15:42:13+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.41.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-28T08:25:59+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.38.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-25T13:48:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.38.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-27T06:59:30+00:00" }, { - "name": "sebastian/object-reflector", - "version": "2.0.4", + "name": "symfony/polyfill-php84", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "2.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, "classmap": [ - "src/" + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { - "name": "sebastian/recursion-context", - "version": "4.0.4", + "name": "symfony/polyfill-php85", + "version": "v1.41.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, "classmap": [ - "src/" + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2026-07-01T12:47:55+00:00" }, { - "name": "sebastian/resource-operations", - "version": "3.0.3", + "name": "symfony/service-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, - "require-dev": { - "phpunit/phpunit": "^9.0" + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.7-dev" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2026-06-16T09:55:08+00:00" }, { - "name": "sebastian/type", - "version": "3.2.0", + "name": "symfony/string", + "version": "v8.1.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" + "url": "https://github.com/symfony/string.git", + "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "url": "https://api.github.com/repos/symfony/string/zipball/286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", + "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4.1", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" + "source": "https://github.com/symfony/string/tree/v8.1.2" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2022-09-12T14:47:03+00:00" + "time": "2026-07-28T07:35:25+00:00" }, { - "name": "sebastian/version", - "version": "3.0.2", + "name": "theseer/tokenizer", + "version": "1.3.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, "autoload": { "classmap": [ "src/" @@ -2267,130 +5392,207 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/theseer", "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { - "name": "squizlabs/php_codesniffer", - "version": "3.7.1", + "name": "vimeo/psalm", + "version": "6.16.1", "source": { "type": "git", - "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" + "url": "https://github.com/vimeo/psalm.git", + "reference": "f1f5de594dc76faf8784e02d3dc4716c91c6f6ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/f1f5de594dc76faf8784e02d3dc4716c91c6f6ac", + "reference": "f1f5de594dc76faf8784e02d3dc4716c91c6f6ac", "shasum": "" }, "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/parallel": "^2.3", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "danog/advanced-json-rpc": "^3.1", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", "ext-simplexml": "*", "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" + "felixfbecker/language-server-protocol": "^1.5.3", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^5.0", + "nikic/php-parser": "^5.0.0", + "php": "~8.1.31 || ~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^6.0 || ^7.0 || ^8.0", + "symfony/filesystem": "~6.3.12 || ~6.4.3 || ^7.0.3 || ^8.0", + "symfony/polyfill-php84": "^1.31.0" + }, + "provide": { + "psalm/psalm": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "amphp/phpunit-util": "^3", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "danog/class-finder": "^0.4.8", + "dg/bypass-finals": "^1.5", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.19", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^6.0 || ^7.0 || ^8.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalm-review", + "psalter" ], - "type": "library", + "type": "project", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev", + "dev-3.x": "3.x-dev", + "dev-4.x": "4.x-dev", + "dev-5.x": "5.x-dev", + "dev-6.x": "6.x-dev", + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Greg Sherwood", - "role": "lead" + "name": "Matthew Brown" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "description": "A static analysis tool for finding errors in PHP applications", "keywords": [ - "phpcs", - "standards" + "code", + "inspection", + "php", + "static analysis" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" }, - "time": "2022-06-18T07:21:10+00:00" + "time": "2026-03-19T10:56:09+00:00" }, { - "name": "theseer/tokenizer", - "version": "1.2.1", + "name": "webmozart/assert", + "version": "2.4.1", "source": { "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "url": "https://github.com/webmozarts/assert.git", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" + } + }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Webmozart\\Assert\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.4.1" }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2026-06-15T15:31:57+00:00" } ], "aliases": [], @@ -2399,7 +5601,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.0" + "php": "^8.1" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/package-lock.json b/package-lock.json index dc179e80..35c7c127 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "devDependencies": { "@prettier/plugin-php": "^0.25.0", "@seamapi/blueprint": "^1.2.0", + "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.985.0", "change-case": "^5.4.4", @@ -819,6 +820,42 @@ "npm": ">=10.0.0" } }, + "node_modules/@seamapi/fake-devicedb": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@seamapi/fake-devicedb/-/fake-devicedb-1.6.1.tgz", + "integrity": "sha512-w4Ar/s2kPnE5ExJSlpD3sKL8lkF+rLHRROArIRxtR2reHfnDSVwnDt9TzBYkHqgMP/x4o7LUzlRRpobn2xn24A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.12.0", + "npm": ">= 9.0.0" + }, + "optionalDependencies": { + "zod": "^3.21.4", + "zustand": "^4.3.7", + "zustand-hoist": "^2.0.0" + } + }, + "node_modules/@seamapi/fake-seam-connect": { + "version": "1.86.0", + "resolved": "https://registry.npmjs.org/@seamapi/fake-seam-connect/-/fake-seam-connect-1.86.0.tgz", + "integrity": "sha512-iO5fwtSIPhzmIiLxrFDtCYF/7HTb+ywcGmc3WzWt8Sr0bLQlmwCyTJ8YYZy++Hx0FsnNpa5AmlJuJGul9Y5gZA==", + "dev": true, + "license": "MIT", + "bin": { + "fake-seam-connect": "dist/server.js" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">= 9.0.0" + }, + "optionalDependencies": { + "@seamapi/fake-devicedb": ">=1.0.0-rc.0", + "zustand": "^4.3.7", + "zustand-hoist": "^2.0.0" + } + }, "node_modules/@seamapi/smith": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@seamapi/smith/-/smith-1.1.0.tgz", @@ -4881,6 +4918,18 @@ "license": "MIT", "peer": true }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5866,6 +5915,17 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "dev": true, + "license": "MIT", + "optional": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/ware": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", @@ -6065,6 +6125,51 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/zustand-hoist": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/zustand-hoist/-/zustand-hoist-2.0.1.tgz", + "integrity": "sha512-Lhvv3RlLQx1NSUtuhk8jegXe1Wyav9RAOnLd4CRs1SbB5qcFoarAGQTE43vIxXizrm1UQJl1q5uRbOZuXGXGpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.12.0", + "npm": ">= 9.0.0" + }, + "peerDependencies": { + "zustand": ">=4.0.0" + } } } } diff --git a/package.json b/package.json index da30a5a8..c1f2a9ed 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "lint": "eslint .", "postlint": "prettier --check --ignore-path .gitignore --ignore-path .prettierignore .", "format": "prettier --write --ignore-path .gitignore --ignore-path .prettierignore .", - "preformat": "eslint --fix ." + "preformat": "eslint --fix .", + "start": "fake-seam-connect --seed" }, "engines": { "node": ">=22.11.0", @@ -32,6 +33,7 @@ "devDependencies": { "@prettier/plugin-php": "^0.25.0", "@seamapi/blueprint": "^1.2.0", + "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.985.0", "change-case": "^5.4.4", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e953736f..f29c67a2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,9 +1,9 @@ tests - + src - + + + src/Resources + src/Routes + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000..f3cb128b --- /dev/null +++ b/psalm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/src/Auth.php b/src/Auth.php new file mode 100644 index 00000000..127ae84e --- /dev/null +++ b/src/Auth.php @@ -0,0 +1,149 @@ + + */ + public static function get_auth_headers( + ?string $api_key = null, + ?string $personal_access_token = null, + ?string $workspace_id = null, + ): array { + // The environment is only consulted when no credential was passed at + // all, so an explicit personal access token is not second guessed by + // a stray SEAM_API_KEY. + if ($api_key === null && $personal_access_token === null) { + $api_key = Options::get_env("SEAM_API_KEY"); + } + + if ( + Options::is_seam_options_with_api_key( + $api_key, + $personal_access_token, + ) + ) { + return self::get_auth_headers_for_api_key($api_key); + } + + if ( + Options::is_seam_options_with_personal_access_token( + $personal_access_token, + $api_key, + $workspace_id, + ) + ) { + return self::get_auth_headers_for_personal_access_token( + $personal_access_token, + $workspace_id, + ); + } + + throw new InvalidOptionsError( + "Must specify an api_key or personal_access_token. " . + "Attempted reading configuration from the environment, " . + "but the environment variable SEAM_API_KEY is not set.", + ); + } + + /** + * @return array + */ + public static function get_auth_headers_for_api_key(string $api_key): array + { + if (Token::is_client_session_token($api_key)) { + throw new InvalidTokenError( + "A Client Session Token cannot be used as an api_key", + ); + } + + if (Token::is_jwt($api_key)) { + throw new InvalidTokenError("A JWT cannot be used as an api_key"); + } + + if (Token::is_access_token($api_key)) { + throw new InvalidTokenError( + "An Access Token cannot be used as an api_key", + ); + } + + if (Token::is_publishable_key($api_key)) { + throw new InvalidTokenError( + "A Publishable Key cannot be used as an api_key", + ); + } + + if (!Token::is_seam_token($api_key)) { + throw new InvalidTokenError("Unknown or invalid api_key format"); + } + + return ["authorization" => "Bearer " . $api_key]; + } + + /** + * @return array + */ + public static function get_auth_headers_for_personal_access_token( + string $personal_access_token, + string $workspace_id, + ): array { + self::assert_personal_access_token($personal_access_token); + + return [ + "authorization" => "Bearer " . $personal_access_token, + "seam-workspace" => $workspace_id, + ]; + } + + /** + * Headers for a personal access token that is not scoped to a workspace. + * + * @return array + */ + public static function get_auth_headers_for_multi_workspace_personal_access_token( + string $personal_access_token, + ): array { + self::assert_personal_access_token($personal_access_token); + + return ["authorization" => "Bearer " . $personal_access_token]; + } + + private static function assert_personal_access_token(string $token): void + { + if (Token::is_client_session_token($token)) { + throw new InvalidTokenError( + "A Client Session Token cannot be used as a personal_access_token", + ); + } + + if (Token::is_jwt($token)) { + throw new InvalidTokenError( + "A JWT cannot be used as a personal_access_token", + ); + } + + if (Token::is_publishable_key($token)) { + throw new InvalidTokenError( + "A Publishable Key cannot be used as a personal_access_token", + ); + } + + if (!Token::is_access_token($token)) { + throw new InvalidTokenError( + "Unknown or invalid personal_access_token format", + ); + } + } +} diff --git a/src/Exceptions/ActionAttemptError.php b/src/Exceptions/ActionAttemptError.php index 26530d06..7368770b 100644 --- a/src/Exceptions/ActionAttemptError.php +++ b/src/Exceptions/ActionAttemptError.php @@ -1,17 +1,19 @@ name = get_class($this); $this->actionAttempt = $actionAttempt; } diff --git a/src/Exceptions/ActionAttemptFailedError.php b/src/Exceptions/ActionAttemptFailedError.php index 59e307fe..d2d08a37 100644 --- a/src/Exceptions/ActionAttemptFailedError.php +++ b/src/Exceptions/ActionAttemptFailedError.php @@ -1,20 +1,32 @@ error->message, $actionAttempt); - $this->name = get_class($this); - $this->errorCode = $actionAttempt->error->type; + parent::__construct( + $actionAttempt->error->message ?? "Action attempt failed", + $actionAttempt, + ); + $this->errorCode = $actionAttempt->error->type ?? "unknown_error"; } + /** + * The action attempt error type. + * + * Named `getErrorCode` rather than `getCode` because `Exception::getCode` + * is final and returns an int. This is the equivalent of the `code` + * property on the same error in the other Seam SDKs. + */ public function getErrorCode(): string { return $this->errorCode; diff --git a/src/Exceptions/ActionAttemptTimeoutError.php b/src/Exceptions/ActionAttemptTimeoutError.php index d0b6a343..87cfa2bc 100644 --- a/src/Exceptions/ActionAttemptTimeoutError.php +++ b/src/Exceptions/ActionAttemptTimeoutError.php @@ -1,9 +1,15 @@ name = get_class($this); } } diff --git a/src/Exceptions/HttpApiError.php b/src/Exceptions/HttpApiError.php index a79a8dd5..960b1c10 100644 --- a/src/Exceptions/HttpApiError.php +++ b/src/Exceptions/HttpApiError.php @@ -1,27 +1,32 @@ message ?? "Unknown error"; - parent::__construct($message); - $this->errorCode = $error->type ?? "unknown"; + parent::__construct($error->message ?? "Unknown error"); + $this->errorCode = $error->type ?? "unknown_error"; $this->statusCode = $statusCode; $this->requestId = $requestId; $this->data = $error->data ?? null; } + /** + * The Seam error type, e.g. `device_not_found`. + */ public function getErrorCode(): string { return $this->errorCode; @@ -32,7 +37,10 @@ public function getStatusCode(): int return $this->statusCode; } - public function getRequestId(): string + /** + * The `seam-request-id` response header, or null when absent. + */ + public function getRequestId(): ?string { return $this->requestId; } diff --git a/src/Exceptions/HttpInvalidInputError.php b/src/Exceptions/HttpInvalidInputError.php index 38cd8d74..fc20f172 100644 --- a/src/Exceptions/HttpInvalidInputError.php +++ b/src/Exceptions/HttpInvalidInputError.php @@ -1,7 +1,10 @@ errorCode = "invalid_input"; $this->validationErrors = $error->validation_errors ?? (object) []; } + /** + * The validation messages for a request parameter, or an empty array when + * that parameter has none. + * + * @return string[] + */ public function getValidationErrorMessages(string $paramName): array { return $this->validationErrors->{$paramName}->_errors ?? []; diff --git a/src/Exceptions/HttpUnauthorizedError.php b/src/Exceptions/HttpUnauthorizedError.php index 921ee2eb..2b43bffd 100644 --- a/src/Exceptions/HttpUnauthorizedError.php +++ b/src/Exceptions/HttpUnauthorizedError.php @@ -1,15 +1,21 @@ "unauthorized", - "message" => "Unauthorized", - ]; - parent::__construct($error, 401, $requestId); + parent::__construct( + (object) [ + "type" => "unauthorized", + "message" => "Unauthorized", + ], + 401, + $requestId, + ); } } diff --git a/src/Exceptions/InvalidOptionsError.php b/src/Exceptions/InvalidOptionsError.php new file mode 100644 index 00000000..65781860 --- /dev/null +++ b/src/Exceptions/InvalidOptionsError.php @@ -0,0 +1,16 @@ +status === "success") { + return $action_attempt; + } + + if ($action_attempt->status === "error") { + throw new ActionAttemptFailedError($action_attempt); + } + + if (self::now() + $polling_interval > $deadline) { + throw new ActionAttemptTimeoutError($action_attempt, $timeout); + } + + usleep((int) ($polling_interval * 1000000.0)); + + $action_attempt = self::get_action_attempt( + $client, + $action_attempt->action_attempt_id, + ); + } + } + + private static function get_action_attempt( + SeamHttpClient $client, + string $action_attempt_id, + ): ActionAttempt { + $res = $client->request( + "POST", + "/action_attempts/get", + json: (object) ["action_attempt_id" => $action_attempt_id], + ); + + $action_attempt = ActionAttempt::from_json( + $res->action_attempt ?? null, + ); + + if ($action_attempt === null) { + throw new \UnexpectedValueException( + "Seam returned no action attempt for {$action_attempt_id}", + ); + } + + return $action_attempt; + } + + private static function now(): float + { + return microtime(true); + } +} diff --git a/src/Http/SeamHttpClient.php b/src/Http/SeamHttpClient.php new file mode 100644 index 00000000..b9deb34c --- /dev/null +++ b/src/Http/SeamHttpClient.php @@ -0,0 +1,287 @@ + $auth_headers + * @param array $guzzle_options + */ + public function __construct( + string $endpoint, + array $auth_headers, + array $guzzle_options = [], + ?int $retries = null, + ?ClientInterface $client = null, + ) { + $this->client = + $client ?? + new GuzzleClient( + self::create_config( + $endpoint, + $auth_headers, + $guzzle_options, + $retries ?? self::DEFAULT_RETRIES, + ), + ); + } + + /** + * Wraps an already configured Guzzle client. The client carries its own + * endpoint and authorization, so none are resolved here. + */ + public static function from_client(ClientInterface $client): self + { + return new self("", [], [], null, $client); + } + + public function get_client(): ClientInterface + { + return $this->client; + } + + /** + * @param array $auth_headers + * @param array $guzzle_options + * @return array + */ + private static function create_config( + string $endpoint, + array $auth_headers, + array $guzzle_options, + int $retries, + ): array { + // The middleware has to be on the handler stack the client is built + // with: a stack pushed onto after construction does not apply. + $handler = $guzzle_options["handler"] ?? HandlerStack::create(); + + if ($handler instanceof HandlerStack) { + $handler->push( + GuzzleRetryMiddleware::factory([ + "retry_enabled" => $retries > 0, + "max_retry_attempts" => $retries, + // A connection failure never reached the server, so it is + // safe to retry whatever the method. Status codes are + // opted into per request, see request(). + "retry_on_timeout" => true, + "retry_on_status" => [], + ]), + "seam_retry", + ); + } + + $headers = array_merge( + $auth_headers, + $guzzle_options["headers"] ?? [], + self::sdk_headers(), + ); + + return array_merge( + [ + "base_uri" => $endpoint, + "timeout" => self::DEFAULT_TIMEOUT, + ], + $guzzle_options, + [ + "handler" => $handler, + "headers" => $headers, + // Error mapping happens in request(); letting Guzzle throw + // first would bypass it entirely. + "http_errors" => false, + ], + ); + } + + /** + * @return array + */ + private static function sdk_headers(): array + { + $version = PackageVersion::get(); + + return [ + "User-Agent" => "seam-php/" . $version, + "seam-sdk-name" => "seamapi/php", + "seam-sdk-version" => $version, + "seam-lts-version" => self::LTS_VERSION, + ]; + } + + /** + * @param array|object|null $json + * @param array|null $query + */ + public function request( + string $method, + string $path, + mixed $json = null, + ?array $query = null, + ): mixed { + $options = array_filter( + [ + "json" => $json, + "query" => $query, + ], + fn($option) => $option !== null, + ); + + if (in_array(strtoupper($method), self::IDEMPOTENT_METHODS, true)) { + $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; + } + + $response = $this->client->request($method, $path, $options); + $status_code = $response->getStatusCode(); + + if ($status_code < 200 || $status_code >= 300) { + $this->handle_error_response( + new Request($method, $path), + $response, + ); + } + + return self::decode_body($response); + } + + private function handle_error_response( + RequestInterface $request, + ResponseInterface $response, + ): void { + $status_code = $response->getStatusCode(); + $request_id = self::get_request_id($response); + + if ($status_code === 401) { + throw new HttpUnauthorizedError($request_id); + } + + if (!self::is_api_error_response($response)) { + // Not a Seam error response, so there is nothing to map onto a + // Seam exception. The other Seam SDKs surface the underlying + // transport error here too. + throw BadResponseException::create($request, $response); + } + + $error = self::decode_body($response)->error; + + if (($error->type ?? null) === "invalid_input") { + throw new HttpInvalidInputError($error, $status_code, $request_id); + } + + throw new HttpApiError($error, $status_code, $request_id); + } + + /** + * True when the response body is a Seam error envelope, i.e. JSON holding + * an `error` object with a string `type` and `message`. + */ + private static function is_api_error_response( + ResponseInterface $response, + ): bool { + if ( + !str_starts_with( + $response->getHeaderLine("content-type"), + "application/json", + ) + ) { + return false; + } + + $body = self::decode_body($response); + + if (!is_object($body)) { + return false; + } + + $error = $body->error ?? null; + + if (!is_object($error)) { + return false; + } + + return is_string($error->type ?? null) && + is_string($error->message ?? null); + } + + private static function decode_body(ResponseInterface $response): mixed + { + $body = $response->getBody(); + $body->rewind(); + $contents = $body->getContents(); + + if ($contents === "") { + return null; + } + + try { + return Utils::jsonDecode($contents); + } catch (\InvalidArgumentException) { + return null; + } + } + + private static function get_request_id(ResponseInterface $response): ?string + { + return $response->hasHeader("seam-request-id") + ? $response->getHeaderLine("seam-request-id") + : null; + } +} diff --git a/src/Options.php b/src/Options.php new file mode 100644 index 00000000..7f4b7151 --- /dev/null +++ b/src/Options.php @@ -0,0 +1,103 @@ +has_next_page ?? false), + next_page_cursor: $json->next_page_cursor ?? null, + next_page_url: $json->next_page_url ?? null, + ); + } +} diff --git a/src/Paginator.php b/src/Paginator.php index a913cf80..5b86e6db 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -2,11 +2,17 @@ namespace Seam; +/** + * Fetches and walks the pages of a list endpoint. + * + * Create one with `Seam::createPaginator`, passing a callable that invokes the + * list method with a params array. + */ class Paginator { private $request; - private $params; - private $pagination_cache = []; + private array $params; + private array $pagination_cache = []; private const FIRST_PAGE = "FIRST_PAGE"; public function __construct(callable $request, array $params = []) @@ -15,46 +21,55 @@ public function __construct(callable $request, array $params = []) $this->params = $params; } + /** + * @return array{0: array, 1: Pagination} + */ public function firstPage(): array { - $request = $this->request; - $params = $this->params; - - $params["on_response"] = fn($response) => $this->cachePagination( - $response, - self::FIRST_PAGE, - ); - - $data = $request($params); - - return [$data, $this->pagination_cache[self::FIRST_PAGE]]; + return $this->fetchPage(self::FIRST_PAGE); } - public function nextPage(string $next_page_cursor): array + /** + * @return array{0: array, 1: Pagination} + */ + public function nextPage(?string $next_page_cursor): array { - if ($next_page_cursor === null) { + if ($next_page_cursor === null || $next_page_cursor === "") { throw new \InvalidArgumentException( - "Cannot get the next page with a null next_page_cursor", + "Cannot get the next page without a next_page_cursor", ); } + return $this->fetchPage($next_page_cursor); + } + + /** + * @return array{0: array, 1: Pagination} + */ + private function fetchPage(string $cursor): array + { $request = $this->request; $params = $this->params; - $params["page_cursor"] = $next_page_cursor; + if ($cursor !== self::FIRST_PAGE) { + $params["page_cursor"] = $cursor; + } + $params["on_response"] = fn($response) => $this->cachePagination( $response, - $next_page_cursor, + $cursor, ); $data = $request($params); - return [$data, $this->pagination_cache[$next_page_cursor]]; + return [$data, $this->pagination_cache[$cursor] ?? new Pagination()]; } - private function cachePagination($response, $next_page_cursor) + private function cachePagination($response, string $cursor): void { - $this->pagination_cache[$next_page_cursor] = $response->pagination; + $this->pagination_cache[$cursor] = Pagination::from_json( + $response->pagination ?? null, + ); } public function flattenToArray(): array diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 1c53b718..a3465a8d 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -2,19 +2,28 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AccessCode; -use Seam\SeamClient; class AccessCodesClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AccessCodesSimulateClient $simulate; public AccessCodesUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new AccessCodesSimulateClient($seam); - $this->unmanaged = new AccessCodesUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new AccessCodesSimulateClient($client, $defaults); + $this->unmanaged = new AccessCodesUnmanagedClient($client, $defaults); } /** @@ -64,9 +73,7 @@ public function create( ): AccessCode { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -127,7 +134,7 @@ public function create( ] = $use_offline_access_code; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/create", json: (object) $request_payload, @@ -185,9 +192,7 @@ public function create_multiple( ): array { $request_payload = []; - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } + $request_payload["device_ids"] = $device_ids; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -234,7 +239,7 @@ public function create_multiple( ] = $use_backup_access_code_pool; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/create_multiple", json: (object) $request_payload, @@ -259,14 +264,12 @@ public function delete( ): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; if ($device_id !== null) { $request_payload["device_id"] = $device_id; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/delete", json: (object) $request_payload, @@ -283,11 +286,9 @@ public function generate_code(string $device_id): AccessCode { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/generate_code", json: (object) $request_payload, @@ -323,7 +324,7 @@ public function get( $request_payload["device_id"] = $device_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/get", json: (object) $request_payload, @@ -347,6 +348,7 @@ public function get( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned access codes to include all records that satisfy a partial match using `name`, `code` or `access_code_id`. * @param string $user_identifier_key Your user ID for the user by which to filter access codes. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -395,7 +397,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/list", json: (object) $request_payload, @@ -429,11 +431,9 @@ public function pull_backup_access_code(string $access_code_id): AccessCode { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/pull_backup_access_code", json: (object) $request_payload, @@ -461,9 +461,7 @@ public function report_device_constraints( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($max_code_length !== null) { $request_payload["max_code_length"] = $max_code_length; } @@ -476,7 +474,7 @@ public function report_device_constraints( ] = $supported_code_lengths; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/report_device_constraints", json: (object) $request_payload, @@ -536,9 +534,7 @@ public function update( ): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -605,7 +601,7 @@ public function update( ] = $use_offline_access_code; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/update", json: (object) $request_payload, @@ -639,9 +635,7 @@ public function update_multiple( ): void { $request_payload = []; - if ($common_code_key !== null) { - $request_payload["common_code_key"] = $common_code_key; - } + $request_payload["common_code_key"] = $common_code_key; if ($ends_at !== null) { $request_payload["ends_at"] = $ends_at; } @@ -652,7 +646,7 @@ public function update_multiple( $request_payload["starts_at"] = $starts_at; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/update_multiple", json: (object) $request_payload, diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index 7f1ded04..0750e822 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedAccessCode; -use Seam\SeamClient; class AccessCodesSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -29,17 +38,11 @@ public function create_unmanaged_access_code( ): UnmanagedAccessCode { $request_payload = []; - if ($code !== null) { - $request_payload["code"] = $code; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($name !== null) { - $request_payload["name"] = $name; - } - - $res = $this->seam->request( + $request_payload["code"] = $code; + $request_payload["device_id"] = $device_id; + $request_payload["name"] = $name; + + $res = $this->client->request( "POST", "/access_codes/simulate/create_unmanaged_access_code", json: (object) $request_payload, diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 93ec0001..e18b0ab3 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedAccessCode; -use Seam\SeamClient; class AccessCodesUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -35,9 +44,7 @@ public function convert_to_managed( ): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -52,7 +59,7 @@ public function convert_to_managed( ] = $is_external_modification_allowed; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/unmanaged/convert_to_managed", json: (object) $request_payload, @@ -69,11 +76,9 @@ public function delete(string $access_code_id): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; - $this->seam->request( + $this->client->request( "POST", "/access_codes/unmanaged/delete", json: (object) $request_payload, @@ -107,7 +112,7 @@ public function get( $request_payload["device_id"] = $device_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/unmanaged/get", json: (object) $request_payload, @@ -124,6 +129,7 @@ public function get( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned access codes to include all records that satisfy a partial match using `name`, `code` or `access_code_id`. * @param string $user_identifier_key Your user ID for the user by which to filter unmanaged access codes. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -136,9 +142,7 @@ public function list( ): array { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($limit !== null) { $request_payload["limit"] = $limit; } @@ -152,7 +156,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/unmanaged/list", json: (object) $request_payload, @@ -187,12 +191,8 @@ public function update( ): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } - if ($is_managed !== null) { - $request_payload["is_managed"] = $is_managed; - } + $request_payload["access_code_id"] = $access_code_id; + $request_payload["is_managed"] = $is_managed; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -207,7 +207,7 @@ public function update( ] = $is_external_modification_allowed; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/unmanaged/update", json: (object) $request_payload, diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 42c99bd3..188412e5 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -2,18 +2,27 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AccessGrant; use Seam\Resources\Batch; -use Seam\SeamClient; class AccessGrantsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AccessGrantsUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->unmanaged = new AccessGrantsUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->unmanaged = new AccessGrantsUnmanagedClient($client, $defaults); } /** @@ -55,11 +64,9 @@ public function create( ): AccessGrant { $request_payload = []; - if ($requested_access_methods !== null) { - $request_payload[ - "requested_access_methods" - ] = $requested_access_methods; - } + $request_payload[ + "requested_access_methods" + ] = $requested_access_methods; if ($user_identity_id !== null) { $request_payload["user_identity_id"] = $user_identity_id; } @@ -105,7 +112,7 @@ public function create( $request_payload["starts_at"] = $starts_at; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/create", json: (object) $request_payload, @@ -124,11 +131,9 @@ public function delete(string $access_grant_id): void { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } + $request_payload["access_grant_id"] = $access_grant_id; - $this->seam->request( + $this->client->request( "POST", "/access_grants/delete", json: (object) $request_payload, @@ -155,7 +160,7 @@ public function get( $request_payload["access_grant_key"] = $access_grant_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/get", json: (object) $request_payload, @@ -194,7 +199,7 @@ public function get_related( $request_payload["include"] = $include; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/get_related", json: (object) $request_payload, @@ -219,6 +224,7 @@ public function get_related( * @param string $reservation_key Filter Access Grants by reservation_key. * @param string $space_id ID of the space by which you want to filter the list of Access Grants. * @param string $user_identity_id ID of user identity by which you want to filter the list of Access Grants. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -279,7 +285,7 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/list", json: (object) $request_payload, @@ -308,16 +314,12 @@ public function request_access_methods( ): AccessGrant { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } - if ($requested_access_methods !== null) { - $request_payload[ - "requested_access_methods" - ] = $requested_access_methods; - } + $request_payload["access_grant_id"] = $access_grant_id; + $request_payload[ + "requested_access_methods" + ] = $requested_access_methods; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/request_access_methods", json: (object) $request_payload, @@ -361,7 +363,7 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->seam->request( + $this->client->request( "POST", "/access_grants/update", json: (object) $request_payload, diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 84c4c449..72955f56 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedAccessGrant; -use Seam\SeamClient; class AccessGrantsUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function get(string $access_grant_id): UnmanagedAccessGrant { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } + $request_payload["access_grant_id"] = $access_grant_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/unmanaged/get", json: (object) $request_payload, @@ -46,6 +53,7 @@ public function get(string $access_grant_id): UnmanagedAccessGrant * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $reservation_key Filter unmanaged Access Grants by reservation_key. * @param string $user_identity_id ID of user identity by which you want to filter the list of unmanaged Access Grants. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -78,7 +86,7 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/unmanaged/list", json: (object) $request_payload, @@ -113,17 +121,13 @@ public function update( ): void { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } - if ($is_managed !== null) { - $request_payload["is_managed"] = $is_managed; - } + $request_payload["access_grant_id"] = $access_grant_id; + $request_payload["is_managed"] = $is_managed; if ($access_grant_key !== null) { $request_payload["access_grant_key"] = $access_grant_key; } - $this->seam->request( + $this->client->request( "POST", "/access_grants/unmanaged/update", json: (object) $request_payload, diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 6e9d975d..09421e8e 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -2,19 +2,29 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\AccessMethod; use Seam\Resources\ActionAttempt; use Seam\Resources\Batch; -use Seam\SeamClient; class AccessMethodsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AccessMethodsUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->unmanaged = new AccessMethodsUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->unmanaged = new AccessMethodsUnmanagedClient($client, $defaults); } /** @@ -22,37 +32,31 @@ public function __construct(SeamClient $seam) * * @param string $access_method_id ID of the `access_method` to assign the credential to. * @param string $card_number Card number of the credential to assign. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function assign_card( string $access_method_id, string $card_number, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } - if ($card_number !== null) { - $request_payload["card_number"] = $card_number; - } + $request_payload["access_method_id"] = $access_method_id; + $request_payload["card_number"] = $card_number; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/assign_card", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -80,7 +84,7 @@ public function delete( $request_payload["reservation_key"] = $reservation_key; } - $this->seam->request( + $this->client->request( "POST", "/access_methods/delete", json: (object) $request_payload, @@ -92,37 +96,31 @@ public function delete( * * @param string $access_method_id ID of the `access_method` to encode onto a card. * @param string $acs_encoder_id ID of the `acs_encoder` to use to encode the `access_method`. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function encode( string $access_method_id, string $acs_encoder_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["access_method_id"] = $access_method_id; + $request_payload["acs_encoder_id"] = $acs_encoder_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/encode", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -135,11 +133,9 @@ public function get(string $access_method_id): AccessMethod { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } + $request_payload["access_method_id"] = $access_method_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/get", json: (object) $request_payload, @@ -163,9 +159,7 @@ public function get_related( ): Batch { $request_payload = []; - if ($access_method_ids !== null) { - $request_payload["access_method_ids"] = $access_method_ids; - } + $request_payload["access_method_ids"] = $access_method_ids; if ($exclude !== null) { $request_payload["exclude"] = $exclude; } @@ -173,7 +167,7 @@ public function get_related( $request_payload["include"] = $include; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/get_related", json: (object) $request_payload, @@ -193,6 +187,7 @@ public function get_related( * @param int $limit Maximum number of records to return per page. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $space_id ID of the space by which to filter the returned access methods. Must be combined with `access_grant_id`, `access_grant_key`, or `acs_entrance_id`. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -233,7 +228,7 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/list", json: (object) $request_payload, @@ -254,36 +249,30 @@ public function list( * * @param string $access_method_id ID of the cloud_key `access_method` to use for the unlock operation. * @param string $acs_entrance_id ID of the entrance to unlock. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function unlock_door( string $access_method_id, string $acs_entrance_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["access_method_id"] = $access_method_id; + $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/unlock_door", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 94f9eb15..4599a2b4 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedAccessMethod; -use Seam\SeamClient; class AccessMethodsUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function get(string $access_method_id): UnmanagedAccessMethod { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } + $request_payload["access_method_id"] = $access_method_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/unmanaged/get", json: (object) $request_payload, @@ -54,9 +61,7 @@ public function list( ): array { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } + $request_payload["access_grant_id"] = $access_grant_id; if ($acs_entrance_id !== null) { $request_payload["acs_entrance_id"] = $acs_entrance_id; } @@ -67,7 +72,7 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/unmanaged/list", json: (object) $request_payload, diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index f4878d23..8fea3af4 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -2,18 +2,27 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsAccessGroup; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; -use Seam\SeamClient; class AcsAccessGroupsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -31,9 +40,7 @@ public function add_user( ): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -41,7 +48,7 @@ public function add_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/access_groups/add_user", json: (object) $request_payload, @@ -58,11 +65,9 @@ public function delete(string $acs_access_group_id): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; - $this->seam->request( + $this->client->request( "POST", "/acs/access_groups/delete", json: (object) $request_payload, @@ -79,11 +84,9 @@ public function get(string $acs_access_group_id): AcsAccessGroup { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/access_groups/get", json: (object) $request_payload, @@ -122,7 +125,7 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/access_groups/list", json: (object) $request_payload, @@ -145,11 +148,9 @@ public function list_accessible_entrances( ): array { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/access_groups/list_accessible_entrances", json: (object) $request_payload, @@ -171,11 +172,9 @@ public function list_users(string $acs_access_group_id): array { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/access_groups/list_users", json: (object) $request_payload, @@ -199,9 +198,7 @@ public function remove_user( ): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -209,7 +206,7 @@ public function remove_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/access_groups/remove_user", json: (object) $request_payload, diff --git a/src/Routes/AcsClient.php b/src/Routes/AcsClient.php index e6454811..1d5f11e0 100644 --- a/src/Routes/AcsClient.php +++ b/src/Routes/AcsClient.php @@ -2,25 +2,34 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class AcsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AcsAccessGroupsClient $access_groups; public AcsCredentialsClient $credentials; public AcsEncodersClient $encoders; public AcsEntrancesClient $entrances; public AcsSystemsClient $systems; public AcsUsersClient $users; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->access_groups = new AcsAccessGroupsClient($seam); - $this->credentials = new AcsCredentialsClient($seam); - $this->encoders = new AcsEncodersClient($seam); - $this->entrances = new AcsEntrancesClient($seam); - $this->systems = new AcsSystemsClient($seam); - $this->users = new AcsUsersClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->access_groups = new AcsAccessGroupsClient($client, $defaults); + $this->credentials = new AcsCredentialsClient($client, $defaults); + $this->encoders = new AcsEncodersClient($client, $defaults); + $this->entrances = new AcsEntrancesClient($client, $defaults); + $this->systems = new AcsSystemsClient($client, $defaults); + $this->users = new AcsUsersClient($client, $defaults); } } diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index b4151455..43b41e38 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -2,17 +2,26 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; -use Seam\SeamClient; class AcsCredentialsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -30,9 +39,7 @@ public function assign( ): void { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -40,7 +47,7 @@ public function assign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/credentials/assign", json: (object) $request_payload, @@ -82,9 +89,7 @@ public function create( ): AcsCredential { $request_payload = []; - if ($access_method !== null) { - $request_payload["access_method"] = $access_method; - } + $request_payload["access_method"] = $access_method; if ($acs_system_id !== null) { $request_payload["acs_system_id"] = $acs_system_id; } @@ -130,7 +135,7 @@ public function create( $request_payload["visionline_metadata"] = $visionline_metadata; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/credentials/create", json: (object) $request_payload, @@ -149,11 +154,9 @@ public function delete(string $acs_credential_id): void { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; - $this->seam->request( + $this->client->request( "POST", "/acs/credentials/delete", json: (object) $request_payload, @@ -170,11 +173,9 @@ public function get(string $acs_credential_id): AcsCredential { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/credentials/get", json: (object) $request_payload, @@ -194,6 +195,7 @@ public function get(string $acs_credential_id): AcsCredential * @param float $limit Number of credentials to return. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned credentials to include all records that satisfy a partial match using `display_name`, `code`, `card_number`, `acs_user_id` or `acs_credential_id`. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -236,7 +238,7 @@ public function list( $request_payload["search"] = $search; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/credentials/list", json: (object) $request_payload, @@ -262,11 +264,9 @@ public function list_accessible_entrances(string $acs_credential_id): array { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/credentials/list_accessible_entrances", json: (object) $request_payload, @@ -293,9 +293,7 @@ public function unassign( ): void { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -303,7 +301,7 @@ public function unassign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/credentials/unassign", json: (object) $request_payload, @@ -325,9 +323,7 @@ public function update( ): void { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; if ($code !== null) { $request_payload["code"] = $code; } @@ -335,7 +331,7 @@ public function update( $request_payload["ends_at"] = $ends_at; } - $this->seam->request( + $this->client->request( "POST", "/acs/credentials/update", json: (object) $request_payload, diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index d277fdb9..dd12962a 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -2,18 +2,28 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsEncoder; use Seam\Resources\ActionAttempt; -use Seam\SeamClient; class AcsEncodersClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AcsEncodersSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new AcsEncodersSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new AcsEncodersSimulateClient($client, $defaults); } /** @@ -22,19 +32,18 @@ public function __construct(SeamClient $seam) * @param string $acs_encoder_id ID of the `acs_encoder` to use to encode the `acs_credential`. * @param string $access_method_id ID of the `access_method` to encode onto a card. * @param string $acs_credential_id ID of the `acs_credential` to encode onto a card. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function encode_credential( string $acs_encoder_id, ?string $access_method_id = null, ?string $acs_credential_id = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($access_method_id !== null) { $request_payload["access_method_id"] = $access_method_id; } @@ -42,21 +51,18 @@ public function encode_credential( $request_payload["acs_credential_id"] = $acs_credential_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/encode_credential", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -69,11 +75,9 @@ public function get(string $acs_encoder_id): AcsEncoder { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/get", json: (object) $request_payload, @@ -90,6 +94,7 @@ public function get(string $acs_encoder_id): AcsEncoder * @param array $acs_encoder_ids IDs of the encoders that you want to retrieve. * @param float $limit Number of encoders to return. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -118,7 +123,7 @@ public function list( $request_payload["page_cursor"] = $page_cursor; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/list", json: (object) $request_payload, @@ -139,37 +144,33 @@ public function list( * * @param string $acs_encoder_id ID of the encoder to use for the scan. * @param mixed $salto_ks_metadata Salto KS-specific metadata for the scan action. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function scan_credential( string $acs_encoder_id, mixed $salto_ks_metadata = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($salto_ks_metadata !== null) { $request_payload["salto_ks_metadata"] = $salto_ks_metadata; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/scan_credential", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -179,6 +180,7 @@ public function scan_credential( * @param string $acs_user_id ID of the `acs_user` to assign the scanned credential to. * @param mixed $salto_ks_metadata Salto KS-specific metadata for the scan action. * @param string $user_identity_id ID of the `user_identity` to assign the scanned credential to. If the ACS system contains an ACS user linked to this user identity, it is used. Otherwise, one is created. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function scan_to_assign_credential( @@ -186,13 +188,11 @@ public function scan_to_assign_credential( ?string $acs_user_id = null, mixed $salto_ks_metadata = null, ?string $user_identity_id = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -203,20 +203,17 @@ public function scan_to_assign_credential( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/scan_to_assign_credential", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/AcsEncodersSimulateClient.php b/src/Routes/AcsEncodersSimulateClient.php index 7fb9c2b4..54ce9aaf 100644 --- a/src/Routes/AcsEncodersSimulateClient.php +++ b/src/Routes/AcsEncodersSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class AcsEncodersSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -28,9 +37,7 @@ public function next_credential_encode_will_fail( ): void { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($error_code !== null) { $request_payload["error_code"] = $error_code; } @@ -38,7 +45,7 @@ public function next_credential_encode_will_fail( $request_payload["acs_credential_id"] = $acs_credential_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/encoders/simulate/next_credential_encode_will_fail", json: (object) $request_payload, @@ -58,14 +65,12 @@ public function next_credential_encode_will_succeed( ): void { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($scenario !== null) { $request_payload["scenario"] = $scenario; } - $this->seam->request( + $this->client->request( "POST", "/acs/encoders/simulate/next_credential_encode_will_succeed", json: (object) $request_payload, @@ -87,9 +92,7 @@ public function next_credential_scan_will_fail( ): void { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($error_code !== null) { $request_payload["error_code"] = $error_code; } @@ -99,7 +102,7 @@ public function next_credential_scan_will_fail( ] = $acs_credential_id_on_seam; } - $this->seam->request( + $this->client->request( "POST", "/acs/encoders/simulate/next_credential_scan_will_fail", json: (object) $request_payload, @@ -121,9 +124,7 @@ public function next_credential_scan_will_succeed( ): void { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($acs_credential_id_on_seam !== null) { $request_payload[ "acs_credential_id_on_seam" @@ -133,7 +134,7 @@ public function next_credential_scan_will_succeed( $request_payload["scenario"] = $scenario; } - $this->seam->request( + $this->client->request( "POST", "/acs/encoders/simulate/next_credential_scan_will_succeed", json: (object) $request_payload, diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 583d6e5e..270cfb24 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -2,18 +2,28 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; use Seam\Resources\ActionAttempt; -use Seam\SeamClient; class AcsEntrancesClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -26,11 +36,9 @@ public function get(string $acs_entrance_id): AcsEntrance { $request_payload = []; - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/entrances/get", json: (object) $request_payload, @@ -54,9 +62,7 @@ public function grant_access( ): void { $request_payload = []; - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["acs_entrance_id"] = $acs_entrance_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -64,7 +70,7 @@ public function grant_access( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/entrances/grant_access", json: (object) $request_payload, @@ -85,6 +91,7 @@ public function grant_access( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned entrances to include all records that satisfy a partial match using `display_name`. * @param string $space_id ID of the space for which you want to list entrances. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -137,7 +144,7 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/entrances/list", json: (object) $request_payload, @@ -166,14 +173,12 @@ public function list_credentials_with_access( ): array { $request_payload = []; - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["acs_entrance_id"] = $acs_entrance_id; if ($include_if !== null) { $request_payload["include_if"] = $include_if; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/entrances/list_credentials_with_access", json: (object) $request_payload, @@ -190,36 +195,30 @@ public function list_credentials_with_access( * * @param string $acs_credential_id ID of the cloud_key credential to use for the unlock operation. * @param string $acs_entrance_id ID of the entrance to unlock. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function unlock( string $acs_credential_id, string $acs_entrance_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; + $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/entrances/unlock", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index 9278fd9f..cc519bf5 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsSystem; -use Seam\SeamClient; class AcsSystemsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function get(string $acs_system_id): AcsSystem { $request_payload = []; - if ($acs_system_id !== null) { - $request_payload["acs_system_id"] = $acs_system_id; - } + $request_payload["acs_system_id"] = $acs_system_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/systems/get", json: (object) $request_payload, @@ -64,7 +71,7 @@ public function list( $request_payload["search"] = $search; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/systems/list", json: (object) $request_payload, @@ -86,11 +93,9 @@ public function list_compatible_credential_manager_acs_systems( ): array { $request_payload = []; - if ($acs_system_id !== null) { - $request_payload["acs_system_id"] = $acs_system_id; - } + $request_payload["acs_system_id"] = $acs_system_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/systems/list_compatible_credential_manager_acs_systems", json: (object) $request_payload, @@ -114,9 +119,7 @@ public function report_devices( ): void { $request_payload = []; - if ($acs_system_id !== null) { - $request_payload["acs_system_id"] = $acs_system_id; - } + $request_payload["acs_system_id"] = $acs_system_id; if ($acs_encoders !== null) { $request_payload["acs_encoders"] = $acs_encoders; } @@ -124,7 +127,7 @@ public function report_devices( $request_payload["acs_entrances"] = $acs_entrances; } - $this->seam->request( + $this->client->request( "POST", "/acs/systems/report_devices", json: (object) $request_payload, diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 729709e1..422a0502 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -2,17 +2,26 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; -use Seam\SeamClient; class AcsUsersClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -28,14 +37,10 @@ public function add_to_access_group( ): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } - if ($acs_user_id !== null) { - $request_payload["acs_user_id"] = $acs_user_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; + $request_payload["acs_user_id"] = $acs_user_id; - $this->seam->request( + $this->client->request( "POST", "/acs/users/add_to_access_group", json: (object) $request_payload, @@ -67,12 +72,8 @@ public function create( ): AcsUser { $request_payload = []; - if ($acs_system_id !== null) { - $request_payload["acs_system_id"] = $acs_system_id; - } - if ($full_name !== null) { - $request_payload["full_name"] = $full_name; - } + $request_payload["acs_system_id"] = $acs_system_id; + $request_payload["full_name"] = $full_name; if ($access_schedule !== null) { $request_payload["access_schedule"] = $access_schedule; } @@ -92,7 +93,7 @@ public function create( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/users/create", json: (object) $request_payload, @@ -126,7 +127,7 @@ public function delete( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/delete", json: (object) $request_payload, @@ -158,7 +159,7 @@ public function get( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/users/get", json: (object) $request_payload, @@ -178,6 +179,7 @@ public function get( * @param string $user_identity_email_address Email address of the user identity for which you want to retrieve all access system users. * @param string $user_identity_id ID of the user identity for which you want to retrieve all access system users. * @param string $user_identity_phone_number Phone number of the user identity for which you want to retrieve all access system users, in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, `+15555550100`). + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -222,7 +224,7 @@ public function list( ] = $user_identity_phone_number; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/users/list", json: (object) $request_payload, @@ -260,7 +262,7 @@ public function list_accessible_entrances( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/users/list_accessible_entrances", json: (object) $request_payload, @@ -287,9 +289,7 @@ public function remove_from_access_group( ): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -297,7 +297,7 @@ public function remove_from_access_group( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/remove_from_access_group", json: (object) $request_payload, @@ -329,7 +329,7 @@ public function revoke_access_to_all_entrances( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/revoke_access_to_all_entrances", json: (object) $request_payload, @@ -361,7 +361,7 @@ public function suspend( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/suspend", json: (object) $request_payload, @@ -393,7 +393,7 @@ public function unsuspend( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/unsuspend", json: (object) $request_payload, @@ -455,7 +455,7 @@ public function update( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/update", json: (object) $request_payload, diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index becd80db..971a286e 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -2,41 +2,55 @@ namespace Seam\Routes; -use Seam\ActionAttemptFailedError; -use Seam\ActionAttemptTimeoutError; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; -use Seam\SeamClient; class ActionAttemptsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** * Returns a specified [action attempt](https://docs.seam.co/core-concepts/action-attempts). * * @param string $action_attempt_id ID of the action attempt that you want to get. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ - public function get(string $action_attempt_id): ActionAttempt - { + public function get( + string $action_attempt_id, + bool|array|null $wait_for_action_attempt = null, + ): ActionAttempt { $request_payload = []; - if ($action_attempt_id !== null) { - $request_payload["action_attempt_id"] = $action_attempt_id; - } + $request_payload["action_attempt_id"] = $action_attempt_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/action_attempts/get", json: (object) $request_payload, ); - return ActionAttempt::from_json($res->action_attempt); + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], + ); } /** @@ -46,6 +60,8 @@ public function get(string $action_attempt_id): ActionAttempt * @param string $device_id ID of the device to filter action attempts by. * @param int $limit Maximum number of records to return per page. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -53,6 +69,7 @@ public function list( ?string $device_id = null, ?int $limit = null, ?string $page_cursor = null, + bool|array|null $wait_for_action_attempt = null, ?callable $on_response = null, ): array { $request_payload = []; @@ -70,45 +87,17 @@ public function list( $request_payload["page_cursor"] = $page_cursor; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/action_attempts/list", json: (object) $request_payload, ); - if ($on_response !== null) { - $on_response($res); - } - - return array_map( - fn($r) => ActionAttempt::from_json($r), - $res->action_attempts, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempts), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); } - public function poll_until_ready( - string $action_attempt_id, - float $timeout = 20.0, - ): ActionAttempt { - $seam = $this->seam; - $time_waiting = 0.0; - $polling_interval = 0.4; - $action_attempt = $seam->action_attempts->get($action_attempt_id); - - while ($action_attempt->status == "pending") { - $action_attempt = $seam->action_attempts->get( - $action_attempt->action_attempt_id, - ); - if ($time_waiting > $timeout) { - throw new ActionAttemptTimeoutError($action_attempt, $timeout); - } - $time_waiting += $polling_interval; - usleep($polling_interval * 1000000); - } - - if ($action_attempt->status == "error") { - throw new ActionAttemptFailedError($action_attempt); - } - - return $action_attempt; - } } diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index ebb651de..93b81d8c 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\ClientSession; -use Seam\SeamClient; class ClientSessionsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -64,7 +73,7 @@ public function create( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/client_sessions/create", json: (object) $request_payload, @@ -83,11 +92,9 @@ public function delete(string $client_session_id): void { $request_payload = []; - if ($client_session_id !== null) { - $request_payload["client_session_id"] = $client_session_id; - } + $request_payload["client_session_id"] = $client_session_id; - $this->seam->request( + $this->client->request( "POST", "/client_sessions/delete", json: (object) $request_payload, @@ -114,7 +121,7 @@ public function get( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/client_sessions/get", json: (object) $request_payload, @@ -163,7 +170,7 @@ public function get_or_create( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/client_sessions/get_or_create", json: (object) $request_payload, @@ -212,7 +219,7 @@ public function grant_access( $request_payload["user_identity_ids"] = $user_identity_ids; } - $this->seam->request( + $this->client->request( "POST", "/client_sessions/grant_access", json: (object) $request_payload, @@ -256,7 +263,7 @@ public function list( ] = $without_user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/client_sessions/list", json: (object) $request_payload, @@ -280,11 +287,9 @@ public function revoke(string $client_session_id): void { $request_payload = []; - if ($client_session_id !== null) { - $request_payload["client_session_id"] = $client_session_id; - } + $request_payload["client_session_id"] = $client_session_id; - $this->seam->request( + $this->client->request( "POST", "/client_sessions/revoke", json: (object) $request_payload, diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 4bd32a3e..760d8edb 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\ConnectWebview; -use Seam\SeamClient; class ConnectWebviewsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -86,7 +95,7 @@ public function create( ] = $wait_for_device_creation; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connect_webviews/create", json: (object) $request_payload, @@ -107,11 +116,9 @@ public function delete(string $connect_webview_id): void { $request_payload = []; - if ($connect_webview_id !== null) { - $request_payload["connect_webview_id"] = $connect_webview_id; - } + $request_payload["connect_webview_id"] = $connect_webview_id; - $this->seam->request( + $this->client->request( "POST", "/connect_webviews/delete", json: (object) $request_payload, @@ -130,11 +137,9 @@ public function get(string $connect_webview_id): ConnectWebview { $request_payload = []; - if ($connect_webview_id !== null) { - $request_payload["connect_webview_id"] = $connect_webview_id; - } + $request_payload["connect_webview_id"] = $connect_webview_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connect_webviews/get", json: (object) $request_payload, @@ -152,6 +157,7 @@ public function get(string $connect_webview_id): ConnectWebview * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned Connect Webviews to include all records that satisfy a partial match using `connect_webview_id`, `accepted_providers`, `custom_metadata`, or `customer_key`. * @param string $user_identifier_key Your user ID for the user by which you want to filter Connect Webviews. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -184,7 +190,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connect_webviews/list", json: (object) $request_payload, diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index d3873aa0..9725597a 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -2,17 +2,29 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\ConnectedAccount; -use Seam\SeamClient; class ConnectedAccountsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public ConnectedAccountsSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new ConnectedAccountsSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new ConnectedAccountsSimulateClient( + $client, + $defaults, + ); } /** @@ -29,11 +41,9 @@ public function delete(string $connected_account_id): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } + $request_payload["connected_account_id"] = $connected_account_id; - $this->seam->request( + $this->client->request( "POST", "/connected_accounts/delete", json: (object) $request_payload, @@ -60,7 +70,7 @@ public function get( $request_payload["email"] = $email; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connected_accounts/get", json: (object) $request_payload, @@ -79,6 +89,7 @@ public function get( * @param string $search String for which to search. Filters returned connected accounts to include all records that satisfy a partial match using `connected_account_id`, `account_type`, `customer_key`, `custom_metadata`, `user_identifier.username`, `user_identifier.email` or `user_identifier.phone`. * @param string $space_id ID of the space by which you want to filter connected accounts. * @param string $user_identifier_key Your user ID for the user by which you want to filter connected accounts. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -115,7 +126,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connected_accounts/list", json: (object) $request_payload, @@ -141,11 +152,9 @@ public function sync(string $connected_account_id): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } + $request_payload["connected_account_id"] = $connected_account_id; - $this->seam->request( + $this->client->request( "POST", "/connected_accounts/sync", json: (object) $request_payload, @@ -173,9 +182,7 @@ public function update( ): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } + $request_payload["connected_account_id"] = $connected_account_id; if ($accepted_capabilities !== null) { $request_payload["accepted_capabilities"] = $accepted_capabilities; } @@ -194,7 +201,7 @@ public function update( $request_payload["display_name"] = $display_name; } - $this->seam->request( + $this->client->request( "POST", "/connected_accounts/update", json: (object) $request_payload, diff --git a/src/Routes/ConnectedAccountsSimulateClient.php b/src/Routes/ConnectedAccountsSimulateClient.php index d3a913df..4eef7cb8 100644 --- a/src/Routes/ConnectedAccountsSimulateClient.php +++ b/src/Routes/ConnectedAccountsSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class ConnectedAccountsSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -23,11 +32,9 @@ public function disconnect(string $connected_account_id): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } + $request_payload["connected_account_id"] = $connected_account_id; - $this->seam->request( + $this->client->request( "POST", "/connected_accounts/simulate/disconnect", json: (object) $request_payload, diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index f8c97a1f..3fe55788 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\CustomerPortal; -use Seam\SeamClient; class CustomersClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -83,7 +92,7 @@ public function create_portal( $request_payload["customer_data"] = $customer_data; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/customers/create_portal", json: (object) $request_payload, @@ -198,7 +207,7 @@ public function delete_data( $request_payload["user_keys"] = $user_keys; } - $this->seam->request( + $this->client->request( "POST", "/customers/delete_data", json: (object) $request_payload, @@ -254,9 +263,7 @@ public function push_data( ): void { $request_payload = []; - if ($customer_key !== null) { - $request_payload["customer_key"] = $customer_key; - } + $request_payload["customer_key"] = $customer_key; if ($access_grants !== null) { $request_payload["access_grants"] = $access_grants; } @@ -315,7 +322,7 @@ public function push_data( $request_payload["users"] = $users; } - $this->seam->request( + $this->client->request( "POST", "/customers/push_data", json: (object) $request_payload, diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 9eb2f20c..61ed5303 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -2,20 +2,29 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Device; use Seam\Resources\DeviceProvider; -use Seam\SeamClient; class DevicesClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public DevicesSimulateClient $simulate; public DevicesUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new DevicesSimulateClient($seam); - $this->unmanaged = new DevicesUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new DevicesSimulateClient($client, $defaults); + $this->unmanaged = new DevicesUnmanagedClient($client, $defaults); } /** @@ -38,7 +47,7 @@ public function get(?string $device_id = null, ?string $name = null): Device $request_payload["name"] = $name; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/get", json: (object) $request_payload, @@ -66,6 +75,7 @@ public function get(?string $device_id = null, ?string $name = null): Device * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -138,7 +148,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/list", json: (object) $request_payload, @@ -170,7 +180,7 @@ public function list_device_providers( $request_payload["provider_category"] = $provider_category; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/list_device_providers", json: (object) $request_payload, @@ -192,11 +202,9 @@ public function report_provider_metadata(array $devices): void { $request_payload = []; - if ($devices !== null) { - $request_payload["devices"] = $devices; - } + $request_payload["devices"] = $devices; - $this->seam->request( + $this->client->request( "POST", "/devices/report_provider_metadata", json: (object) $request_payload, @@ -226,9 +234,7 @@ public function update( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($backup_access_code_pool_enabled !== null) { $request_payload[ "backup_access_code_pool_enabled" @@ -247,7 +253,7 @@ public function update( $request_payload["properties"] = $properties; } - $this->seam->request( + $this->client->request( "POST", "/devices/update", json: (object) $request_payload, diff --git a/src/Routes/DevicesSimulateClient.php b/src/Routes/DevicesSimulateClient.php index 9fb351b3..2a14d57d 100644 --- a/src/Routes/DevicesSimulateClient.php +++ b/src/Routes/DevicesSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class DevicesSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -23,11 +32,9 @@ public function connect(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/connect", json: (object) $request_payload, @@ -47,11 +54,9 @@ public function connect_to_hub(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/connect_to_hub", json: (object) $request_payload, @@ -68,11 +73,9 @@ public function disconnect(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/disconnect", json: (object) $request_payload, @@ -93,11 +96,9 @@ public function disconnect_from_hub(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/disconnect_from_hub", json: (object) $request_payload, @@ -117,14 +118,10 @@ public function paid_subscription(string $device_id, bool $is_expired): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($is_expired !== null) { - $request_payload["is_expired"] = $is_expired; - } + $request_payload["device_id"] = $device_id; + $request_payload["is_expired"] = $is_expired; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/paid_subscription", json: (object) $request_payload, @@ -141,11 +138,9 @@ public function remove(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/remove", json: (object) $request_payload, diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 171090df..541be58e 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedDevice; -use Seam\SeamClient; class DevicesUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -38,7 +47,7 @@ public function get( $request_payload["name"] = $name; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/unmanaged/get", json: (object) $request_payload, @@ -68,6 +77,7 @@ public function get( * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -140,7 +150,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/unmanaged/list", json: (object) $request_payload, @@ -173,9 +183,7 @@ public function update( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($custom_metadata !== null) { $request_payload["custom_metadata"] = $custom_metadata; } @@ -183,7 +191,7 @@ public function update( $request_payload["is_managed"] = $is_managed; } - $this->seam->request( + $this->client->request( "POST", "/devices/unmanaged/update", json: (object) $request_payload, diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index a1c37ba1..f2f1f65c 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Event; -use Seam\SeamClient; class EventsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -39,7 +48,7 @@ public function get( $request_payload["event_type"] = $event_type; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/events/get", json: (object) $request_payload, @@ -198,7 +207,7 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/events/list", json: (object) $request_payload, diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index 34815729..e91f4f30 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\InstantKey; -use Seam\SeamClient; class InstantKeysClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function delete(string $instant_key_id): void { $request_payload = []; - if ($instant_key_id !== null) { - $request_payload["instant_key_id"] = $instant_key_id; - } + $request_payload["instant_key_id"] = $instant_key_id; - $this->seam->request( + $this->client->request( "POST", "/instant_keys/delete", json: (object) $request_payload, @@ -55,7 +62,7 @@ public function get( $request_payload["instant_key_url"] = $instant_key_url; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/instant_keys/get", json: (object) $request_payload, @@ -78,7 +85,7 @@ public function list(?string $user_identity_id = null): array $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/instant_keys/list", json: (object) $request_payload, diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 00878fd9..d54afadf 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -2,18 +2,28 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; -use Seam\SeamClient; class LocksClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public LocksSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new LocksSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new LocksSimulateClient($client, $defaults); } /** @@ -22,43 +32,37 @@ public function __construct(SeamClient $seam) * @param bool $auto_lock_enabled Whether to enable or disable auto-lock. * @param string $device_id ID of the lock for which you want to configure the auto-lock. * @param float $auto_lock_delay_seconds Delay in seconds before the lock automatically locks. Required when enabling auto-lock. Must be between 1 and 60. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function configure_auto_lock( bool $auto_lock_enabled, string $device_id, ?float $auto_lock_delay_seconds = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($auto_lock_enabled !== null) { - $request_payload["auto_lock_enabled"] = $auto_lock_enabled; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["auto_lock_enabled"] = $auto_lock_enabled; + $request_payload["device_id"] = $device_id; if ($auto_lock_delay_seconds !== null) { $request_payload[ "auto_lock_delay_seconds" ] = $auto_lock_delay_seconds; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/configure_auto_lock", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -80,7 +84,7 @@ public function get(?string $device_id = null, ?string $name = null): Device $request_payload["name"] = $name; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/get", json: (object) $request_payload, @@ -108,6 +112,7 @@ public function get(?string $device_id = null, ?string $name = null): Device * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -180,7 +185,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/list", json: (object) $request_payload, @@ -197,65 +202,57 @@ public function list( * Locks a [lock](https://docs.seam.co/low-level-apis/smart-locks). See also [Locking and Unlocking Smart Locks](https://docs.seam.co/low-level-apis/smart-locks/lock-and-unlock). * * @param string $device_id ID of the lock that you want to lock. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function lock_door( string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/lock_door", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** * Unlocks a [lock](https://docs.seam.co/low-level-apis/smart-locks). See also [Locking and Unlocking Smart Locks](https://docs.seam.co/low-level-apis/smart-locks/lock-and-unlock). * * @param string $device_id ID of the lock that you want to unlock. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function unlock_door( string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/unlock_door", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index 500448d8..ca91968c 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -2,16 +2,26 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; -use Seam\SeamClient; class LocksSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -19,69 +29,59 @@ public function __construct(SeamClient $seam) * * @param string $code Code that you want to simulate entering on a keypad. * @param string $device_id ID of the device for which you want to simulate a keypad code entry. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function keypad_code_entry( string $code, string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($code !== null) { - $request_payload["code"] = $code; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["code"] = $code; + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/simulate/keypad_code_entry", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** * Simulates a manual lock action using a keypad. You can only perform this action for [August](https://docs.seam.co/device-and-system-integration-guides/august-locks) devices within [sandbox workspaces](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces). * * @param string $device_id ID of the device for which you want to simulate a manual lock action using a keypad. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function manual_lock_via_keypad( string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/simulate/manual_lock_via_keypad", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index d34a85b9..3f199835 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -2,19 +2,31 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Device; -use Seam\SeamClient; class NoiseSensorsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public NoiseSensorsNoiseThresholdsClient $noise_thresholds; public NoiseSensorsSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->noise_thresholds = new NoiseSensorsNoiseThresholdsClient($seam); - $this->simulate = new NoiseSensorsSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->noise_thresholds = new NoiseSensorsNoiseThresholdsClient( + $client, + $defaults, + ); + $this->simulate = new NoiseSensorsSimulateClient($client, $defaults); } /** @@ -36,6 +48,7 @@ public function __construct(SeamClient $seam) * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -108,7 +121,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/noise_sensors/list", json: (object) $request_payload, diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 80492126..af7ad0de 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\NoiseThreshold; -use Seam\SeamClient; class NoiseSensorsNoiseThresholdsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -35,15 +44,9 @@ public function create( ): NoiseThreshold { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($ends_daily_at !== null) { - $request_payload["ends_daily_at"] = $ends_daily_at; - } - if ($starts_daily_at !== null) { - $request_payload["starts_daily_at"] = $starts_daily_at; - } + $request_payload["device_id"] = $device_id; + $request_payload["ends_daily_at"] = $ends_daily_at; + $request_payload["starts_daily_at"] = $starts_daily_at; if ($name !== null) { $request_payload["name"] = $name; } @@ -56,7 +59,7 @@ public function create( $request_payload["noise_threshold_nrs"] = $noise_threshold_nrs; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/noise_sensors/noise_thresholds/create", json: (object) $request_payload, @@ -76,14 +79,10 @@ public function delete(string $device_id, string $noise_threshold_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($noise_threshold_id !== null) { - $request_payload["noise_threshold_id"] = $noise_threshold_id; - } + $request_payload["device_id"] = $device_id; + $request_payload["noise_threshold_id"] = $noise_threshold_id; - $this->seam->request( + $this->client->request( "POST", "/noise_sensors/noise_thresholds/delete", json: (object) $request_payload, @@ -100,11 +99,9 @@ public function get(string $noise_threshold_id): NoiseThreshold { $request_payload = []; - if ($noise_threshold_id !== null) { - $request_payload["noise_threshold_id"] = $noise_threshold_id; - } + $request_payload["noise_threshold_id"] = $noise_threshold_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/noise_sensors/noise_thresholds/get", json: (object) $request_payload, @@ -123,11 +120,9 @@ public function list(string $device_id): array { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/noise_sensors/noise_thresholds/list", json: (object) $request_payload, @@ -162,12 +157,8 @@ public function update( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($noise_threshold_id !== null) { - $request_payload["noise_threshold_id"] = $noise_threshold_id; - } + $request_payload["device_id"] = $device_id; + $request_payload["noise_threshold_id"] = $noise_threshold_id; if ($ends_daily_at !== null) { $request_payload["ends_daily_at"] = $ends_daily_at; } @@ -186,7 +177,7 @@ public function update( $request_payload["starts_daily_at"] = $starts_daily_at; } - $this->seam->request( + $this->client->request( "POST", "/noise_sensors/noise_thresholds/update", json: (object) $request_payload, diff --git a/src/Routes/NoiseSensorsSimulateClient.php b/src/Routes/NoiseSensorsSimulateClient.php index ac6c5891..7388b50b 100644 --- a/src/Routes/NoiseSensorsSimulateClient.php +++ b/src/Routes/NoiseSensorsSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class NoiseSensorsSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -23,11 +32,9 @@ public function trigger_noise_threshold(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/noise_sensors/simulate/trigger_noise_threshold", json: (object) $request_payload, diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index 922b3f64..b97d5243 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -2,17 +2,26 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Phone; -use Seam\SeamClient; class PhonesClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public PhonesSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new PhonesSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new PhonesSimulateClient($client, $defaults); } /** @@ -25,11 +34,9 @@ public function deactivate(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/phones/deactivate", json: (object) $request_payload, @@ -46,11 +53,9 @@ public function get(string $device_id): Phone { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/phones/get", json: (object) $request_payload, @@ -81,7 +86,7 @@ public function list( ] = $owner_user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/phones/list", json: (object) $request_payload, diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index e33ee226..85c5e1ff 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Phone; -use Seam\SeamClient; class PhonesSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -31,9 +40,7 @@ public function create_sandbox_phone( ): Phone { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; if ($assa_abloy_metadata !== null) { $request_payload["assa_abloy_metadata"] = $assa_abloy_metadata; } @@ -46,7 +53,7 @@ public function create_sandbox_phone( $request_payload["phone_metadata"] = $phone_metadata; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/phones/simulate/create_sandbox_phone", json: (object) $request_payload, diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index b90f93b9..46db212d 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -2,17 +2,26 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Batch; use Seam\Resources\Space; -use Seam\SeamClient; class SpacesClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -28,14 +37,10 @@ public function add_acs_entrances( ): void { $request_payload = []; - if ($acs_entrance_ids !== null) { - $request_payload["acs_entrance_ids"] = $acs_entrance_ids; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["acs_entrance_ids"] = $acs_entrance_ids; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/add_acs_entrances", json: (object) $request_payload, @@ -55,14 +60,10 @@ public function add_connected_account( ): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["connected_account_id"] = $connected_account_id; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/add_connected_account", json: (object) $request_payload, @@ -80,14 +81,10 @@ public function add_devices(array $device_ids, string $space_id): void { $request_payload = []; - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["device_ids"] = $device_ids; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/add_devices", json: (object) $request_payload, @@ -117,9 +114,7 @@ public function create( ): Space { $request_payload = []; - if ($name !== null) { - $request_payload["name"] = $name; - } + $request_payload["name"] = $name; if ($acs_entrance_ids !== null) { $request_payload["acs_entrance_ids"] = $acs_entrance_ids; } @@ -139,7 +134,7 @@ public function create( $request_payload["space_key"] = $space_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/create", json: (object) $request_payload, @@ -158,11 +153,9 @@ public function delete(string $space_id): void { $request_payload = []; - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/delete", json: (object) $request_payload, @@ -189,7 +182,7 @@ public function get( $request_payload["space_key"] = $space_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/get", json: (object) $request_payload, @@ -228,7 +221,7 @@ public function get_related( $request_payload["space_keys"] = $space_keys; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/get_related", json: (object) $request_payload, @@ -245,6 +238,7 @@ public function get_related( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned spaces to include all records that satisfy a partial match using `name`, `space_key`, or `customer_key`. * @param string $space_key Filter spaces by space_key. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -273,7 +267,7 @@ public function list( $request_payload["space_key"] = $space_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/list", json: (object) $request_payload, @@ -299,14 +293,10 @@ public function remove_acs_entrances( ): void { $request_payload = []; - if ($acs_entrance_ids !== null) { - $request_payload["acs_entrance_ids"] = $acs_entrance_ids; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["acs_entrance_ids"] = $acs_entrance_ids; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/remove_acs_entrances", json: (object) $request_payload, @@ -326,14 +316,10 @@ public function remove_connected_account( ): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["connected_account_id"] = $connected_account_id; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/remove_connected_account", json: (object) $request_payload, @@ -351,14 +337,10 @@ public function remove_devices(array $device_ids, string $space_id): void { $request_payload = []; - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["device_ids"] = $device_ids; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/remove_devices", json: (object) $request_payload, @@ -405,7 +387,7 @@ public function update( $request_payload["space_key"] = $space_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/update", json: (object) $request_payload, diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 650aa7d6..7d7c4b4d 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -2,22 +2,35 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; -use Seam\SeamClient; class ThermostatsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public ThermostatsDailyProgramsClient $daily_programs; public ThermostatsSchedulesClient $schedules; public ThermostatsSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->daily_programs = new ThermostatsDailyProgramsClient($seam); - $this->schedules = new ThermostatsSchedulesClient($seam); - $this->simulate = new ThermostatsSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->daily_programs = new ThermostatsDailyProgramsClient( + $client, + $defaults, + ); + $this->schedules = new ThermostatsSchedulesClient($client, $defaults); + $this->simulate = new ThermostatsSimulateClient($client, $defaults); } /** @@ -25,37 +38,31 @@ public function __construct(SeamClient $seam) * * @param string $climate_preset_key Climate preset key of the climate preset that you want to activate. * @param string $device_id ID of the thermostat device for which you want to activate a climate preset. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function activate_climate_preset( string $climate_preset_key, string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/activate_climate_preset", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -64,19 +71,18 @@ public function activate_climate_preset( * @param string $device_id ID of the thermostat device that you want to set to cool mode. * @param float $cooling_set_point_celsius [Cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °C that you want to set for the thermostat. You must set one of the `cooling_set_point` parameters. * @param float $cooling_set_point_fahrenheit [Cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `cooling_set_point` parameters. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function cool( string $device_id, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($cooling_set_point_celsius !== null) { $request_payload[ "cooling_set_point_celsius" @@ -88,21 +94,18 @@ public function cool( ] = $cooling_set_point_fahrenheit; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/cool", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -138,12 +141,8 @@ public function create_climate_preset( ): void { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; if ($climate_preset_mode !== null) { $request_payload["climate_preset_mode"] = $climate_preset_mode; } @@ -185,7 +184,7 @@ public function create_climate_preset( $request_payload["name"] = $name; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/create_climate_preset", json: (object) $request_payload, @@ -205,14 +204,10 @@ public function delete_climate_preset( ): void { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/thermostats/delete_climate_preset", json: (object) $request_payload, @@ -225,19 +220,18 @@ public function delete_climate_preset( * @param string $device_id ID of the thermostat device that you want to set to heat mode. * @param float $heating_set_point_celsius [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °C that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. * @param float $heating_set_point_fahrenheit [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function heat( string $device_id, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($heating_set_point_celsius !== null) { $request_payload[ "heating_set_point_celsius" @@ -249,21 +243,18 @@ public function heat( ] = $heating_set_point_fahrenheit; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/heat", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -274,6 +265,7 @@ public function heat( * @param float $cooling_set_point_fahrenheit [Cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `cooling_set_point` parameters. * @param float $heating_set_point_celsius [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °C that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. * @param float $heating_set_point_fahrenheit [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function heat_cool( @@ -282,13 +274,11 @@ public function heat_cool( ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($cooling_set_point_celsius !== null) { $request_payload[ "cooling_set_point_celsius" @@ -310,21 +300,18 @@ public function heat_cool( ] = $heating_set_point_fahrenheit; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/heat_cool", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -346,6 +333,7 @@ public function heat_cool( * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -418,7 +406,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/list", json: (object) $request_payload, @@ -435,33 +423,29 @@ public function list( * Sets a specified [thermostat](https://docs.seam.co/capability-guides/thermostats) to ["off" mode](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings). * * @param string $device_id ID of the thermostat device that you want to set to off mode. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function off( string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/off", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -477,14 +461,10 @@ public function set_fallback_climate_preset( ): void { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/thermostats/set_fallback_climate_preset", json: (object) $request_payload, @@ -497,19 +477,18 @@ public function set_fallback_climate_preset( * @param string $device_id ID of the thermostat device for which you want to set the fan mode. * @param string $fan_mode Fan mode setting for the thermostat, such as `auto`, `on`, or `circulate`. * @param string $fan_mode_setting [Fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings) that you want to set for the thermostat. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function set_fan_mode( string $device_id, ?string $fan_mode = null, ?string $fan_mode_setting = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($fan_mode !== null) { $request_payload["fan_mode"] = $fan_mode; } @@ -517,21 +496,18 @@ public function set_fan_mode( $request_payload["fan_mode_setting"] = $fan_mode_setting; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/set_fan_mode", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -543,6 +519,7 @@ public function set_fan_mode( * @param float $cooling_set_point_fahrenheit [Cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `cooling_set_point` parameters. * @param float $heating_set_point_celsius [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °C that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. * @param float $heating_set_point_fahrenheit [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function set_hvac_mode( @@ -552,16 +529,12 @@ public function set_hvac_mode( ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($hvac_mode_setting !== null) { - $request_payload["hvac_mode_setting"] = $hvac_mode_setting; - } + $request_payload["device_id"] = $device_id; + $request_payload["hvac_mode_setting"] = $hvac_mode_setting; if ($cooling_set_point_celsius !== null) { $request_payload[ "cooling_set_point_celsius" @@ -583,21 +556,18 @@ public function set_hvac_mode( ] = $heating_set_point_fahrenheit; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/set_hvac_mode", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -619,9 +589,7 @@ public function set_temperature_threshold( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($lower_limit_celsius !== null) { $request_payload["lower_limit_celsius"] = $lower_limit_celsius; } @@ -639,7 +607,7 @@ public function set_temperature_threshold( ] = $upper_limit_fahrenheit; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/set_temperature_threshold", json: (object) $request_payload, @@ -679,12 +647,8 @@ public function update_climate_preset( ): void { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; if ($climate_preset_mode !== null) { $request_payload["climate_preset_mode"] = $climate_preset_mode; } @@ -726,7 +690,7 @@ public function update_climate_preset( $request_payload["name"] = $name; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/update_climate_preset", json: (object) $request_payload, @@ -744,6 +708,7 @@ public function update_climate_preset( * @param string $thursday_program_id ID of the thermostat daily program to run on Thursdays. * @param string $tuesday_program_id ID of the thermostat daily program to run on Tuesdays. * @param string $wednesday_program_id ID of the thermostat daily program to run on Wednesdays. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function update_weekly_program( @@ -755,13 +720,11 @@ public function update_weekly_program( ?string $thursday_program_id = null, ?string $tuesday_program_id = null, ?string $wednesday_program_id = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($friday_program_id !== null) { $request_payload["friday_program_id"] = $friday_program_id; } @@ -784,20 +747,17 @@ public function update_weekly_program( $request_payload["wednesday_program_id"] = $wednesday_program_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/update_weekly_program", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 6d786342..19f8196f 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -2,17 +2,27 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\ThermostatDailyProgram; -use Seam\SeamClient; class ThermostatsDailyProgramsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -30,17 +40,11 @@ public function create( ): ThermostatDailyProgram { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($name !== null) { - $request_payload["name"] = $name; - } - if ($periods !== null) { - $request_payload["periods"] = $periods; - } - - $res = $this->seam->request( + $request_payload["device_id"] = $device_id; + $request_payload["name"] = $name; + $request_payload["periods"] = $periods; + + $res = $this->client->request( "POST", "/thermostats/daily_programs/create", json: (object) $request_payload, @@ -61,13 +65,11 @@ public function delete(string $thermostat_daily_program_id): void { $request_payload = []; - if ($thermostat_daily_program_id !== null) { - $request_payload[ - "thermostat_daily_program_id" - ] = $thermostat_daily_program_id; - } + $request_payload[ + "thermostat_daily_program_id" + ] = $thermostat_daily_program_id; - $this->seam->request( + $this->client->request( "POST", "/thermostats/daily_programs/delete", json: (object) $request_payload, @@ -80,42 +82,34 @@ public function delete(string $thermostat_daily_program_id): void * @param string $name Name of the thermostat daily program that you want to update. * @param array $periods Array of thermostat daily program periods. The periods that you specify overwrite any existing periods for the daily program. * @param string $thermostat_daily_program_id ID of the thermostat daily program that you want to update. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function update( string $name, array $periods, string $thermostat_daily_program_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($name !== null) { - $request_payload["name"] = $name; - } - if ($periods !== null) { - $request_payload["periods"] = $periods; - } - if ($thermostat_daily_program_id !== null) { - $request_payload[ - "thermostat_daily_program_id" - ] = $thermostat_daily_program_id; - } - - $res = $this->seam->request( + $request_payload["name"] = $name; + $request_payload["periods"] = $periods; + $request_payload[ + "thermostat_daily_program_id" + ] = $thermostat_daily_program_id; + + $res = $this->client->request( "POST", "/thermostats/daily_programs/update", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index fbc504b3..51864c69 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\ThermostatSchedule; -use Seam\SeamClient; class ThermostatsSchedulesClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -37,18 +46,10 @@ public function create( ): ThermostatSchedule { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($ends_at !== null) { - $request_payload["ends_at"] = $ends_at; - } - if ($starts_at !== null) { - $request_payload["starts_at"] = $starts_at; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; + $request_payload["ends_at"] = $ends_at; + $request_payload["starts_at"] = $starts_at; if ($is_override_allowed !== null) { $request_payload["is_override_allowed"] = $is_override_allowed; } @@ -61,7 +62,7 @@ public function create( $request_payload["name"] = $name; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/schedules/create", json: (object) $request_payload, @@ -80,13 +81,9 @@ public function delete(string $thermostat_schedule_id): void { $request_payload = []; - if ($thermostat_schedule_id !== null) { - $request_payload[ - "thermostat_schedule_id" - ] = $thermostat_schedule_id; - } + $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $this->seam->request( + $this->client->request( "POST", "/thermostats/schedules/delete", json: (object) $request_payload, @@ -103,13 +100,9 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule { $request_payload = []; - if ($thermostat_schedule_id !== null) { - $request_payload[ - "thermostat_schedule_id" - ] = $thermostat_schedule_id; - } + $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/schedules/get", json: (object) $request_payload, @@ -131,14 +124,12 @@ public function list( ): array { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($user_identifier_key !== null) { $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/schedules/list", json: (object) $request_payload, @@ -173,11 +164,7 @@ public function update( ): void { $request_payload = []; - if ($thermostat_schedule_id !== null) { - $request_payload[ - "thermostat_schedule_id" - ] = $thermostat_schedule_id; - } + $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; if ($climate_preset_key !== null) { $request_payload["climate_preset_key"] = $climate_preset_key; } @@ -199,7 +186,7 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/schedules/update", json: (object) $request_payload, diff --git a/src/Routes/ThermostatsSimulateClient.php b/src/Routes/ThermostatsSimulateClient.php index e3cf413f..9b00648f 100644 --- a/src/Routes/ThermostatsSimulateClient.php +++ b/src/Routes/ThermostatsSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class ThermostatsSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -34,12 +43,8 @@ public function hvac_mode_adjusted( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($hvac_mode !== null) { - $request_payload["hvac_mode"] = $hvac_mode; - } + $request_payload["device_id"] = $device_id; + $request_payload["hvac_mode"] = $hvac_mode; if ($cooling_set_point_celsius !== null) { $request_payload[ "cooling_set_point_celsius" @@ -61,7 +66,7 @@ public function hvac_mode_adjusted( ] = $heating_set_point_fahrenheit; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/simulate/hvac_mode_adjusted", json: (object) $request_payload, @@ -83,9 +88,7 @@ public function temperature_reached( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($temperature_celsius !== null) { $request_payload["temperature_celsius"] = $temperature_celsius; } @@ -95,7 +98,7 @@ public function temperature_reached( ] = $temperature_fahrenheit; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/simulate/temperature_reached", json: (object) $request_payload, diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 4dca58e2..e70ad961 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -2,22 +2,34 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsSystem; use Seam\Resources\AcsUser; use Seam\Resources\Device; use Seam\Resources\InstantKey; use Seam\Resources\UserIdentity; -use Seam\SeamClient; class UserIdentitiesClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public UserIdentitiesUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->unmanaged = new UserIdentitiesUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->unmanaged = new UserIdentitiesUnmanagedClient( + $client, + $defaults, + ); } /** @@ -39,9 +51,7 @@ public function add_acs_user( ): void { $request_payload = []; - if ($acs_user_id !== null) { - $request_payload["acs_user_id"] = $acs_user_id; - } + $request_payload["acs_user_id"] = $acs_user_id; if ($user_identity_id !== null) { $request_payload["user_identity_id"] = $user_identity_id; } @@ -49,7 +59,7 @@ public function add_acs_user( $request_payload["user_identity_key"] = $user_identity_key; } - $this->seam->request( + $this->client->request( "POST", "/user_identities/add_acs_user", json: (object) $request_payload, @@ -91,7 +101,7 @@ public function create( $request_payload["user_identity_key"] = $user_identity_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/create", json: (object) $request_payload, @@ -110,11 +120,9 @@ public function delete(string $user_identity_id): void { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $this->seam->request( + $this->client->request( "POST", "/user_identities/delete", json: (object) $request_payload, @@ -136,9 +144,7 @@ public function generate_instant_key( ): InstantKey { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; if ($customization_profile_id !== null) { $request_payload[ "customization_profile_id" @@ -148,7 +154,7 @@ public function generate_instant_key( $request_payload["max_use_count"] = $max_use_count; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/generate_instant_key", json: (object) $request_payload, @@ -177,7 +183,7 @@ public function get( $request_payload["user_identity_key"] = $user_identity_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/get", json: (object) $request_payload, @@ -199,14 +205,10 @@ public function grant_access_to_device( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["device_id"] = $device_id; + $request_payload["user_identity_id"] = $user_identity_id; - $this->seam->request( + $this->client->request( "POST", "/user_identities/grant_access_to_device", json: (object) $request_payload, @@ -222,6 +224,7 @@ public function grant_access_to_device( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned user identities to include all records that satisfy a partial match using `full_name`, `phone_number`, `email_address` or `user_identity_id`. * @param array $user_identity_ids Array of user identity IDs by which to filter the list of user identities. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -256,7 +259,7 @@ public function list( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list", json: (object) $request_payload, @@ -282,11 +285,9 @@ public function list_accessible_devices(string $user_identity_id): array { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list_accessible_devices", json: (object) $request_payload, @@ -305,11 +306,9 @@ public function list_accessible_entrances(string $user_identity_id): array { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list_accessible_entrances", json: (object) $request_payload, @@ -331,11 +330,9 @@ public function list_acs_systems(string $user_identity_id): array { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list_acs_systems", json: (object) $request_payload, @@ -354,11 +351,9 @@ public function list_acs_users(string $user_identity_id): array { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list_acs_users", json: (object) $request_payload, @@ -380,14 +375,10 @@ public function remove_acs_user( ): void { $request_payload = []; - if ($acs_user_id !== null) { - $request_payload["acs_user_id"] = $acs_user_id; - } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["acs_user_id"] = $acs_user_id; + $request_payload["user_identity_id"] = $user_identity_id; - $this->seam->request( + $this->client->request( "POST", "/user_identities/remove_acs_user", json: (object) $request_payload, @@ -407,14 +398,10 @@ public function revoke_access_to_device( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["device_id"] = $device_id; + $request_payload["user_identity_id"] = $user_identity_id; - $this->seam->request( + $this->client->request( "POST", "/user_identities/revoke_access_to_device", json: (object) $request_payload, @@ -440,9 +427,7 @@ public function update( ): void { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; if ($email_address !== null) { $request_payload["email_address"] = $email_address; } @@ -456,7 +441,7 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->seam->request( + $this->client->request( "POST", "/user_identities/update", json: (object) $request_payload, diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index e8f40773..6683b8a2 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedUserIdentity; -use Seam\SeamClient; class UserIdentitiesUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function get(string $user_identity_id): UnmanagedUserIdentity { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/unmanaged/get", json: (object) $request_payload, @@ -44,6 +51,7 @@ public function get(string $user_identity_id): UnmanagedUserIdentity * @param int $limit Maximum number of records to return per page. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned unmanaged user identities to include all records that satisfy a partial match using `full_name`, `phone_number`, `email_address`, `user_identity_id` or `acs_system_id`. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -68,7 +76,7 @@ public function list( $request_payload["search"] = $search; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/unmanaged/list", json: (object) $request_payload, @@ -101,17 +109,13 @@ public function update( ): void { $request_payload = []; - if ($is_managed !== null) { - $request_payload["is_managed"] = $is_managed; - } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["is_managed"] = $is_managed; + $request_payload["user_identity_id"] = $user_identity_id; if ($user_identity_key !== null) { $request_payload["user_identity_key"] = $user_identity_key; } - $this->seam->request( + $this->client->request( "POST", "/user_identities/unmanaged/update", json: (object) $request_payload, diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 67fd844c..4d95a1b7 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Webhook; -use Seam\SeamClient; class WebhooksClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -25,14 +34,12 @@ public function create(string $url, ?array $event_types = null): Webhook { $request_payload = []; - if ($url !== null) { - $request_payload["url"] = $url; - } + $request_payload["url"] = $url; if ($event_types !== null) { $request_payload["event_types"] = $event_types; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/webhooks/create", json: (object) $request_payload, @@ -51,11 +58,9 @@ public function delete(string $webhook_id): void { $request_payload = []; - if ($webhook_id !== null) { - $request_payload["webhook_id"] = $webhook_id; - } + $request_payload["webhook_id"] = $webhook_id; - $this->seam->request( + $this->client->request( "POST", "/webhooks/delete", json: (object) $request_payload, @@ -72,11 +77,9 @@ public function get(string $webhook_id): Webhook { $request_payload = []; - if ($webhook_id !== null) { - $request_payload["webhook_id"] = $webhook_id; - } + $request_payload["webhook_id"] = $webhook_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/webhooks/get", json: (object) $request_payload, @@ -92,7 +95,7 @@ public function get(string $webhook_id): Webhook */ public function list(): array { - $res = $this->seam->request("POST", "/webhooks/list"); + $res = $this->client->request("POST", "/webhooks/list"); return array_map(fn($r) => Webhook::from_json($r), $res->webhooks); } @@ -108,14 +111,10 @@ public function update(array $event_types, string $webhook_id): void { $request_payload = []; - if ($event_types !== null) { - $request_payload["event_types"] = $event_types; - } - if ($webhook_id !== null) { - $request_payload["webhook_id"] = $webhook_id; - } + $request_payload["event_types"] = $event_types; + $request_payload["webhook_id"] = $webhook_id; - $this->seam->request( + $this->client->request( "POST", "/webhooks/update", json: (object) $request_payload, diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 630f4755..42a52ec5 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -2,17 +2,27 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Workspace; -use Seam\SeamClient; class WorkspacesClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -44,9 +54,7 @@ public function create( ): Workspace { $request_payload = []; - if ($name !== null) { - $request_payload["name"] = $name; - } + $request_payload["name"] = $name; if ($company_name !== null) { $request_payload["company_name"] = $company_name; } @@ -83,7 +91,7 @@ public function create( ] = $webview_success_message; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/workspaces/create", json: (object) $request_payload, @@ -99,7 +107,7 @@ public function create( */ public function get(): Workspace { - $res = $this->seam->request("POST", "/workspaces/get"); + $res = $this->client->request("POST", "/workspaces/get"); return Workspace::from_json($res->workspace); } @@ -111,7 +119,7 @@ public function get(): Workspace */ public function list(): array { - $res = $this->seam->request("POST", "/workspaces/list"); + $res = $this->client->request("POST", "/workspaces/list"); return array_map(fn($r) => Workspace::from_json($r), $res->workspaces); } @@ -119,22 +127,20 @@ public function list(): array /** * Resets the [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) associated with the authentication value. Note that this endpoint is only available for sandbox workspaces. * + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function reset_sandbox( - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - $res = $this->seam->request("POST", "/workspaces/reset_sandbox"); + $res = $this->client->request("POST", "/workspaces/reset_sandbox"); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -181,7 +187,7 @@ public function update( $request_payload["organization_id"] = $organization_id; } - $this->seam->request( + $this->client->request( "POST", "/workspaces/update", json: (object) $request_payload, diff --git a/src/Seam.php b/src/Seam.php new file mode 100644 index 00000000..588c7617 --- /dev/null +++ b/src/Seam.php @@ -0,0 +1,256 @@ + $guzzle_options Options merged into the underlying Guzzle client, e.g. timeout or headers. + * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. + * @param ClientInterface|null $client A preconfigured Guzzle client. It carries its own endpoint and authorization, so no other authentication option may be given with it. + */ + public function __construct( + ?string $api_key = null, + ?string $personal_access_token = null, + ?string $workspace_id = null, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ?ClientInterface $client = null, + ) { + $this->defaults = [ + "wait_for_action_attempt" => $wait_for_action_attempt ?? true, + ]; + + // A client carries its own endpoint and authorization, so the + // authentication options are only read when one has to be built. + $this->client = + $client !== null + ? SeamHttpClient::from_client($client) + : new SeamHttpClient( + Options::get_endpoint($endpoint), + Auth::get_auth_headers( + $api_key, + $personal_access_token, + $workspace_id, + ), + $guzzle_options, + $retries, + ); + + $this->access_codes = new AccessCodesClient( + $this->client, + $this->defaults, + ); + $this->access_grants = new AccessGrantsClient( + $this->client, + $this->defaults, + ); + $this->access_methods = new AccessMethodsClient( + $this->client, + $this->defaults, + ); + $this->acs = new AcsClient($this->client, $this->defaults); + $this->action_attempts = new ActionAttemptsClient( + $this->client, + $this->defaults, + ); + $this->client_sessions = new ClientSessionsClient( + $this->client, + $this->defaults, + ); + $this->connect_webviews = new ConnectWebviewsClient( + $this->client, + $this->defaults, + ); + $this->connected_accounts = new ConnectedAccountsClient( + $this->client, + $this->defaults, + ); + $this->customers = new CustomersClient($this->client, $this->defaults); + $this->devices = new DevicesClient($this->client, $this->defaults); + $this->events = new EventsClient($this->client, $this->defaults); + $this->instant_keys = new InstantKeysClient( + $this->client, + $this->defaults, + ); + $this->locks = new LocksClient($this->client, $this->defaults); + $this->noise_sensors = new NoiseSensorsClient( + $this->client, + $this->defaults, + ); + $this->phones = new PhonesClient($this->client, $this->defaults); + $this->spaces = new SpacesClient($this->client, $this->defaults); + $this->thermostats = new ThermostatsClient( + $this->client, + $this->defaults, + ); + $this->user_identities = new UserIdentitiesClient( + $this->client, + $this->defaults, + ); + $this->webhooks = new WebhooksClient($this->client, $this->defaults); + $this->workspaces = new WorkspacesClient( + $this->client, + $this->defaults, + ); + } + + /** + * Creates a client authorized with an API key. + */ + public static function from_api_key( + string $api_key, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ): static { + return new static( + api_key: $api_key, + endpoint: $endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + guzzle_options: $guzzle_options, + retries: $retries, + ); + } + + /** + * Creates a client authorized with a personal access token, scoped to the + * given workspace. + */ + public static function from_personal_access_token( + string $personal_access_token, + string $workspace_id, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ): static { + return new static( + personal_access_token: $personal_access_token, + workspace_id: $workspace_id, + endpoint: $endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + retries: $retries, + guzzle_options: $guzzle_options, + ); + } + + /** + * Creates a client from a preconfigured Guzzle client. + */ + public static function from_client( + ClientInterface $client, + bool|array|null $wait_for_action_attempt = null, + ): static { + return new static( + client: $client, + wait_for_action_attempt: $wait_for_action_attempt, + ); + } + + public function lts_version(): string + { + return self::LTS_VERSION; + } + + /** + * Makes a request against the Seam API with this client's authorization. + * + * @param array|object|null $json + * @param array|null $query + */ + public function request( + string $method, + string $path, + mixed $json = null, + ?array $query = null, + ): mixed { + return $this->client->request($method, $path, $json, $query); + } + + /** + * Creates a paginator for a list endpoint. + * + * @param callable $request Invokes the list method with a params array, e.g. fn($params) => $seam->devices->list(...$params) + * @param array $params + */ + public function createPaginator( + callable $request, + array $params = [], + ): Paginator { + return new Paginator($request, $params); + } +} diff --git a/src/SeamClient.php b/src/SeamClient.php index f2384ab2..c43d7e1b 100644 --- a/src/SeamClient.php +++ b/src/SeamClient.php @@ -2,154 +2,8 @@ namespace Seam; -use Seam\Routes\AccessCodesClient; -use Seam\Routes\AccessGrantsClient; -use Seam\Routes\AccessMethodsClient; -use Seam\Routes\AcsClient; -use Seam\Routes\ActionAttemptsClient; -use Seam\Routes\ClientSessionsClient; -use Seam\Routes\ConnectedAccountsClient; -use Seam\Routes\ConnectWebviewsClient; -use Seam\Routes\CustomersClient; -use Seam\Routes\DevicesClient; -use Seam\Routes\EventsClient; -use Seam\Routes\InstantKeysClient; -use Seam\Routes\LocksClient; -use Seam\Routes\NoiseSensorsClient; -use Seam\Routes\PhonesClient; -use Seam\Routes\SpacesClient; -use Seam\Routes\ThermostatsClient; -use Seam\Routes\UserIdentitiesClient; -use Seam\Routes\WebhooksClient; -use Seam\Routes\WorkspacesClient; -use Seam\Utils\PackageVersion; - -use GuzzleHttp\Client as HTTPClient; -use \Exception as Exception; -use Seam\HttpApiError; -use Seam\HttpUnauthorizedError; -use Seam\HttpInvalidInputError; - -define("LTS_VERSION", "1.0.0"); - -class SeamClient -{ - public AccessCodesClient $access_codes; - public AccessGrantsClient $access_grants; - public AccessMethodsClient $access_methods; - public AcsClient $acs; - public ActionAttemptsClient $action_attempts; - public ClientSessionsClient $client_sessions; - public ConnectWebviewsClient $connect_webviews; - public ConnectedAccountsClient $connected_accounts; - public CustomersClient $customers; - public DevicesClient $devices; - public EventsClient $events; - public InstantKeysClient $instant_keys; - public LocksClient $locks; - public NoiseSensorsClient $noise_sensors; - public PhonesClient $phones; - public SpacesClient $spaces; - public ThermostatsClient $thermostats; - public UserIdentitiesClient $user_identities; - public WebhooksClient $webhooks; - public WorkspacesClient $workspaces; - - public string $api_key; - public HTTPClient $client; - public string $ltsVersion = LTS_VERSION; - - public function __construct( - $api_key = null, - $endpoint = "https://connect.getseam.com", - $throw_http_errors = false, - ) { - $this->api_key = $api_key ?: (getenv("SEAM_API_KEY") ?: null); - $seam_sdk_version = PackageVersion::get(); - $this->client = new HTTPClient([ - "base_uri" => $endpoint, - "timeout" => 60.0, - "headers" => [ - "Authorization" => "Bearer " . $this->api_key, - "User-Agent" => "Seam PHP Client " . $seam_sdk_version, - "seam-sdk-name" => "seamapi/php", - "seam-sdk-version" => $seam_sdk_version, - "seam-lts-version" => $this->ltsVersion, - ], - "http_errors" => $throw_http_errors, - ]); - $this->access_codes = new AccessCodesClient($this); - $this->access_grants = new AccessGrantsClient($this); - $this->access_methods = new AccessMethodsClient($this); - $this->acs = new AcsClient($this); - $this->action_attempts = new ActionAttemptsClient($this); - $this->client_sessions = new ClientSessionsClient($this); - $this->connect_webviews = new ConnectWebviewsClient($this); - $this->connected_accounts = new ConnectedAccountsClient($this); - $this->customers = new CustomersClient($this); - $this->devices = new DevicesClient($this); - $this->events = new EventsClient($this); - $this->instant_keys = new InstantKeysClient($this); - $this->locks = new LocksClient($this); - $this->noise_sensors = new NoiseSensorsClient($this); - $this->phones = new PhonesClient($this); - $this->spaces = new SpacesClient($this); - $this->thermostats = new ThermostatsClient($this); - $this->user_identities = new UserIdentitiesClient($this); - $this->webhooks = new WebhooksClient($this); - $this->workspaces = new WorkspacesClient($this); - } - - public function request($method, $path, $json = null, $query = null) - { - $options = [ - "json" => $json, - "query" => $query, - ]; - $options = array_filter($options, fn($option) => $option !== null); - - $response = $this->client->request($method, $path, $options); - $status_code = $response->getStatusCode(); - $request_id = $response->getHeaderLine("seam-request-id"); - - $res_json = null; - try { - $res_json = json_decode($response->getBody()); - } catch (Exception $ignoreError) { - } - - if ($status_code >= 400) { - if ($status_code === 401) { - throw new HttpUnauthorizedError($request_id); - } - - if (($res_json->error ?? null) != null) { - if ($res_json->error->type === "invalid_input") { - throw new HttpInvalidInputError( - $res_json->error, - $status_code, - $request_id, - ); - } - - throw new HttpApiError( - $res_json->error, - $status_code, - $request_id, - ); - } - - throw \GuzzleHttp\Exception\RequestException::create( - new \GuzzleHttp\Psr7\Request($method, $path), - $response, - ); - } - - return $res_json; - } - - public function createPaginator($request, $params = []) - { - return new Paginator($request, $params); - } -} +/** + * @deprecated Use Seam\Seam instead. This alias will be removed in a future + * major version. + */ +class SeamClient extends Seam {} diff --git a/src/SeamMultiWorkspace.php b/src/SeamMultiWorkspace.php new file mode 100644 index 00000000..315ffd29 --- /dev/null +++ b/src/SeamMultiWorkspace.php @@ -0,0 +1,91 @@ + $guzzle_options + */ + public function __construct( + ?string $personal_access_token = null, + ?string $endpoint = null, + array $guzzle_options = [], + ?int $retries = null, + ?ClientInterface $client = null, + ) { + // A client carries its own endpoint and authorization, so the + // authentication options are only read when one has to be built. + if ($client !== null) { + $this->client = SeamHttpClient::from_client($client); + } else { + if ($personal_access_token === null) { + throw new Exceptions\InvalidOptionsError( + "Must specify a personal_access_token", + ); + } + + $this->client = new SeamHttpClient( + Options::get_endpoint($endpoint), + Auth::get_auth_headers_for_multi_workspace_personal_access_token( + $personal_access_token, + ), + $guzzle_options, + $retries, + ); + } + + $this->workspaces = new WorkspacesProxy( + new WorkspacesClient($this->client, [ + "wait_for_action_attempt" => false, + ]), + ); + } + + /** + * Creates a client authorized with a personal access token, not scoped to + * any workspace. + */ + public static function from_personal_access_token( + string $personal_access_token, + ?string $endpoint = null, + array $guzzle_options = [], + ?int $retries = null, + ): static { + return new static( + personal_access_token: $personal_access_token, + endpoint: $endpoint, + guzzle_options: $guzzle_options, + retries: $retries, + ); + } + + /** + * Creates a client from a preconfigured Guzzle client. + */ + public static function from_client(ClientInterface $client): static + { + return new static(client: $client); + } + + public function lts_version(): string + { + return self::LTS_VERSION; + } +} diff --git a/src/SeamWebhook.php b/src/SeamWebhook.php new file mode 100644 index 00000000..9ab920af --- /dev/null +++ b/src/SeamWebhook.php @@ -0,0 +1,53 @@ +webhook = new Webhook($secret); + } + + /** + * Verifies an incoming webhook request and returns the event it carries. + * + * @param string $payload The raw HTTP request body. + * @param array $headers The HTTP request headers. + * + * @throws \Svix\Exception\WebhookVerificationException When the signature does not match. + */ + public function verify(string $payload, array $headers): Event + { + $normalized_headers = []; + foreach ($headers as $name => $value) { + $normalized_headers[strtolower($name)] = $value; + } + + $this->webhook->verify($payload, $normalized_headers); + + $event = Event::from_json(json_decode($payload)); + + if ($event === null) { + throw new WebhookVerificationException( + "The verified webhook payload did not contain an event", + ); + } + + return $event; + } +} diff --git a/src/Token.php b/src/Token.php new file mode 100644 index 00000000..90349502 --- /dev/null +++ b/src/Token.php @@ -0,0 +1,67 @@ +workspaces = $workspaces; + } + + /** + * @return Workspace[] + */ + public function list(): array + { + return $this->workspaces->list(); + } + + public function create( + string $name, + ?string $company_name = null, + ?string $connect_partner_name = null, + mixed $connect_webview_customization = null, + ?bool $is_sandbox = null, + ?string $organization_id = null, + ?string $webview_logo_shape = null, + ?string $webview_primary_button_color = null, + ?string $webview_primary_button_text_color = null, + ?string $webview_success_message = null, + ): Workspace { + return $this->workspaces->create( + $name, + $company_name, + $connect_partner_name, + $connect_webview_customization, + $is_sandbox, + $organization_id, + $webview_logo_shape, + $webview_primary_button_color, + $webview_primary_button_text_color, + $webview_success_message, + ); + } +} diff --git a/tests/ApiKeyTest.php b/tests/ApiKeyTest.php new file mode 100644 index 00000000..b1b8e9bb --- /dev/null +++ b/tests/ApiKeyTest.php @@ -0,0 +1,89 @@ +seed["seam_apikey1_token"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + $this->assertSame( + $this->seed["seed_workspace_1"], + $device->workspace_id, + ); + } + + public function testConstructorReturnsAnAuthorizedClient(): void + { + $seam = new Seam( + api_key: $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testInvalidApiKeyIsRejectedByTheServer(): void + { + $seam = new Seam( + api_key: "seam_invalid_api_key", + endpoint: $this->endpoint, + ); + + $this->expectException(HttpUnauthorizedError::class); + + $seam->devices->list(); + } + + /** + * @dataProvider unusableTokens + */ + public function testApiKeyFormatIsChecked( + string $token, + string $expected_message, + ): void { + $this->expectException(InvalidTokenError::class); + $this->expectExceptionMessage($expected_message); + + new Seam(api_key: $token, endpoint: $this->endpoint); + } + + public static function unusableTokens(): array + { + return [ + "client session token" => [ + "seam_cst_1234", + "A Client Session Token cannot be used as an api_key", + ], + "jwt" => ["ey_some_jwt", "A JWT cannot be used as an api_key"], + "access token" => [ + "seam_at_1234", + "An Access Token cannot be used as an api_key", + ], + "publishable key" => [ + "seam_pk_1234", + "A Publishable Key cannot be used as an api_key", + ], + "unknown format" => [ + "some-random-token", + "Unknown or invalid api_key format", + ], + ]; + } +} diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 00000000..739c263b --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,168 @@ +seam(); + + $res = $seam->client->request( + "POST", + "/devices/get", + (object) [ + "device_id" => $this->seed["august_device_1"], + ], + ); + + $this->assertSame( + $this->seed["august_device_1"], + $res->device->device_id, + ); + $this->assertSame( + $this->seed["seed_workspace_1"], + $res->device->workspace_id, + ); + } + + public function testRequestDelegatesToTheClient(): void + { + $seam = $this->seam(); + + $res = $seam->request( + "POST", + "/devices/get", + (object) [ + "device_id" => $this->seed["august_device_1"], + ], + ); + + $this->assertSame( + $this->seed["august_device_1"], + $res->device->device_id, + ); + } + + /** + * A client carries its own endpoint and authorization, so it has to work + * on its own without any authentication option beside it. + */ + public function testFromClientNeedsNoCredentials(): void + { + $authorized = $this->seam(); + + $seam = Seam::from_client($authorized->client->get_client()); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + $this->assertSame( + $this->seed["seed_workspace_1"], + $device->workspace_id, + ); + } + + public function testClientOptionReusesAnotherInstancesClient(): void + { + $seam = new Seam(client: $this->seam()->client->get_client()); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testGuzzleOptionsAreMergedIntoTheClient(): void + { + $seam = $this->seam( + guzzle_options: [ + "headers" => ["Custom-Header" => "Test-Value"], + "timeout" => 30, + ], + ); + + $config = $seam->client->get_client()->getConfig(); + + $this->assertSame(30, $config["timeout"]); + $this->assertSame("Test-Value", $config["headers"]["Custom-Header"]); + // The custom headers must not displace the authorization or the SDK + // headers. + $this->assertSame( + "Bearer " . $this->seed["seam_apikey1_token"], + $config["headers"]["authorization"], + ); + $this->assertSame("seamapi/php", $config["headers"]["seam-sdk-name"]); + } + + public function testGuzzleOptionsStillAuthorizeRequests(): void + { + $seam = $this->seam( + guzzle_options: ["headers" => ["Custom-Header" => "Test-Value"]], + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testTimeoutDefaultsToSixtySeconds(): void + { + $config = $this->seam()->client->get_client()->getConfig(); + + $this->assertSame(SeamHttpClient::DEFAULT_TIMEOUT, $config["timeout"]); + } + + /** + * Every constructor and factory has to agree on the default, otherwise + * the same call waits or does not depending on how the client was built. + */ + public function testWaitForActionAttemptDefaultsToTrueEverywhere(): void + { + $api_key = $this->seed["seam_apikey1_token"]; + + $clients = [ + "constructor" => new Seam( + api_key: $api_key, + endpoint: $this->endpoint, + ), + "from_api_key" => Seam::from_api_key( + $api_key, + endpoint: $this->endpoint, + ), + "from_personal_access_token" => Seam::from_personal_access_token( + $this->seed["seam_at1_token"], + $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ), + "from_client" => Seam::from_client( + $this->seam()->client->get_client(), + ), + ]; + + foreach ($clients as $name => $seam) { + $this->assertTrue( + $seam->defaults["wait_for_action_attempt"], + "{$name} should wait for action attempts by default", + ); + } + } + + public function testWaitForActionAttemptDefaultCanBeDisabled(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $this->assertFalse($seam->defaults["wait_for_action_attempt"]); + } + + public function testLtsVersionIsExposed(): void + { + $this->assertSame("1.0.0", Seam::LTS_VERSION); + $this->assertSame("1.0.0", $this->seam()->lts_version()); + } +} diff --git a/tests/EnvTest.php b/tests/EnvTest.php new file mode 100644 index 00000000..59d4886c --- /dev/null +++ b/tests/EnvTest.php @@ -0,0 +1,143 @@ + */ + private array $saved_env = []; + + protected function setUp(): void + { + parent::setUp(); + + foreach (self::VARIABLES as $name) { + $this->saved_env[$name] = getenv($name); + putenv($name); + } + } + + protected function tearDown(): void + { + foreach ($this->saved_env as $name => $value) { + if ($value === false) { + putenv($name); + } else { + putenv("{$name}={$value}"); + } + } + + parent::tearDown(); + } + + public function testReadsTheApiKeyFromTheEnvironment(): void + { + putenv("SEAM_API_KEY=" . $this->seed["seam_apikey1_token"]); + + $seam = new Seam(endpoint: $this->endpoint); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testReadsTheEndpointFromTheEnvironment(): void + { + putenv("SEAM_ENDPOINT=" . $this->endpoint); + + $seam = new Seam(api_key: $this->seed["seam_apikey1_token"]); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testFallsBackToTheDefaultEndpoint(): void + { + $this->assertSame(Options::DEFAULT_ENDPOINT, Options::get_endpoint()); + } + + public function testEndpointOptionWinsOverTheEnvironment(): void + { + putenv("SEAM_ENDPOINT=https://from-the-environment.example.com"); + + $this->assertSame( + "https://from-the-option.example.com", + Options::get_endpoint("https://from-the-option.example.com"), + ); + } + + public function testSeamEndpointWinsOverTheDeprecatedSeamApiUrl(): void + { + putenv("SEAM_ENDPOINT=https://endpoint.example.com"); + putenv("SEAM_API_URL=https://api-url.example.com"); + + // Both the deprecation and the precedence notice are raised. + $endpoint = @Options::get_endpoint(); + + $this->assertSame("https://endpoint.example.com", $endpoint); + } + + public function testDeprecatedSeamApiUrlIsStillHonored(): void + { + putenv("SEAM_API_URL=https://api-url.example.com"); + + $this->assertSame( + "https://api-url.example.com", + @Options::get_endpoint(), + ); + } + + public function testDeprecatedSeamApiUrlWarns(): void + { + putenv("SEAM_API_URL=https://api-url.example.com"); + + set_error_handler( + static fn( + int $severity, + string $message, + ) => throw new \ErrorException($message), + E_USER_WARNING, + ); + + try { + $this->expectException(\ErrorException::class); + $this->expectExceptionMessage("SEAM_API_URL"); + Options::get_endpoint(); + } finally { + restore_error_handler(); + } + } + + public function testFailsWhenNoCredentialsAreAvailable(): void + { + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage("SEAM_API_KEY is not set"); + + new Seam(endpoint: $this->endpoint); + } + + public function testApiKeyEnvironmentVariableIsIgnoredForAPersonalAccessToken(): void + { + putenv("SEAM_API_KEY=" . $this->seed["seam_apikey1_token"]); + + $seam = new Seam( + personal_access_token: $this->seed["seam_at1_token"], + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } +} diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php new file mode 100644 index 00000000..3f8cf4c7 --- /dev/null +++ b/tests/HeadersTest.php @@ -0,0 +1,141 @@ + ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + ); + + $device = $seam->devices->get("d1"); + + $this->assertSame("d1", $device->device_id); + + $request = $recorder->request(); + + $this->assertSame("/devices/get", $request->getUri()->getPath()); + $this->assertEquals((object) ["device_id" => "d1"], $recorder->body()); + + $this->assertSame( + "Bearer seam_apikey_token", + $request->getHeaderLine("authorization"), + ); + $this->assertSame( + "seamapi/php", + $request->getHeaderLine("seam-sdk-name"), + ); + $this->assertSame( + PackageVersion::get(), + $request->getHeaderLine("seam-sdk-version"), + ); + $this->assertSame( + SeamHttpClient::LTS_VERSION, + $request->getHeaderLine("seam-lts-version"), + ); + $this->assertSame( + "seam-php/" . PackageVersion::get(), + $request->getHeaderLine("User-Agent"), + ); + } + + public function testSendsWorkspaceHeaderWithAPersonalAccessToken(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, ["device" => ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_personal_access_token( + "seam_at_token", + "workspace-1", + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + ); + + $seam->devices->get("d1"); + + $request = $recorder->request(); + + $this->assertSame( + "Bearer seam_at_token", + $request->getHeaderLine("authorization"), + ); + $this->assertSame( + "workspace-1", + $request->getHeaderLine("seam-workspace"), + ); + } + + public function testCustomHeadersAreSentAlongsideTheSdkHeaders(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, ["device" => ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: array_merge($recorder->guzzle_options(), [ + "headers" => ["Custom-Header" => "Test-Value"], + ]), + ); + + $seam->devices->get("d1"); + + $request = $recorder->request(); + + $this->assertSame( + "Test-Value", + $request->getHeaderLine("Custom-Header"), + ); + $this->assertSame( + "seamapi/php", + $request->getHeaderLine("seam-sdk-name"), + ); + } + + /** + * The SDK headers identify the SDK, so a caller cannot displace them. + */ + public function testSdkHeadersCannotBeOverridden(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, ["device" => ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: array_merge($recorder->guzzle_options(), [ + "headers" => ["seam-sdk-name" => "not-the-sdk"], + ]), + ); + + $seam->devices->get("d1"); + + $this->assertSame( + "seamapi/php", + $recorder->request()->getHeaderLine("seam-sdk-name"), + ); + } +} diff --git a/tests/HttpErrorTest.php b/tests/HttpErrorTest.php index 648eb882..72a029cb 100644 --- a/tests/HttpErrorTest.php +++ b/tests/HttpErrorTest.php @@ -2,29 +2,118 @@ declare(strict_types=1); -use PHPUnit\Framework\TestCase; +namespace Tests; -final class HttpErrorTest extends TestCase +use Seam\Exceptions\HttpApiError; +use Seam\Exceptions\HttpInvalidInputError; +use Seam\Exceptions\HttpUnauthorizedError; +use Seam\Seam; +use Tests\Support\FakeSeamConnectTestCase; + +final class HttpErrorTest extends FakeSeamConnectTestCase { - public function testNonSeamError(): void + public function testThrowsUnauthorizedError(): void { - $seam = new \Seam\SeamClient( - "seam_apikey1_token", - "https://nonexistent.example.com", + $seam = new Seam( + api_key: "seam_invalid_api_key", + endpoint: $this->endpoint, ); try { $seam->devices->list(); - $this->fail("Expected GuzzleHttp ConnectException"); - } catch (\GuzzleHttp\Exception\ConnectException $e) { - $this->assertInstanceOf( - \GuzzleHttp\Exception\ConnectException::class, - $e, + $this->fail("Expected HttpUnauthorizedError"); + } catch (HttpUnauthorizedError $error) { + $this->assertSame(401, $error->getStatusCode()); + $this->assertSame("unauthorized", $error->getErrorCode()); + $this->assertStringStartsWith( + "request", + (string) $error->getRequestId(), + ); + } + } + + public function testThrowsApiErrorOnStandardErrorResponse(): void + { + try { + $this->seam()->devices->get("unknown-device"); + $this->fail("Expected HttpApiError"); + } catch (HttpApiError $error) { + $this->assertSame(404, $error->getStatusCode()); + $this->assertSame("device_not_found", $error->getErrorCode()); + $this->assertStringStartsWith( + "request", + (string) $error->getRequestId(), + ); + } + } + + public function testThrowsInvalidInputErrorWithValidationMessages(): void + { + try { + $this->seam()->client->request( + "POST", + "/devices/list", + (object) [ + "device_ids" => 4242, + ], + ); + $this->fail("Expected HttpInvalidInputError"); + } catch (HttpInvalidInputError $error) { + $this->assertSame(400, $error->getStatusCode()); + $this->assertSame("invalid_input", $error->getErrorCode()); + $this->assertStringStartsWith( + "request", + (string) $error->getRequestId(), + ); + $this->assertSame( + ["Expected array, received number"], + $error->getValidationErrorMessages("device_ids"), ); - $this->assertStringContainsString( - "Could not resolve host", - $e->getMessage(), + } + } + + public function testValidationMessagesAreEmptyForAnUnknownParam(): void + { + try { + $this->seam()->client->request( + "POST", + "/devices/list", + (object) [ + "device_ids" => 4242, + ], + ); + $this->fail("Expected HttpInvalidInputError"); + } catch (HttpInvalidInputError $error) { + $this->assertSame( + [], + $error->getValidationErrorMessages("non_existent_param"), ); } } + + /** + * A workspace outage answers with a 503 that is not a Seam error + * envelope, so it surfaces as the underlying transport error rather than + * a Seam exception, which is what the other Seam SDKs do too. + */ + public function testWorkspaceOutageSurfacesTheTransportError(): void + { + $seam = $this->seam(retries: 0); + + $seam->client->request( + "POST", + "/_fake/simulate_workspace_outage", + (object) [ + "workspace_id" => $this->seed["seed_workspace_1"], + "routes" => ["/devices/list"], + ], + ); + + try { + $seam->devices->list(); + $this->fail("Expected a Guzzle BadResponseException"); + } catch (\GuzzleHttp\Exception\BadResponseException $error) { + $this->assertSame(503, $error->getResponse()->getStatusCode()); + } + } } diff --git a/tests/MalformedResponseTest.php b/tests/MalformedResponseTest.php new file mode 100644 index 00000000..b9df3dd0 --- /dev/null +++ b/tests/MalformedResponseTest.php @@ -0,0 +1,112 @@ +guzzle_options(), + retries: 0, + ); + } + + /** + * @dataProvider nonSeamErrorResponses + */ + public function testNonSeamErrorResponsesSurfaceTheTransportError( + Response $response, + ): void { + $seam = $this->seam(new RecordingClient([$response])); + + try { + $seam->devices->list(); + $this->fail("Expected a Guzzle BadResponseException"); + } catch (HttpApiError $error) { + $this->fail( + "Expected a transport error, got " . + $error::class . + ": " . + $error->getMessage(), + ); + } catch (BadResponseException $error) { + $this->assertSame(500, $error->getResponse()->getStatusCode()); + } + } + + public static function nonSeamErrorResponses(): array + { + return [ + "plain text body" => [ + RecordingClient::raw(500, "Internal Server Error"), + ], + "html body" => [ + RecordingClient::raw( + 500, + "Gateway", + "text/html", + ), + ], + "malformed json" => [ + RecordingClient::raw(500, "{invalid json", "application/json"), + ], + "json without an error object" => [ + RecordingClient::json(500, ["message" => "Some error"]), + ], + "error without a type and message" => [ + RecordingClient::json(500, ["error" => ["code" => 500]]), + ], + "json that is not an object" => [ + RecordingClient::json(500, [1, 2]), + ], + "error that is not an object" => [ + RecordingClient::json(500, ["error" => "boom"]), + ], + "empty body" => [RecordingClient::raw(500, "", "application/json")], + ]; + } + + /** + * A redirect is not a success, so it must not be handed back to the + * caller as though it were a resource. + */ + public function testRedirectIsNotTreatedAsSuccess(): void + { + $recorder = new RecordingClient([ + new Response(302, ["location" => "https://example.com/elsewhere"]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: array_merge($recorder->guzzle_options(), [ + "allow_redirects" => false, + ]), + retries: 0, + ); + + // A 3xx is not a Seam error envelope either, so it surfaces as the + // transport error rather than being handed back as a resource. + $this->expectException(RequestException::class); + + $seam->devices->list(); + } +} diff --git a/tests/PackageVersionTest.php b/tests/PackageVersionTest.php index d45e970d..7b1d7f9a 100644 --- a/tests/PackageVersionTest.php +++ b/tests/PackageVersionTest.php @@ -28,8 +28,8 @@ public function testVersionMatchesPackageJson(): void public function testVersionIsUsedAsTheSdkVersionHeader(): void { - $seam = new \Seam\SeamClient("seam_apikey1_token"); - $headers = $seam->client->getConfig("headers"); + $seam = new \Seam\Seam("seam_apikey1_token"); + $headers = $seam->client->get_client()->getConfig("headers"); $this->assertSame(PackageVersion::get(), $headers["seam-sdk-version"]); } diff --git a/tests/PaginatorTest.php b/tests/PaginatorTest.php new file mode 100644 index 00000000..8442318a --- /dev/null +++ b/tests/PaginatorTest.php @@ -0,0 +1,123 @@ + 2]): Paginator + { + $seam = $this->seam(); + + return $seam->createPaginator( + fn($p) => $seam->connected_accounts->list(...$p), + $params, + ); + } + + public function testCreatePaginatorReturnsAPaginator(): void + { + $this->assertInstanceOf(Paginator::class, $this->paginator()); + } + + public function testFirstPageReturnsTheFirstPage(): void + { + [$accounts, $pagination] = $this->paginator()->firstPage(); + + $this->assertCount(2, $accounts); + $this->assertInstanceOf(Pagination::class, $pagination); + $this->assertTrue($pagination->has_next_page); + $this->assertNotNull($pagination->next_page_cursor); + } + + public function testNextPageReturnsTheNextPage(): void + { + $pages = $this->paginator(); + + [$first, $pagination] = $pages->firstPage(); + [$second] = $pages->nextPage($pagination->next_page_cursor); + + $this->assertNotEmpty($second); + + $first_ids = array_map( + fn($account) => $account->connected_account_id, + $first, + ); + $second_ids = array_map( + fn($account) => $account->connected_account_id, + $second, + ); + + $this->assertEmpty(array_intersect($first_ids, $second_ids)); + } + + public function testNextPageRequiresACursor(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("next_page_cursor"); + + $this->paginator()->nextPage(null); + } + + public function testNextPageRejectsAnEmptyCursor(): void + { + $this->expectException(\InvalidArgumentException::class); + + $this->paginator()->nextPage(""); + } + + public function testLastPageHasNoNextPage(): void + { + $pages = $this->paginator(["limit" => 100]); + + [, $pagination] = $pages->firstPage(); + + $this->assertFalse($pagination->has_next_page); + $this->assertNull($pagination->next_page_cursor); + } + + public function testFlattenToArrayReturnsEveryResource(): void + { + $all = $this->paginator()->flattenToArray(); + $expected = $this->seam()->connected_accounts->list(); + + $this->assertCount(count($expected), $all); + } + + public function testFlattenIteratesEveryResource(): void + { + $ids = []; + + foreach ($this->paginator()->flatten() as $account) { + $ids[] = $account->connected_account_id; + } + + $expected = $this->seam()->connected_accounts->list(); + + $this->assertCount(count($expected), $ids); + $this->assertSame(array_unique($ids), $ids); + } + + /** + * Not every list endpoint returns pagination metadata, and asking for a + * page of one that does not should not fail. + */ + public function testEndpointWithoutPaginationYieldsAnEmptyPagination(): void + { + $seam = $this->seam(); + + $pages = $seam->createPaginator(fn($p) => $seam->workspaces->list()); + + [$workspaces, $pagination] = $pages->firstPage(); + + $this->assertNotEmpty($workspaces); + $this->assertInstanceOf(Pagination::class, $pagination); + $this->assertFalse($pagination->has_next_page); + $this->assertNull($pagination->next_page_cursor); + } +} diff --git a/tests/PersonalAccessTokenTest.php b/tests/PersonalAccessTokenTest.php new file mode 100644 index 00000000..5abd2d81 --- /dev/null +++ b/tests/PersonalAccessTokenTest.php @@ -0,0 +1,140 @@ +seed["seam_at1_token"], + $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + $this->assertSame( + $this->seed["seed_workspace_1"], + $device->workspace_id, + ); + } + + public function testConstructorReturnsAnAuthorizedClient(): void + { + $seam = new Seam( + personal_access_token: $this->seed["seam_at1_token"], + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testWorkspaceIdIsRequired(): void + { + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage( + "Must pass a workspace_id when using a personal_access_token", + ); + + new Seam( + personal_access_token: $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + } + + public function testApiKeyCannotBeCombinedWithAPersonalAccessToken(): void + { + $this->expectException(InvalidOptionsError::class); + + new Seam( + api_key: $this->seed["seam_apikey1_token"], + personal_access_token: $this->seed["seam_at1_token"], + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + } + + public function testPersonalAccessTokenFormatIsChecked(): void + { + $this->expectException(InvalidTokenError::class); + + new Seam( + personal_access_token: "seam_cst_1234", + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + } + + public function testMultiWorkspaceClientListsWorkspaces(): void + { + $seam = SeamMultiWorkspace::from_personal_access_token( + $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + + $workspaces = $seam->workspaces->list(); + + $workspace_ids = array_map( + fn($workspace) => $workspace->workspace_id, + $workspaces, + ); + + $this->assertContains($this->seed["seed_workspace_1"], $workspace_ids); + } + + public function testMultiWorkspaceConstructorListsWorkspaces(): void + { + $seam = new SeamMultiWorkspace( + personal_access_token: $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + + $this->assertNotEmpty($seam->workspaces->list()); + } + + public function testMultiWorkspaceClientCreatesAWorkspace(): void + { + $seam = SeamMultiWorkspace::from_personal_access_token( + $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + + $workspace = $seam->workspaces->create( + name: "Test Workspace", + connect_partner_name: "Test Partner", + is_sandbox: true, + ); + + $this->assertSame("Test Workspace", $workspace->name); + } + + public function testMultiWorkspaceClientRequiresAToken(): void + { + $this->expectException(InvalidOptionsError::class); + + new SeamMultiWorkspace(endpoint: $this->endpoint); + } + + public function testMultiWorkspaceClientChecksTheTokenFormat(): void + { + $this->expectException(InvalidTokenError::class); + + SeamMultiWorkspace::from_personal_access_token( + $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + ); + } +} diff --git a/tests/RetryTest.php b/tests/RetryTest.php new file mode 100644 index 00000000..322697e7 --- /dev/null +++ b/tests/RetryTest.php @@ -0,0 +1,139 @@ + ["type" => "service_unavailable", "message" => "Down"], + ]); + } + + private static function devices(): \GuzzleHttp\Psr7\Response + { + return RecordingClient::json(200, ["devices" => []]); + } + + private function seam(RecordingClient $recorder, ?int $retries = null): Seam + { + return new Seam( + api_key: self::API_KEY, + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + retries: $retries, + ); + } + + /** + * Every Seam endpoint is a POST. Retrying one the server may already have + * processed could duplicate a write, so a status code never triggers a + * retry. The Ruby SDK asserts the same thing. + */ + public function testDoesNotRetryPostOnServiceUnavailable(): void + { + $recorder = RecordingClient::repeating(self::service_unavailable()); + + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the 503 to surface"); + } catch (\Throwable) { + // The error mapping is covered in HttpErrorTest. + } + + $this->assertSame(1, $recorder->attempt_count()); + } + + /** + * A connection failure never reached the server, so retrying it cannot + * duplicate anything. + */ + public function testRetriesPostOnConnectionFailure(): void + { + $connect_error = new ConnectException( + "Could not resolve host", + new Request("POST", "/devices/list"), + ); + + $recorder = new RecordingClient([ + $connect_error, + $connect_error, + self::devices(), + ]); + + $devices = $this->seam($recorder)->devices->list(); + + $this->assertSame([], $devices); + $this->assertSame(3, $recorder->attempt_count()); + } + + public function testStopsRetryingOnceRetriesAreExhausted(): void + { + $connect_error = new ConnectException( + "Could not resolve host", + new Request("POST", "/devices/list"), + ); + + $recorder = RecordingClient::repeating_throwable($connect_error); + + $this->expectException(ConnectException::class); + + try { + $this->seam($recorder, retries: 1)->devices->list(); + } finally { + $this->assertSame(2, $recorder->attempt_count()); + } + } + + public function testDoesNotRetryWhenRetriesAreDisabled(): void + { + $connect_error = new ConnectException( + "Could not resolve host", + new Request("POST", "/devices/list"), + ); + + $recorder = RecordingClient::repeating_throwable($connect_error); + + try { + $this->seam($recorder, retries: 0)->devices->list(); + $this->fail("Expected the connection failure to surface"); + } catch (ConnectException) { + // Expected. + } + + $this->assertSame(1, $recorder->attempt_count()); + } + + /** + * The SDK itself only issues POSTs, but a caller reaching for the client + * directly with an idempotent method does get status based retries. + */ + public function testRetriesIdempotentRequestsOnServiceUnavailable(): void + { + $recorder = new RecordingClient([ + self::service_unavailable(), + self::service_unavailable(), + self::devices(), + ]); + + $res = $this->seam($recorder)->client->request("GET", "/devices/list"); + + $this->assertSame([], $res->devices); + $this->assertSame(3, $recorder->attempt_count()); + } +} diff --git a/tests/SeamWebhookTest.php b/tests/SeamWebhookTest.php new file mode 100644 index 00000000..be871513 --- /dev/null +++ b/tests/SeamWebhookTest.php @@ -0,0 +1,103 @@ + "8d7e0b26-5e6c-4a1f-9b3d-1b0f0e5a9c11", + "event_type" => "device.connected", + "workspace_id" => "398d80b7-3f96-47c2-b85a-6f8ba21d07be", + "device_id" => "054765c8-a2fc-4599-b486-14c19f462c45", + "created_at" => "2024-01-01T00:00:00.000Z", + "occurred_at" => "2024-01-01T00:00:00.000Z", + ]); + } + + /** + * @return array + */ + private function signed_headers(string $payload): array + { + $id = "msg_test"; + $timestamp = (string) time(); + + $signature = (new Webhook(self::SECRET))->sign( + $id, + $timestamp, + $payload, + ); + + return [ + "svix-id" => $id, + "svix-timestamp" => $timestamp, + "svix-signature" => $signature, + ]; + } + + public function testVerifyReturnsTheEvent(): void + { + $payload = $this->payload(); + + $event = (new SeamWebhook(self::SECRET))->verify( + $payload, + $this->signed_headers($payload), + ); + + $this->assertSame("device.connected", $event->event_type); + $this->assertSame( + "8d7e0b26-5e6c-4a1f-9b3d-1b0f0e5a9c11", + $event->event_id, + ); + } + + public function testVerifyAcceptsHeadersInAnyCase(): void + { + $payload = $this->payload(); + + $headers = []; + foreach ($this->signed_headers($payload) as $name => $value) { + $headers[strtoupper($name)] = $value; + } + + $event = (new SeamWebhook(self::SECRET))->verify($payload, $headers); + + $this->assertSame("device.connected", $event->event_type); + } + + public function testVerifyRejectsATamperedPayload(): void + { + $payload = $this->payload(); + $headers = $this->signed_headers($payload); + + $this->expectException(WebhookVerificationException::class); + + (new SeamWebhook(self::SECRET))->verify( + str_replace("device.connected", "device.disconnected", $payload), + $headers, + ); + } + + public function testVerifyRejectsTheWrongSecret(): void + { + $payload = $this->payload(); + $headers = $this->signed_headers($payload); + + $this->expectException(WebhookVerificationException::class); + + (new SeamWebhook( + "whsec_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + ))->verify($payload, $headers); + } +} diff --git a/tests/SerializationTest.php b/tests/SerializationTest.php new file mode 100644 index 00000000..83310710 --- /dev/null +++ b/tests/SerializationTest.php @@ -0,0 +1,55 @@ +seam()->devices->list(); + + $this->assertNotEmpty($devices); + } + + public function testNullArrayParameterIsNotSent(): void + { + $devices = $this->seam()->devices->list(device_ids: null); + + $this->assertCount(count($this->seam()->devices->list()), $devices); + } + + public function testEmptyArrayParameterIsSent(): void + { + $devices = $this->seam()->devices->list(device_ids: []); + + $this->assertCount(0, $devices); + } + + public function testPopulatedArrayParameterFiltersTheResults(): void + { + $device_ids = [ + $this->seed["august_device_1"], + $this->seed["ecobee_device_1"], + ]; + + $devices = $this->seam()->devices->list(device_ids: $device_ids); + + $this->assertCount(2, $devices); + + $returned_ids = array_map(fn($device) => $device->device_id, $devices); + + sort($returned_ids); + sort($device_ids); + + $this->assertSame($device_ids, $returned_ids); + } +} diff --git a/tests/Support/FakeSeamConnect.php b/tests/Support/FakeSeamConnect.php new file mode 100644 index 00000000..93976532 --- /dev/null +++ b/tests/Support/FakeSeamConnect.php @@ -0,0 +1,171 @@ + */ + private array $pipes = []; + + public static function start(): self + { + $fake = new self(); + $fake->run(); + + return $fake; + } + + public function endpoint(): string + { + return $this->endpoint; + } + + /** + * The ids and tokens of the seeded records. + */ + public function seed(): array + { + return $this->seed; + } + + private function run(): void + { + $binary = dirname(__DIR__, 2) . "/node_modules/.bin/fake-seam-connect"; + + if (!is_executable($binary)) { + throw new \RuntimeException( + "Could not find {$binary}, run npm install before the tests.", + ); + } + + $port = self::unused_port(); + $this->endpoint = "http://127.0.0.1:{$port}"; + + // The binary is spawned directly rather than through npm so the + // process handle is the server itself and stopping it does not leave + // an orphan behind. PORT goes to the child only, leaving the parent + // environment alone for the tests that read it. + $this->process = proc_open( + [$binary, "--seed"], + [ + 0 => ["file", "/dev/null", "r"], + 1 => ["file", "/dev/null", "w"], + 2 => ["file", "/dev/null", "w"], + ], + $this->pipes, + dirname(__DIR__, 2), + ["PORT" => (string) $port] + getenv(), + ); + + if (!is_resource($this->process)) { + throw new \RuntimeException("Could not start Fake Seam Connect."); + } + + $this->wait_for_health(); + $this->seed = $this->fetch_seed(); + } + + public function stop(): void + { + if (!is_resource($this->process)) { + return; + } + + proc_terminate($this->process, SIGTERM); + + $deadline = microtime(true) + self::SHUTDOWN_TIMEOUT; + while (microtime(true) < $deadline) { + if (!proc_get_status($this->process)["running"]) { + break; + } + usleep(self::POLL_INTERVAL); + } + + if (proc_get_status($this->process)["running"]) { + proc_terminate($this->process, SIGKILL); + } + + proc_close($this->process); + $this->process = null; + } + + private function wait_for_health(): void + { + $deadline = microtime(true) + self::STARTUP_TIMEOUT; + + while (microtime(true) < $deadline) { + if (!proc_get_status($this->process)["running"]) { + throw new \RuntimeException( + "Fake Seam Connect exited before becoming healthy.", + ); + } + + if ($this->get("/health") !== null) { + return; + } + + usleep(self::POLL_INTERVAL); + } + + throw new \RuntimeException( + "Fake Seam Connect did not become healthy within " . + self::STARTUP_TIMEOUT . + "s.", + ); + } + + private function fetch_seed(): array + { + $body = $this->get("/_fake/default_seed"); + + if ($body === null) { + throw new \RuntimeException( + "Could not read the seed from Fake Seam Connect.", + ); + } + + return json_decode($body, true); + } + + private function get(string $path): ?string + { + $context = stream_context_create([ + "http" => ["timeout" => 5, "ignore_errors" => true], + ]); + + $body = @file_get_contents($this->endpoint . $path, false, $context); + + return $body === false ? null : $body; + } + + private static function unused_port(): int + { + $socket = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr); + + if ($socket === false) { + throw new \RuntimeException( + "Could not find an unused port: {$errstr}", + ); + } + + $name = stream_socket_get_name($socket, false); + fclose($socket); + + return (int) substr($name, strrpos($name, ":") + 1); + } +} diff --git a/tests/Support/FakeSeamConnectTestCase.php b/tests/Support/FakeSeamConnectTestCase.php new file mode 100644 index 00000000..81f91400 --- /dev/null +++ b/tests/Support/FakeSeamConnectTestCase.php @@ -0,0 +1,45 @@ +fake = FakeSeamConnect::start(); + $this->endpoint = $this->fake->endpoint(); + $this->seed = $this->fake->seed(); + } + + protected function tearDown(): void + { + $this->fake->stop(); + } + + /** + * A client authorized against the fake with the seeded API key. + */ + protected function seam( + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ): Seam { + return new Seam( + api_key: $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + guzzle_options: $guzzle_options, + retries: $retries, + ); + } +} diff --git a/tests/Support/RecordingClient.php b/tests/Support/RecordingClient.php new file mode 100644 index 00000000..d2494332 --- /dev/null +++ b/tests/Support/RecordingClient.php @@ -0,0 +1,107 @@ + */ + public array $transactions = []; + + private MockHandler $mock; + + /** + * @param array $responses Served in order; the last one repeats. + */ + public function __construct(private array $responses) + { + $this->mock = new MockHandler($responses); + } + + /** + * Builds the Guzzle options to hand to a Seam client so its requests land + * here instead of on the network. + * + * @return array + */ + public function guzzle_options(): array + { + $stack = HandlerStack::create($this->mock); + $stack->push(Middleware::history($this->transactions)); + + return ["handler" => $stack]; + } + + /** + * How many logical calls the SDK made. Retries are invisible here because + * the SDK pushes its retry middleware inside this recorder; use + * attempt_count() to count those. + */ + public function request_count(): int + { + return count($this->transactions); + } + + /** + * How many requests actually reached the handler, retries included. + */ + public function attempt_count(): int + { + return count($this->responses) - $this->mock->count(); + } + + public function request(int $index = 0): RequestInterface + { + return $this->transactions[$index]["request"]; + } + + public function body(int $index = 0): mixed + { + return json_decode((string) $this->request($index)->getBody()); + } + + /** + * A response whose body repeats for every request, so retries keep + * failing the same way. + */ + public static function repeating(Response $response, int $times = 20): self + { + return new self(array_fill(0, $times, $response)); + } + + public static function repeating_throwable( + \Throwable $error, + int $times = 20, + ): self { + return new self(array_fill(0, $times, $error)); + } + + public static function json(int $status, mixed $body): Response + { + return new Response( + $status, + ["content-type" => "application/json"], + json_encode($body), + ); + } + + public static function raw( + int $status, + string $body, + string $content_type = "text/plain", + ): Response { + return new Response($status, ["content-type" => $content_type], $body); + } +} diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php new file mode 100644 index 00000000..d6bbe194 --- /dev/null +++ b/tests/WaitForActionAttemptTest.php @@ -0,0 +1,283 @@ +locks->unlock_door( + $this->seed["august_device_1"], + ); + + $this->assertSame("pending", $action_attempt->status); + + $this->set_status($seam, $action_attempt, "pending"); + + return $action_attempt; + } + + private function set_status( + Seam $seam, + ActionAttempt $action_attempt, + string $status, + ?array $error = null, + ): void { + $seam->client->request( + "POST", + "/_fake/update_action_attempt", + (object) array_filter([ + "action_attempt_id" => $action_attempt->action_attempt_id, + "status" => $status, + "error" => $error, + ]), + ); + } + + public function testWaitsByDefault(): void + { + $action_attempt = $this->seam()->locks->unlock_door( + $this->seed["august_device_1"], + ); + + $this->assertSame("success", $action_attempt->status); + } + + public function testClientDefaultCanDisableWaiting(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + + $this->assertSame("pending", $action_attempt->status); + } + + /** + * The options form of the client default has to wait just like `true` + * does; treating it as "no waiting" would hand back a pending attempt + * with no indication anything was skipped. + */ + public function testClientDefaultCanBeAnOptionsArray(): void + { + $seam = $this->seam( + wait_for_action_attempt: [ + "timeout" => 5.0, + "polling_interval" => 0.05, + ], + ); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + + $this->assertSame("success", $action_attempt->status); + } + + public function testPerCallOptionCanDisableWaiting(): void + { + $action_attempt = $this->seam()->locks->unlock_door( + $this->seed["august_device_1"], + wait_for_action_attempt: false, + ); + + $this->assertSame("pending", $action_attempt->status); + } + + public function testPerCallOptionCanEnableWaiting(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + wait_for_action_attempt: true, + ); + + $this->assertSame("success", $action_attempt->status); + } + + public function testReturnsAnAlreadySuccessfulActionAttempt(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + $this->set_status($seam, $action_attempt, "success"); + + $resolved = $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: true, + ); + + $this->assertSame("success", $resolved->status); + $this->assertSame( + $action_attempt->action_attempt_id, + $resolved->action_attempt_id, + ); + } + + /** + * Proves the resolver really re-reads the action attempt: it starts out + * pending and is moved to success by something outside this process, the + * way the JavaScript and Ruby suites do it. + */ + public function testWaitsForAnActionAttemptResolvedOutOfBand(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + $action_attempt = $this->pending_action_attempt($seam); + + $resolver = $this->resolve_after( + $action_attempt->action_attempt_id, + 0.5, + ); + + try { + $resolved = $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: [ + "timeout" => 15.0, + "polling_interval" => 0.1, + ], + ); + + $this->assertSame("success", $resolved->status); + } finally { + proc_close($resolver); + } + } + + /** + * @return resource + */ + private function resolve_after(string $action_attempt_id, float $delay) + { + $payload = json_encode([ + "action_attempt_id" => $action_attempt_id, + "status" => "success", + ]); + + $script = sprintf( + "usleep(%d); file_get_contents(%s, false, stream_context_create(%s));", + (int) ($delay * 1000000.0), + var_export($this->endpoint . "/_fake/update_action_attempt", true), + var_export( + [ + "http" => [ + "method" => "POST", + "header" => "Content-Type: application/json", + "content" => $payload, + "ignore_errors" => true, + ], + ], + true, + ), + ); + + $process = proc_open( + [PHP_BINARY, "-r", $script], + [ + 1 => ["file", "/dev/null", "w"], + 2 => ["file", "/dev/null", "w"], + ], + $pipes, + ); + + if (!is_resource($process)) { + $this->fail("Could not start the out of band resolver"); + } + + return $process; + } + + public function testThrowsWhenTheActionAttemptFails(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + $this->set_status($seam, $action_attempt, "error", [ + "type" => "foo", + "message" => "Failed", + ]); + + try { + $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: true, + ); + $this->fail("Expected ActionAttemptFailedError"); + } catch (ActionAttemptFailedError $error) { + $this->assertSame("Failed", $error->getMessage()); + $this->assertSame("foo", $error->getErrorCode()); + $this->assertSame("error", $error->getActionAttempt()->status); + $this->assertSame( + $action_attempt->action_attempt_id, + $error->getActionAttempt()->action_attempt_id, + ); + $this->assertInstanceOf(ActionAttemptError::class, $error); + } + } + + public function testTimesOutWhileTheActionAttemptIsPending(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + $action_attempt = $this->pending_action_attempt($seam); + + try { + $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: [ + "timeout" => 0.2, + "polling_interval" => 5.0, + ], + ); + $this->fail("Expected ActionAttemptTimeoutError"); + } catch (ActionAttemptTimeoutError $error) { + $this->assertSame( + $action_attempt->action_attempt_id, + $error->getActionAttempt()->action_attempt_id, + ); + $this->assertStringContainsString( + "Timed out waiting for action attempt", + $error->getMessage(), + ); + } + } + + /** + * Resolving fetches the action attempt through the HTTP client rather + * than the route client, so enabling the option on the route that reads + * action attempts cannot recurse. + */ + public function testActionAttemptsGetDoesNotRecurse(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + $this->set_status($seam, $action_attempt, "success"); + + $resolved = $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: [ + "timeout" => 1.0, + "polling_interval" => 0.05, + ], + ); + + $this->assertSame("success", $resolved->status); + } +} From e3c87edbc018aa14901c9a3c4c467f47691c1e87 Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 11 Aug 2026 17:06:12 +0000 Subject: [PATCH 02/80] refactor: keep the exception classes in the Seam namespace The core public API is small enough to read at a glance, so nesting part of it under Seam\Exceptions bought organization it does not need. Keeping the classes where 3.x had them also means existing catch blocks keep working. Sub-namespacing errors is the more common PHP convention, but the Python and JavaScript SDKs both export theirs at the package root, so this is closer to them as well. The new SeamException marker interface, InvalidOptionsError, and InvalidTokenError are all that changes for a caller upgrading from 3.x. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 21 ++++++++++--------- src/{Exceptions => }/ActionAttemptError.php | 2 +- .../ActionAttemptFailedError.php | 2 +- .../ActionAttemptTimeoutError.php | 2 +- src/Auth.php | 3 --- src/Http/ResolveActionAttempt.php | 4 ++-- src/Http/SeamHttpClient.php | 6 +++--- src/{Exceptions => }/HttpApiError.php | 2 +- .../HttpInvalidInputError.php | 2 +- .../HttpUnauthorizedError.php | 2 +- src/{Exceptions => }/InvalidOptionsError.php | 2 +- src/{Exceptions => }/InvalidTokenError.php | 2 +- src/Options.php | 2 -- src/{Exceptions => }/SeamException.php | 2 +- src/SeamMultiWorkspace.php | 2 +- tests/ApiKeyTest.php | 4 ++-- tests/EnvTest.php | 2 +- tests/HttpErrorTest.php | 6 +++--- tests/MalformedResponseTest.php | 2 +- tests/PersonalAccessTokenTest.php | 4 ++-- tests/WaitForActionAttemptTest.php | 6 +++--- 21 files changed, 38 insertions(+), 42 deletions(-) rename src/{Exceptions => }/ActionAttemptError.php (95%) rename src/{Exceptions => }/ActionAttemptFailedError.php (96%) rename src/{Exceptions => }/ActionAttemptTimeoutError.php (94%) rename src/{Exceptions => }/HttpApiError.php (97%) rename src/{Exceptions => }/HttpInvalidInputError.php (96%) rename src/{Exceptions => }/HttpUnauthorizedError.php (93%) rename src/{Exceptions => }/InvalidOptionsError.php (93%) rename src/{Exceptions => }/InvalidTokenError.php (92%) rename src/{Exceptions => }/SeamException.php (87%) diff --git a/README.md b/README.md index 35ef6120..9ba25b03 100644 --- a/README.md +++ b/README.md @@ -108,18 +108,18 @@ By default the SDK waits for the action attempt to finish: - It polls up to a timeout at a polling interval. - It returns a fresh copy of the successful action attempt. -- It throws `Seam\Exceptions\ActionAttemptFailedError` if the action failed. -- It throws `Seam\Exceptions\ActionAttemptTimeoutError` if the timeout +- It throws `Seam\ActionAttemptFailedError` if the action failed. +- It throws `Seam\ActionAttemptTimeoutError` if the timeout elapses first. -Both errors extend `Seam\Exceptions\ActionAttemptError` and expose the action +Both errors extend `Seam\ActionAttemptError` and expose the action attempt with `getActionAttempt()`. [action attempt]: https://docs.seam.co/latest/core-concepts/action-attempts ```php -use Seam\Exceptions\ActionAttemptFailedError; -use Seam\Exceptions\ActionAttemptTimeoutError; +use Seam\ActionAttemptFailedError; +use Seam\ActionAttemptTimeoutError; try { $seam->locks->unlock_door($device_id); @@ -355,7 +355,7 @@ $seam = Seam\Seam::from_client($client); #### Errors -Every exception the SDK raises implements `Seam\Exceptions\SeamException`. +Every exception the SDK raises implements `Seam\SeamException`. | Error | Raised when | | --------------------------- | -------------------------------------------------- | @@ -373,8 +373,8 @@ BadResponseException` instead. Transport failures surface as the corresponding Guzzle exception. ```php -use Seam\Exceptions\HttpApiError; -use Seam\Exceptions\HttpInvalidInputError; +use Seam\HttpApiError; +use Seam\HttpInvalidInputError; try { $seam->devices->get($device_id); @@ -397,8 +397,9 @@ Version 4 brings this SDK in line with the Seam SDKs for other languages. - The constructor takes named options. `$endpoint` is no longer the second positional argument, and `$throw_http_errors` is gone; API errors always raise a Seam exception. -- Exceptions moved to the `Seam\Exceptions` namespace, e.g. - `Seam\HttpApiError` is now `Seam\Exceptions\HttpApiError`. +- The exception classes keep their `Seam\` namespace. They now all implement + a shared `Seam\SeamException` interface, and `Seam\InvalidOptionsError` and + `Seam\InvalidTokenError` are new. - `$seam->action_attempts->poll_until_ready()` was removed. Use `wait_for_action_attempt`, which now also accepts a `timeout` and `polling_interval`. The defaults changed from 20s/0.4s to 10s/1s. diff --git a/src/Exceptions/ActionAttemptError.php b/src/ActionAttemptError.php similarity index 95% rename from src/Exceptions/ActionAttemptError.php rename to src/ActionAttemptError.php index 7368770b..776ee6fe 100644 --- a/src/Exceptions/ActionAttemptError.php +++ b/src/ActionAttemptError.php @@ -1,6 +1,6 @@ client = SeamHttpClient::from_client($client); } else { if ($personal_access_token === null) { - throw new Exceptions\InvalidOptionsError( + throw new InvalidOptionsError( "Must specify a personal_access_token", ); } diff --git a/tests/ApiKeyTest.php b/tests/ApiKeyTest.php index b1b8e9bb..838fa24a 100644 --- a/tests/ApiKeyTest.php +++ b/tests/ApiKeyTest.php @@ -4,8 +4,8 @@ namespace Tests; -use Seam\Exceptions\HttpUnauthorizedError; -use Seam\Exceptions\InvalidTokenError; +use Seam\HttpUnauthorizedError; +use Seam\InvalidTokenError; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; diff --git a/tests/EnvTest.php b/tests/EnvTest.php index 59d4886c..3c0f92a7 100644 --- a/tests/EnvTest.php +++ b/tests/EnvTest.php @@ -4,7 +4,7 @@ namespace Tests; -use Seam\Exceptions\InvalidOptionsError; +use Seam\InvalidOptionsError; use Seam\Options; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; diff --git a/tests/HttpErrorTest.php b/tests/HttpErrorTest.php index 72a029cb..d445defc 100644 --- a/tests/HttpErrorTest.php +++ b/tests/HttpErrorTest.php @@ -4,9 +4,9 @@ namespace Tests; -use Seam\Exceptions\HttpApiError; -use Seam\Exceptions\HttpInvalidInputError; -use Seam\Exceptions\HttpUnauthorizedError; +use Seam\HttpApiError; +use Seam\HttpInvalidInputError; +use Seam\HttpUnauthorizedError; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; diff --git a/tests/MalformedResponseTest.php b/tests/MalformedResponseTest.php index b9df3dd0..e132c10a 100644 --- a/tests/MalformedResponseTest.php +++ b/tests/MalformedResponseTest.php @@ -8,7 +8,7 @@ use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\TestCase; -use Seam\Exceptions\HttpApiError; +use Seam\HttpApiError; use Seam\Seam; use Tests\Support\RecordingClient; diff --git a/tests/PersonalAccessTokenTest.php b/tests/PersonalAccessTokenTest.php index 5abd2d81..bce47735 100644 --- a/tests/PersonalAccessTokenTest.php +++ b/tests/PersonalAccessTokenTest.php @@ -4,8 +4,8 @@ namespace Tests; -use Seam\Exceptions\InvalidOptionsError; -use Seam\Exceptions\InvalidTokenError; +use Seam\InvalidOptionsError; +use Seam\InvalidTokenError; use Seam\Seam; use Seam\SeamMultiWorkspace; use Tests\Support\FakeSeamConnectTestCase; diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index d6bbe194..32dae081 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -4,9 +4,9 @@ namespace Tests; -use Seam\Exceptions\ActionAttemptError; -use Seam\Exceptions\ActionAttemptFailedError; -use Seam\Exceptions\ActionAttemptTimeoutError; +use Seam\ActionAttemptError; +use Seam\ActionAttemptFailedError; +use Seam\ActionAttemptTimeoutError; use Seam\Resources\ActionAttempt; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; From 1236f7bf746a9fc49860449824c9ac25ac668be7 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 18:08:31 +0000 Subject: [PATCH 03/80] fix: namespace the generated nested resource classes A nested class was named after the root resource plus the property, but the base name was never replaced as the recursion descended, so every shape below the first level competed for one name and addClass silently kept whichever was generated first. device.properties.battery and device.properties.accessory_keypad.battery both resolved to DeviceBattery. The keypad won, so the device battery was generated with only level and $device->properties->battery->status did not exist. The same collapse hit the climate preset metadata, which took the shape of properties.ecobee_metadata and lost climate_ref, is_optimized and owner, and the phone_session credential and entrance metadata pairs. The flat map is now a recursive tree. A nested class is named after its property alone and declared in the namespace of the class that owns it, so the two batteries are Device\Properties\Battery and Device\Properties\AccessoryKeypad\Battery. Properties reference their nested classes relatively, letting PHP resolve them from the owning namespace. This also keeps Seam\Resources free of the hundreds of names that existed only to type a property. Where the old code silently overwrote, codegen now throws: on two siblings producing the same class name, on a name PHP reserves as a type, and on nesting deeper than 16 levels, which means a cyclic schema rather than a real shape. Merging a discriminated union moves to codegen/lib/merge-properties.ts, which unions by name recursively rather than taking the first occurrence, so a merged class keeps every variant's fields. A merged property keeps its description only when every variant that documents it agrees, because each variant documents the property for its own case and that text is not necessarily true of the class the variants collapse into. Deprecation is now deprecate-if-any, since first wins could undeprecate a field depending on blueprint ordering. This drops the description on is_device_error, which claimed the error is not a device error on every variant including the ones where it is, while is_bridge_error keeps the text all variants share. Resource classes are emitted as one braced namespace block per namespace in a single file per resource, which works because src/Resources is autoloaded by classmap rather than PSR-4. The resource and property docblock helpers are indented one level deeper to match. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- codegen/layouts/resource.hbs | 33 +- codegen/lib/handlebars-helpers.ts | 6 +- codegen/lib/layouts/resource.ts | 42 +- codegen/lib/merge-properties.ts | 148 + codegen/lib/resource-model.ts | 331 +- src/Resources/AccessCode.php | 964 +-- src/Resources/AccessGrant.php | 804 ++- src/Resources/AccessMethod.php | 538 +- src/Resources/AcsAccessGroup.php | 645 +- src/Resources/AcsCredential.php | 722 +- src/Resources/AcsEncoder.php | 190 +- src/Resources/AcsEntrance.php | 1386 ++-- src/Resources/AcsSystem.php | 504 +- src/Resources/AcsUser.php | 870 ++- src/Resources/ActionAttempt.php | 1890 ++--- src/Resources/Batch.php | 425 +- src/Resources/ClientSession.php | 170 +- src/Resources/ConnectWebview.php | 245 +- src/Resources/ConnectedAccount.php | 677 +- src/Resources/CustomerPortal.php | 88 +- src/Resources/Device.php | 8277 +++++++++++----------- src/Resources/DeviceProvider.php | 284 +- src/Resources/Event.php | 1710 +++-- src/Resources/InstantKey.php | 187 +- src/Resources/NoiseThreshold.php | 101 +- src/Resources/Phone.php | 482 +- src/Resources/Space.php | 264 +- src/Resources/ThermostatDailyProgram.php | 150 +- src/Resources/ThermostatSchedule.php | 206 +- src/Resources/UnmanagedAccessCode.php | 667 +- src/Resources/UnmanagedAccessGrant.php | 782 +- src/Resources/UnmanagedAccessMethod.php | 521 +- src/Resources/UnmanagedDevice.php | 1002 +-- src/Resources/UnmanagedUserIdentity.php | 285 +- src/Resources/UserIdentity.php | 288 +- src/Resources/Webhook.php | 70 +- src/Resources/Workspace.php | 213 +- tests/ResourceTest.php | 161 + 38 files changed, 13669 insertions(+), 12659 deletions(-) create mode 100644 codegen/lib/merge-properties.ts create mode 100644 tests/ResourceTest.php diff --git a/codegen/layouts/resource.hbs b/codegen/layouts/resource.hbs index a7d7f4ed..271e4355 100644 --- a/codegen/layouts/resource.hbs +++ b/codegen/layouts/resource.hbs @@ -1,34 +1,37 @@ } +// Resource classes and their properties are emitted inside a braced namespace +// block, so both sit one level deeper than a docblock at file scope would. export const resourcePhpDoc = (context: DeprecatedPhpDocContext): string => - createPhpDoc(context.description, deprecatedTag(context)) + createPhpDoc(context.description, deprecatedTag(context), ' ') export const hasPhpDoc = (context: DeprecatedPhpDocContext): boolean => context.description.trim() !== '' || context.isDeprecated export const propertyPhpDoc = (context: DeprecatedPhpDocContext): string => - createPhpDoc(context.description, deprecatedTag(context), ' ') + createPhpDoc(context.description, deprecatedTag(context), ' ') export const methodPhpDoc = (context: MethodPhpDocContext): string => createPhpDoc( diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index d0400061..6ebd0ee6 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -1,7 +1,7 @@ // Builds the template context for resource files (src/Resources/{Name}.php): -// the resource class followed by the local classes for its object properties. -// Each class contributes its from_json body lines and constructor parameter -// lines. +// the resource class and the nested classes for its object properties, grouped +// into one braced namespace block per namespace. Each class contributes its +// from_json body lines and constructor parameter lines. // // The blueprint does not track which resource properties are required, so // every property is optional: from_json falls back to null for missing values @@ -29,10 +29,15 @@ export interface ConstructorParamLayoutContext { deprecationMessage: string } -export interface ResourceLayoutContext { +export interface NamespaceLayoutContext { + namespace: string classes: ClassLayoutContext[] } +export interface ResourceLayoutContext { + namespaces: NamespaceLayoutContext[] +} + const generateFromJsonProp = (property: ResourceClassProperty): string => { const { name } = property @@ -96,8 +101,27 @@ const getClassLayoutContext = ( export const setResourceLayoutContext = ( resource: ResourceSchema, -): ResourceLayoutContext => ({ - classes: [resource.resourceClass, ...resource.localClasses].map( - getClassLayoutContext, - ), -}) +): ResourceLayoutContext => { + // First appearance order, so the resource class leads the file and every + // owning namespace precedes the namespaces nested inside it. + const namespaces = new Map() + + for (const schema of resource.classes) { + const classes = namespaces.get(schema.namespace) + const context = getClassLayoutContext(schema) + + if (classes == null) { + namespaces.set(schema.namespace, [context]) + continue + } + + classes.push(context) + } + + return { + namespaces: [...namespaces.entries()].map(([namespace, classes]) => ({ + namespace, + classes, + })), + } +} diff --git a/codegen/lib/merge-properties.ts b/codegen/lib/merge-properties.ts new file mode 100644 index 00000000..43a28523 --- /dev/null +++ b/codegen/lib/merge-properties.ts @@ -0,0 +1,148 @@ +// Merges the property lists of a discriminated union into one list. +// +// Two things in the blueprint are unions the SDK collapses into a single +// class: the event and action attempt resources, which the blueprint models as +// one resource per variant, and any property that is a list of discriminated +// objects. +// +// This logic is shared with the Ruby and Python SDKs. Changing the semantics +// here means changing them there too, or the SDKs drift. + +import type { Property } from '@seamapi/blueprint' + +/** + * Unions property lists by name, recursively. + * + * A name carried by one variant is taken as is. A name carried by several is + * merged: objects and lists of objects recurse, lists of discriminated objects + * concatenate their variants for whoever consumes the list to merge in turn, + * and scalars collapse onto one another because they map to the same PHP type. + */ +export const mergeProperties = ( + propertyLists: Property[][], + path = '', +): Property[] => { + const occurrencesByName = new Map() + + for (const properties of propertyLists) { + for (const property of properties) { + const occurrences = occurrencesByName.get(property.name) + if (occurrences == null) { + occurrencesByName.set(property.name, [property]) + continue + } + occurrences.push(property) + } + } + + return [...occurrencesByName.entries()].map(([name, occurrences]) => + mergeOccurrences(occurrences, path === '' ? name : `${path}.${name}`), + ) +} + +const mergeOccurrences = (occurrences: Property[], path: string): Property => { + const [first] = occurrences + + if (first == null) { + throw new Error(`Cannot merge ${path}: no occurrences`) + } + + if (occurrences.length === 1) return first + + // Each variant documents the property for its own case, which is accurate + // there and not necessarily on the class the variants collapse into. Keep + // the text only when every variant that documents it agrees, since no + // documentation beats wrong documentation. + const docs = mergeDocs(occurrences) + + const formatKeys = new Set(occurrences.map(getFormatKey)) + + if (formatKeys.size > 1) { + if (occurrences.every(isScalar)) { + // Differing scalar formats map to the same PHP type, so any one will do. + return { ...first, ...docs } + } + + throw new Error( + `Cannot merge ${path}: variants disagree on its shape (${[...formatKeys].sort().join(', ')})`, + ) + } + + if (first.format === 'object') { + return { + ...first, + ...docs, + properties: mergeProperties( + occurrences.map((occurrence) => + occurrence.format === 'object' ? occurrence.properties : [], + ), + path, + ), + } + } + + if (first.format === 'list' && first.itemFormat === 'object') { + return { + ...first, + ...docs, + itemProperties: mergeProperties( + occurrences.map((occurrence) => + occurrence.format === 'list' && occurrence.itemFormat === 'object' + ? occurrence.itemProperties + : [], + ), + path, + ), + } + } + + if (first.format === 'list' && first.itemFormat === 'discriminated_object') { + return { + ...first, + ...docs, + variants: occurrences.flatMap((occurrence) => + occurrence.format === 'list' && + occurrence.itemFormat === 'discriminated_object' + ? occurrence.variants + : [], + ), + } + } + + return { ...first, ...docs } +} + +interface MergedDocs { + description: string + isDeprecated: boolean + deprecationMessage: string +} + +const mergeDocs = (occurrences: Property[]): MergedDocs => { + const descriptions = [ + ...new Set( + occurrences + .map((occurrence) => occurrence.description.trim()) + .filter((description) => description !== ''), + ), + ] + + // Deprecating a property in any variant deprecates it on the merged class: + // first wins could silently undeprecate a field depending on the order the + // blueprint happens to list the variants in. + const deprecated = occurrences.find((occurrence) => occurrence.isDeprecated) + + return { + description: descriptions.length === 1 ? (descriptions[0] ?? '') : '', + isDeprecated: deprecated != null, + deprecationMessage: deprecated?.deprecationMessage ?? '', + } +} + +// A list of objects and a list of scalars are different shapes, so the item +// format is part of the key. +const getFormatKey = (property: Property): string => + property.format === 'list' ? `list<${property.itemFormat}>` : property.format + +const isScalar = (property: Property): boolean => + property.format !== 'list' && property.format !== 'object' diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index e29a192f..2d9bccd3 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -1,18 +1,22 @@ // Builds the resource class model for src/Resources from the blueprint. // -// Each blueprint resource becomes a PHP class in its own file. Nested object -// properties and lists of objects are split into their own classes, named -// after the base resource and the property, e.g. the device battery property -// becomes DeviceBattery. Those classes only exist to type a resource -// property, so they are emitted as local classes in the file of the resource -// that introduced them. Discriminated unions (events, action attempts, and -// discriminated object lists) are flattened into a single class with the -// union of the variant properties. - -import type { Blueprint, Property } from '@seamapi/blueprint' +// Each blueprint resource becomes a PHP class in its own file. A nested object +// property, or a list of objects, becomes its own class named after the +// property alone and declared in the namespace of the class that owns it, so +// the device battery is Seam\Resources\Device\Properties\Battery. Nesting the +// classes keeps Seam\Resources free of the hundreds of names that exist only +// to type a property, and lets two properties of the same name at different +// depths keep their own shapes. +// +// Discriminated unions (events, action attempts, and discriminated object +// lists) are flattened into a single class holding the union of the variant +// properties. + +import type { Blueprint, Property, Resource } from '@seamapi/blueprint' import { pascalCase } from 'change-case' import { getPhpType } from './map-php-type.js' +import { mergeProperties } from './merge-properties.js' export type ResourceClassProperty = | ({ kind: 'value'; phpType: string } & ResourceClassPropertyMetadata) @@ -34,6 +38,7 @@ interface ResourceClassPropertyMetadata { export interface ResourceClassSchema { name: string + namespace: string description: string isDeprecated: boolean deprecationMessage: string @@ -42,8 +47,7 @@ export interface ResourceClassSchema { export interface ResourceSchema { name: string - resourceClass: ResourceClassSchema - localClasses: ResourceClassSchema[] + classes: ResourceClassSchema[] } export interface ResourceModel { @@ -51,6 +55,46 @@ export interface ResourceModel { resources: ResourceSchema[] } +const rootNamespace = 'Seam\\Resources' + +// A cyclic schema would recurse forever. Real shapes are nowhere near this +// deep, so exceeding it means the blueprint is cyclic rather than nested. +const maxDepth = 16 + +// PHP reserves these as type names, so a class cannot be called any of them. +// Class names are case insensitive, so the check has to be too. +const reservedClassNames = new Set([ + 'array', + 'bool', + 'callable', + 'enum', + 'false', + 'float', + 'int', + 'iterable', + 'mixed', + 'never', + 'null', + 'object', + 'parent', + 'self', + 'static', + 'string', + 'true', + 'void', +]) + +interface ClassDocs { + description: string + isDeprecated: boolean + deprecationMessage: string +} + +interface BuiltClass { + schema: ResourceClassSchema + nestedClasses: BuiltClass[] +} + export const createResourceModel = (blueprint: Blueprint): ResourceModel => { const baseResources = new Map() @@ -65,7 +109,10 @@ export const createResourceModel = (blueprint: Blueprint): ResourceModel => { if (events.length > 0) { baseResources.set( 'event', - mergeProperties(events.map((event) => event.properties)), + mergeProperties( + events.map((event) => event.properties), + 'event', + ), ) } @@ -75,164 +122,172 @@ export const createResourceModel = (blueprint: Blueprint): ResourceModel => { 'action_attempt', mergeProperties( actionAttempts.map((actionAttempt) => actionAttempt.properties), + 'action_attempt', ), ) } - const classes = new Map() - const localClassNames = new Map() - - let currentResourceName = '' - - const addClass = ( - name: string, - properties: Property[], - baseName: string, - description = '', - isDeprecated = false, - deprecationMessage = '', - ): void => { - if (classes.has(name)) return - const schema: ResourceClassSchema = { - name, - description, - isDeprecated, - deprecationMessage, - properties: [], - } - classes.set(name, schema) - if (name !== currentResourceName) { - localClassNames.get(currentResourceName)?.push(name) - } - schema.properties = properties.map((property) => - createResourceClassProperty(property, baseName, addClass), - ) - } - const baseResourceTypes = [...baseResources.keys()].sort() + const resources = baseResourceTypes.map((resourceType) => { const name = pascalCase(resourceType) - currentResourceName = name - localClassNames.set(name, []) - const sourceResource = - blueprint.resources.find( - (resource) => resource.resourceType === resourceType, - ) ?? - (resourceType === 'event' - ? blueprint.events[0] - : blueprint.actionAttempts[0]) - addClass( + const sourceResource = getSourceResource(blueprint, resourceType) + + const built = buildClass( name, + rootNamespace, baseResources.get(resourceType) ?? [], resourceType, - sourceResource?.description, - sourceResource?.isDeprecated, - sourceResource?.deprecationMessage, + 0, + { + description: sourceResource?.description ?? '', + isDeprecated: sourceResource?.isDeprecated ?? false, + deprecationMessage: sourceResource?.deprecationMessage ?? '', + }, + ) + + return { name, classes: flattenClasses(built) } + }) + + return { + resourceNames: resources.map((resource) => resource.name), + resources, + } +} + +const getSourceResource = ( + blueprint: Blueprint, + resourceType: string, +): Resource | undefined => + blueprint.resources.find( + (resource) => resource.resourceType === resourceType, + ) ?? + (resourceType === 'event' + ? blueprint.events[0] + : resourceType === 'action_attempt' + ? blueprint.actionAttempts[0] + : undefined) + +const buildClass = ( + className: string, + namespace: string, + classProperties: Property[], + path: string, + depth: number, + docs: ClassDocs, +): BuiltClass => { + if (depth > maxDepth) { + throw new Error( + `Cannot generate ${path}: nesting exceeded a depth of ${maxDepth}, which means the schema is cyclic`, + ) + } + + // A class at namespace N with short name S owns its children at N\S. + const nestedNamespace = `${namespace}\\${className}` + const nestedClasses: BuiltClass[] = [] + const takenClassNames = new Set() + + const properties = classProperties.map((property): ResourceClassProperty => { + const metadata = { + name: property.name, + description: property.description, + isDeprecated: property.isDeprecated, + deprecationMessage: property.deprecationMessage, + } + + const nestedProperties = getNestedProperties( + property, + `${path}.${property.name}`, ) - const resourceClass = classes.get(name) - if (resourceClass == null) { + if (nestedProperties == null) { + return { ...metadata, kind: 'value', phpType: getPhpType(property) } + } + + const nestedClassName = pascalCase(property.name) + const nestedPath = `${path}.${property.name}` + + if (reservedClassNames.has(nestedClassName.toLowerCase())) { + throw new Error( + `Cannot generate ${nestedPath}: ${nestedClassName} is a reserved PHP type name`, + ) + } + + if (takenClassNames.has(nestedClassName)) { throw new Error( - `Missing class for resource ${resourceType}: ${name} is already used by a property class of another resource`, + `Cannot generate ${nestedPath}: ${nestedClassName} is already used by a sibling property in ${nestedNamespace}`, ) } + takenClassNames.add(nestedClassName) + + nestedClasses.push( + buildClass( + nestedClassName, + nestedNamespace, + nestedProperties, + nestedPath, + depth + 1, + { + description: property.description, + isDeprecated: property.isDeprecated, + deprecationMessage: property.deprecationMessage, + }, + ), + ) + + // Referenced relative to the namespace the owning class is declared in, + // so PHP resolves Device\Properties from within Seam\Resources. return { - name, - resourceClass, - localClasses: (localClassNames.get(name) ?? []) - .map((localClassName) => { - const localClass = classes.get(localClassName) - if (localClass == null) { - throw new Error(`Missing local class ${localClassName}`) - } - return localClass - }) - .sort((a, b) => a.name.localeCompare(b.name)), + ...metadata, + kind: property.format === 'list' ? 'listReference' : 'objectReference', + referenceName: `${className}\\${nestedClassName}`, } }) return { - resourceNames: resources.map((resource) => resource.name), - resources, + schema: { + name: className, + namespace, + ...docs, + properties, + }, + nestedClasses, } } -const createResourceClassProperty = ( +// Decides whether a property earns a class of its own. An object with no +// documented properties stays a plain value rather than becoming an empty +// class. +const getNestedProperties = ( property: Property, - baseName: string, - addClass: ( - name: string, - properties: Property[], - baseName: string, - description?: string, - isDeprecated?: boolean, - deprecationMessage?: string, - ) => void, -): ResourceClassProperty => { - const referenceName = pascalCase(`${baseName}_${property.name}`) - const metadata = { - name: property.name, - description: property.description, - isDeprecated: property.isDeprecated, - deprecationMessage: property.deprecationMessage, - } - + path: string, +): Property[] | undefined => { if (property.format === 'object') { - const { properties } = property - - if (properties.length > 0) { - addClass(referenceName, properties, baseName, property.description) - return { ...metadata, kind: 'objectReference', referenceName } - } + return property.properties.length > 0 ? property.properties : undefined } if (property.format === 'list') { - const itemProperties = - property.itemFormat === 'object' + if (property.itemFormat === 'object') { + return property.itemProperties.length > 0 ? property.itemProperties - : property.itemFormat === 'discriminated_object' - ? mergeProperties( - property.variants.map((variant) => variant.properties), - ) - : [] - - if (itemProperties.length > 0) { - addClass(referenceName, itemProperties, baseName, property.description) - return { ...metadata, kind: 'listReference', referenceName } + : undefined } - } - - return { - ...metadata, - kind: 'value', - phpType: getPhpType(property), - } -} -const mergeProperties = (propertyLists: Property[][]): Property[] => { - const merged = new Map() - - for (const properties of propertyLists) { - for (const property of properties) { - const existing = merged.get(property.name) - - if (existing == null) { - merged.set(property.name, property) - continue - } - - if (existing.format === 'object' && property.format === 'object') { - merged.set(property.name, { - ...existing, - properties: mergeProperties([ - existing.properties, - property.properties, - ]), - }) - } + if (property.itemFormat === 'discriminated_object') { + const itemProperties = mergeProperties( + property.variants.map((variant) => variant.properties), + path, + ) + return itemProperties.length > 0 ? itemProperties : undefined } } - return [...merged.values()] + return undefined } + +// Depth first, so an owning class always precedes the children it references. +const flattenClasses = (built: BuiltClass): ResourceClassSchema[] => [ + built.schema, + ...built.nestedClasses.flatMap(flattenClasses), +] diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index 3fe2aa9c..d2c1a118 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -1,508 +1,532 @@ access_code_id ?? null, - code: $json->code ?? null, - common_code_key: $json->common_code_key ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - dormakaba_oracode_metadata: isset($json->dormakaba_oracode_metadata) - ? AccessCodeDormakabaOracodeMetadata::from_json( + public static function from_json(mixed $json): AccessCode|null + { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + code: $json->code ?? null, + common_code_key: $json->common_code_key ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + dormakaba_oracode_metadata: isset( $json->dormakaba_oracode_metadata, ) - : null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => AccessCodeErrors::from_json($e), - $json->errors ?? [], - ), - is_backup: $json->is_backup ?? null, - is_backup_access_code_available: $json->is_backup_access_code_available ?? - null, - is_external_modification_allowed: $json->is_external_modification_allowed ?? - null, - is_managed: $json->is_managed ?? null, - is_offline_access_code: $json->is_offline_access_code ?? null, - is_one_time_use: $json->is_one_time_use ?? null, - is_scheduled_on_device: $json->is_scheduled_on_device ?? null, - is_waiting_for_code_assignment: $json->is_waiting_for_code_assignment ?? - null, - name: $json->name ?? null, - pending_mutations: array_map( - fn($p) => AccessCodePendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - pulled_backup_access_code_id: $json->pulled_backup_access_code_id ?? - null, - starts_at: $json->starts_at ?? null, - status: $json->status ?? null, - type: $json->type ?? null, - warnings: array_map( - fn($w) => AccessCodeWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + ? AccessCode\DormakabaOracodeMetadata::from_json( + $json->dormakaba_oracode_metadata, + ) + : null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => AccessCode\Errors::from_json($e), + $json->errors ?? [], + ), + is_backup: $json->is_backup ?? null, + is_backup_access_code_available: $json->is_backup_access_code_available ?? + null, + is_external_modification_allowed: $json->is_external_modification_allowed ?? + null, + is_managed: $json->is_managed ?? null, + is_offline_access_code: $json->is_offline_access_code ?? null, + is_one_time_use: $json->is_one_time_use ?? null, + is_scheduled_on_device: $json->is_scheduled_on_device ?? null, + is_waiting_for_code_assignment: $json->is_waiting_for_code_assignment ?? + null, + name: $json->name ?? null, + pending_mutations: array_map( + fn($p) => AccessCode\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + pulled_backup_access_code_id: $json->pulled_backup_access_code_id ?? + null, + starts_at: $json->starts_at ?? null, + status: $json->status ?? null, + type: $json->type ?? null, + warnings: array_map( + fn($w) => AccessCode\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Unique identifier for the access code. - */ - public string|null $access_code_id, - /** - * Code used for access. Typically, a numeric or alphanumeric string. - */ - public string|null $code, - /** - * Unique identifier for a group of access codes that share the same code. - */ - public string|null $common_code_key, - /** - * Date and time at which the access code was created. - */ - public string|null $created_at, - /** - * Unique identifier for the device associated with the access code. - */ - public string|null $device_id, - /** - * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. - */ - public AccessCodeDormakabaOracodeMetadata|null $dormakaba_oracode_metadata, - /** - * Date and time after which the time-bound access code becomes inactive. - */ - public string|null $ends_at, - /** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ - public array $errors, - /** - * Indicates whether the access code is a backup code. - */ - public bool|null $is_backup, - /** - * Indicates whether a backup access code is available for use if the primary access code is lost or compromised. - */ - public bool|null $is_backup_access_code_available, - /** - * Indicates whether changes to the access code from external sources are permitted. - */ - public bool|null $is_external_modification_allowed, - /** - * Indicates whether Seam manages the access code. - */ - public bool|null $is_managed, - /** - * Indicates whether the access code is intended for use in offline scenarios. If `true`, this code can be created on a device without a network connection. - */ - public bool|null $is_offline_access_code, - /** - * Indicates whether the access code can only be used once. If `true`, the code becomes invalid after the first use. - */ - public bool|null $is_one_time_use, - /** - * Indicates whether the code is set on the device according to a preconfigured schedule. - */ - public bool|null $is_scheduled_on_device, - /** - * Indicates whether the access code is waiting for a code assignment. - */ - public bool|null $is_waiting_for_code_assignment, - /** - * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). - */ - public string|null $name, - /** - * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. - */ - public array $pending_mutations, - /** - * Identifier of the pulled backup access code. Used to associate the pulled backup access code with the original access code. - */ - public string|null $pulled_backup_access_code_id, - /** - * Date and time at which the time-bound access code becomes active. - */ - public string|null $starts_at, - /** - * Current status of the access code within the operational lifecycle. Values are `setting`, a transitional phase that indicates that the code is being configured or activated; `set`, which indicates that the code is active and operational; `unset`, which indicates a deactivated or unused state, either before activation or after deliberate deactivation; `removing`, which indicates a transitional period in which the code is being deleted or made inactive; and `unknown`, which indicates an indeterminate state, due to reasons such as system errors or incomplete data, that highlights a potential need for system review or troubleshooting. See also [Lifecycle of Access Codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes). - */ - public string|null $status, - /** - * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. - */ - public string|null $type, - /** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the access code. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Unique identifier for the access code. + */ + public string|null $access_code_id, + /** + * Code used for access. Typically, a numeric or alphanumeric string. + */ + public string|null $code, + /** + * Unique identifier for a group of access codes that share the same code. + */ + public string|null $common_code_key, + /** + * Date and time at which the access code was created. + */ + public string|null $created_at, + /** + * Unique identifier for the device associated with the access code. + */ + public string|null $device_id, + /** + * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. + */ + public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + /** + * Date and time after which the time-bound access code becomes inactive. + */ + public string|null $ends_at, + /** + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + public array $errors, + /** + * Indicates whether the access code is a backup code. + */ + public bool|null $is_backup, + /** + * Indicates whether a backup access code is available for use if the primary access code is lost or compromised. + */ + public bool|null $is_backup_access_code_available, + /** + * Indicates whether changes to the access code from external sources are permitted. + */ + public bool|null $is_external_modification_allowed, + /** + * Indicates whether Seam manages the access code. + */ + public bool|null $is_managed, + /** + * Indicates whether the access code is intended for use in offline scenarios. If `true`, this code can be created on a device without a network connection. + */ + public bool|null $is_offline_access_code, + /** + * Indicates whether the access code can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use, + /** + * Indicates whether the code is set on the device according to a preconfigured schedule. + */ + public bool|null $is_scheduled_on_device, + /** + * Indicates whether the access code is waiting for a code assignment. + */ + public bool|null $is_waiting_for_code_assignment, + /** + * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). + */ + public string|null $name, + /** + * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. + */ + public array $pending_mutations, + /** + * Identifier of the pulled backup access code. Used to associate the pulled backup access code with the original access code. + */ + public string|null $pulled_backup_access_code_id, + /** + * Date and time at which the time-bound access code becomes active. + */ + public string|null $starts_at, + /** + * Current status of the access code within the operational lifecycle. Values are `setting`, a transitional phase that indicates that the code is being configured or activated; `set`, which indicates that the code is active and operational; `unset`, which indicates a deactivated or unused state, either before activation or after deliberate deactivation; `removing`, which indicates a transitional period in which the code is being deleted or made inactive; and `unknown`, which indicates an indeterminate state, due to reasons such as system errors or incomplete data, that highlights a potential need for system review or troubleshooting. See also [Lifecycle of Access Codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes). + */ + public string|null $status, + /** + * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. + */ + public string|null $type, + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the access code. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. - */ -class AccessCodeDormakabaOracodeMetadata -{ - public static function from_json( - mixed $json, - ): AccessCodeDormakabaOracodeMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\AccessCode { + /** + * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. + */ + class DormakabaOracodeMetadata + { + public static function from_json( + mixed $json, + ): DormakabaOracodeMetadata|null { + if (!$json) { + return null; + } + return new self( + is_cancellable: $json->is_cancellable ?? null, + is_early_checkin_able: $json->is_early_checkin_able ?? null, + is_extendable: $json->is_extendable ?? null, + is_overridable: $json->is_overridable ?? null, + site_name: $json->site_name ?? null, + stay_id: $json->stay_id ?? null, + user_level_id: $json->user_level_id ?? null, + user_level_name: $json->user_level_name ?? null, + ); } - return new self( - is_cancellable: $json->is_cancellable ?? null, - is_early_checkin_able: $json->is_early_checkin_able ?? null, - is_extendable: $json->is_extendable ?? null, - is_overridable: $json->is_overridable ?? null, - site_name: $json->site_name ?? null, - stay_id: $json->stay_id ?? null, - user_level_id: $json->user_level_id ?? null, - user_level_name: $json->user_level_name ?? null, - ); - } - public function __construct( - /** - * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. - */ - public bool|null $is_cancellable, - /** - * Indicates whether early check-in is available for this stay. - */ - public bool|null $is_early_checkin_able, - /** - * Indicates whether the stay can be extended via the Dormakaba Oracode API. - */ - public bool|null $is_extendable, - /** - * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. - */ - public bool|null $is_overridable, - /** - * Dormakaba Oracode site name associated with this access code. - */ - public string|null $site_name, - /** - * Dormakaba Oracode stay ID associated with this access code. - */ - public float|null $stay_id, - /** - * Dormakaba Oracode user level ID associated with this access code. - */ - public string|null $user_level_id, - /** - * Dormakaba Oracode user level name associated with this access code. - */ - public string|null $user_level_name, - ) {} -} + public function __construct( + /** + * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. + */ + public bool|null $is_cancellable, + /** + * Indicates whether early check-in is available for this stay. + */ + public bool|null $is_early_checkin_able, + /** + * Indicates whether the stay can be extended via the Dormakaba Oracode API. + */ + public bool|null $is_extendable, + /** + * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. + */ + public bool|null $is_overridable, + /** + * Dormakaba Oracode site name associated with this access code. + */ + public string|null $site_name, + /** + * Dormakaba Oracode stay ID associated with this access code. + */ + public float|null $stay_id, + /** + * Dormakaba Oracode user level ID associated with this access code. + */ + public string|null $user_level_id, + /** + * Dormakaba Oracode user level name associated with this access code. + */ + public string|null $user_level_name, + ) {} + } -/** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ -class AccessCodeErrors -{ - public static function from_json(mixed $json): AccessCodeErrors|null + /** + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + managed_access_code_id: $json->managed_access_code_id ?? null, + message: $json->message ?? null, + modified_fields: array_map( + fn($m) => Errors\ModifiedFields::from_json($m), + $json->modified_fields ?? [], + ), + unmanaged_access_code_id: $json->unmanaged_access_code_id ?? + null, + ); } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_access_code_error: $json->is_access_code_error ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - managed_access_code_id: $json->managed_access_code_id ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => AccessCodeModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - unmanaged_access_code_id: $json->unmanaged_access_code_id ?? null, - ); - } - public function __construct( - /** - * 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. - */ - public string|null $change_type, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates that this is an access code error. - */ - public bool|null $is_access_code_error, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. - */ - public bool|null $is_connected_account_error, - /** - * Indicates that the error is not a device error. - */ - public bool|null $is_device_error, - /** - * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $managed_access_code_id, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array $modified_fields, - /** - * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $unmanaged_access_code_id, - ) {} -} + public function __construct( + /** + * 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. + */ + public string|null $change_type, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public bool|null $is_access_code_error, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $managed_access_code_id, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * List of fields that were changed externally, with their previous and new values. + */ + public array $modified_fields, + /** + * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $unmanaged_access_code_id, + ) {} + } -/** - * Previous code configuration. - */ -class AccessCodeFrom -{ - public static function from_json(mixed $json): AccessCodeFrom|null + /** + * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + scheduled_at: $json->scheduled_at ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous PIN code. - */ - public string|null $code, - /** - * Previous end time for the access code. - */ - public string|null $ends_at, - /** - * Previous access code name. - */ - public string|null $name, - /** - * Previous start time for the access code. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + /** + * Date and time at which Seam will attempt to program this access code on the device. + */ + public string|null $scheduled_at, + public PendingMutations\To|null $to, + ) {} + } -/** - * List of fields that were changed externally, with their previous and new values. - */ -class AccessCodeModifiedFields -{ - public static function from_json(mixed $json): AccessCodeModifiedFields|null + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + modified_fields: array_map( + fn($m) => Warnings\ModifiedFields::from_json($m), + $json->modified_fields ?? [], + ), + warning_code: $json->warning_code ?? null, + ); } - return new self( - field: $json->field ?? null, - from: $json->from ?? null, - to: $json->to ?? null, - ); - } - public function __construct( - /** - * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). - */ - public string|null $field, - /** - * The previous value of the field. - */ - public string|null $from, - /** - * The new value of the field. - */ - public string|null $to, - ) {} + public function __construct( + /** + * 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. + */ + public string|null $change_type, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * List of fields that were changed externally, with their previous and new values. + */ + public array $modified_fields, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. - */ -class AccessCodePendingMutations -{ - public static function from_json( - mixed $json, - ): AccessCodePendingMutations|null { - if (!$json) { - return null; +namespace Seam\Resources\AccessCode\Errors { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AccessCodeFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - scheduled_at: $json->scheduled_at ?? null, - to: isset($json->to) ? AccessCodeTo::from_json($json->to) : null, - ); - } - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous code configuration. - */ - public AccessCodeFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of setting an access code on the device. - */ - public string|null $mutation_code, - /** - * Date and time at which Seam will attempt to program this access code on the device. - */ - public string|null $scheduled_at, - /** - * New code configuration. - */ - public AccessCodeTo|null $to, - ) {} + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } } -/** - * New code configuration. - */ -class AccessCodeTo -{ - public static function from_json(mixed $json): AccessCodeTo|null +namespace Seam\Resources\AccessCode\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); + + public function __construct( + /** + * Previous PIN code. + */ + public string|null $code, + /** + * Previous end time for the access code. + */ + public string|null $ends_at, + /** + * Previous access code name. + */ + public string|null $name, + /** + * Previous start time for the access code. + */ + public string|null $starts_at, + ) {} } - public function __construct( - /** - * New PIN code. - */ - public string|null $code, - /** - * New end time for the access code. - */ - public string|null $ends_at, - /** - * New access code name. - */ - public string|null $name, - /** - * New start time for the access code. - */ - public string|null $starts_at, - ) {} + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * New PIN code. + */ + public string|null $code, + /** + * New end time for the access code. + */ + public string|null $ends_at, + /** + * New access code name. + */ + public string|null $name, + /** + * New start time for the access code. + */ + public string|null $starts_at, + ) {} + } } -/** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ -class AccessCodeWarnings -{ - public static function from_json(mixed $json): AccessCodeWarnings|null +namespace Seam\Resources\AccessCode\Warnings { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields { - if (!$json) { - return null; + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => AccessCodeModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * 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. - */ - public string|null $change_type, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array $modified_fields, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } } diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index a4ff5cca..6df3876a 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -1,441 +1,433 @@ access_grant_id ?? null, + access_grant_key: $json->access_grant_key ?? null, + access_method_ids: $json->access_method_ids ?? null, + client_session_token: $json->client_session_token ?? null, + created_at: $json->created_at ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => AccessGrant\Errors::from_json($e), + $json->errors ?? [], + ), + instant_key_url: $json->instant_key_url ?? null, + location_ids: $json->location_ids ?? null, + name: $json->name ?? null, + pending_mutations: array_map( + fn($p) => AccessGrant\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + requested_access_methods: array_map( + fn($r) => AccessGrant\RequestedAccessMethods::from_json($r), + $json->requested_access_methods ?? [], + ), + reservation_key: $json->reservation_key ?? null, + space_ids: $json->space_ids ?? null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + warnings: array_map( + fn($w) => AccessGrant\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_grant_id: $json->access_grant_id ?? null, - access_grant_key: $json->access_grant_key ?? null, - access_method_ids: $json->access_method_ids ?? null, - client_session_token: $json->client_session_token ?? null, - created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => AccessGrantErrors::from_json($e), - $json->errors ?? [], - ), - instant_key_url: $json->instant_key_url ?? null, - location_ids: $json->location_ids ?? null, - name: $json->name ?? null, - pending_mutations: array_map( - fn($p) => AccessGrantPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - requested_access_methods: array_map( - fn($r) => AccessGrantRequestedAccessMethods::from_json($r), - $json->requested_access_methods ?? [], - ), - reservation_key: $json->reservation_key ?? null, - space_ids: $json->space_ids ?? null, - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - warnings: array_map( - fn($w) => AccessGrantWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the Access Grant. - */ - public string|null $access_grant_id, - /** - * Unique key for the access grant within the workspace. - */ - public string|null $access_grant_key, - /** - * IDs of the access methods created for the Access Grant. - */ - public array|null $access_method_ids, - /** - * Client Session Token. Only returned if the Access Grant has a mobile_key access method. - */ - public string|null $client_session_token, - /** - * Date and time at which the Access Grant was created. - */ - public string|null $created_at, - /** - * ID of the customization profile associated with the Access Grant. - */ - public string|null $customization_profile_id, - /** - * Display name of the Access Grant. - */ - public string|null $display_name, - /** - * Date and time at which the Access Grant ends. - */ - public string|null $ends_at, - /** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ - public array $errors, - /** - * Instant Key URL. Only returned if the Access Grant has a single mobile_key access_method. - */ - public string|null $instant_key_url, - /** - * @deprecated Use `space_ids`. - */ - public array|null $location_ids, - /** - * Name of the Access Grant. If not provided, the display name will be computed. - */ - public string|null $name, - /** - * List of pending mutations for the access grant. This shows updates that are in progress. - */ - public array $pending_mutations, - /** - * Access methods that the user requested for the Access Grant. - */ - public array $requested_access_methods, - /** - * Reservation key for the access grant. - */ - public string|null $reservation_key, - /** - * IDs of the spaces to which the Access Grant gives access. - */ - public array|null $space_ids, - /** - * Date and time at which the Access Grant starts. - */ - public string|null $starts_at, - /** - * ID of user identity to which the Access Grant gives access. - */ - public string|null $user_identity_id, - /** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ - public array $warnings, - /** - * ID of the Seam workspace associated with the Access Grant. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the Access Grant. + */ + public string|null $access_grant_id, + /** + * Unique key for the access grant within the workspace. + */ + public string|null $access_grant_key, + /** + * IDs of the access methods created for the Access Grant. + */ + public array|null $access_method_ids, + /** + * Client Session Token. Only returned if the Access Grant has a mobile_key access method. + */ + public string|null $client_session_token, + /** + * Date and time at which the Access Grant was created. + */ + public string|null $created_at, + /** + * ID of the customization profile associated with the Access Grant. + */ + public string|null $customization_profile_id, + /** + * Display name of the Access Grant. + */ + public string|null $display_name, + /** + * Date and time at which the Access Grant ends. + */ + public string|null $ends_at, + /** + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + public array $errors, + /** + * Instant Key URL. Only returned if the Access Grant has a single mobile_key access_method. + */ + public string|null $instant_key_url, + /** + * @deprecated Use `space_ids`. + */ + public array|null $location_ids, + /** + * Name of the Access Grant. If not provided, the display name will be computed. + */ + public string|null $name, + /** + * List of pending mutations for the access grant. This shows updates that are in progress. + */ + public array $pending_mutations, + /** + * Access methods that the user requested for the Access Grant. + */ + public array $requested_access_methods, + /** + * Reservation key for the access grant. + */ + public string|null $reservation_key, + /** + * IDs of the spaces to which the Access Grant gives access. + */ + public array|null $space_ids, + /** + * Date and time at which the Access Grant starts. + */ + public string|null $starts_at, + /** + * ID of user identity to which the Access Grant gives access. + */ + public string|null $user_identity_id, + /** + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + public array $warnings, + /** + * ID of the Seam workspace associated with the Access Grant. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ -class AccessGrantErrors -{ - public static function from_json(mixed $json): AccessGrantErrors|null +namespace Seam\Resources\AccessGrant { + /** + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. - */ - public array|null $missing_device_ids, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. + */ + public array|null $missing_device_ids, + ) {} + } -/** - * 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). - */ -class AccessGrantFailedDevices -{ - public static function from_json(mixed $json): AccessGrantFailedDevices|null + /** + * List of pending mutations for the access grant. This shows updates that are in progress. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - device_id: $json->device_id ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Device whose access code could not be revoked. - */ - public string|null $device_id, - /** - * Reason the access code could not be revoked (e.g. `offline_access_code_not_revocable`). - */ - public string|null $error_code, - /** - * Human-readable description of why revocation failed. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * IDs of the access methods being updated. + */ + public array|null $access_method_ids, + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + ) {} + } -/** - * Previous location configuration. - */ -class AccessGrantFrom -{ - public static function from_json(mixed $json): AccessGrantFrom|null + /** + * Access methods that the user requested for the Access Grant. + */ + class RequestedAccessMethods { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): RequestedAccessMethods|null { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + created_access_method_ids: $json->created_access_method_ids ?? + null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + instant_key_max_use_count: $json->instant_key_max_use_count ?? + null, + mode: $json->mode ?? null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous device IDs where access codes existed. - */ - public array|null $device_ids, - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Specific PIN code to use for this access method. Only applicable when mode is 'code'. + */ + public string|null $code, + /** + * IDs of the access methods created for the requested access method. + */ + public array|null $created_access_method_ids, + /** + * Date and time at which the requested access method was added to the Access Grant. + */ + public string|null $created_at, + /** + * Display name of the access method. + */ + public string|null $display_name, + /** + * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. + */ + public int|null $instant_key_max_use_count, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + ) {} + } -/** - * List of pending mutations for the access grant. This shows updates that are in progress. - */ -class AccessGrantPendingMutations -{ - public static function from_json( - mixed $json, - ): AccessGrantPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + failed_devices: array_map( + fn($f) => Warnings\FailedDevices::from_json($f), + $json->failed_devices ?? [], + ), + message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, + reason: $json->reason ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AccessGrantFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) ? AccessGrantTo::from_json($json->to) : null, - ); - } - public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous location configuration. - */ - public AccessGrantFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. - */ - public string|null $mutation_code, - /** - * New location configuration. - */ - public AccessGrantTo|null $to, - ) {} + public function __construct( + /** + * IDs of the access methods being updated. + */ + public array|null $access_method_ids, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + public string|null $device_id, + /** + * 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). + */ + public array $failed_devices, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * The new PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that was unavailable. + */ + public string|null $original_code, + /** + * Specific reason why the grant's times are not programmable on the device. + */ + public string|null $reason, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Access methods that the user requested for the Access Grant. - */ -class AccessGrantRequestedAccessMethods -{ - public static function from_json( - mixed $json, - ): AccessGrantRequestedAccessMethods|null { - if (!$json) { - return null; +namespace Seam\Resources\AccessGrant\PendingMutations { + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - code: $json->code ?? null, - created_access_method_ids: $json->created_access_method_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - instant_key_max_use_count: $json->instant_key_max_use_count ?? null, - mode: $json->mode ?? null, - ); - } - public function __construct( - /** - * Specific PIN code to use for this access method. Only applicable when mode is 'code'. - */ - public string|null $code, - /** - * IDs of the access methods created for the requested access method. - */ - public array|null $created_access_method_ids, - /** - * Date and time at which the requested access method was added to the Access Grant. - */ - public string|null $created_at, - /** - * Display name of the access method. - */ - public string|null $display_name, - /** - * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. - */ - public int|null $instant_key_max_use_count, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - ) {} -} + public function __construct( + /** + * Previous device IDs where access codes existed. + */ + public array|null $device_ids, + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} + } -/** - * New location configuration. - */ -class AccessGrantTo -{ - public static function from_json(mixed $json): AccessGrantTo|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + common_code_key: $json->common_code_key ?? null, + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - common_code_key: $json->common_code_key ?? null, - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Common code key to ensure PIN code reuse across devices. - */ - public string|null $common_code_key, - /** - * New device IDs where access codes should be created. - */ - public array|null $device_ids, - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} + public function __construct( + /** + * Common code key to ensure PIN code reuse across devices. + */ + public string|null $common_code_key, + /** + * New device IDs where access codes should be created. + */ + public array|null $device_ids, + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } -/** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ -class AccessGrantWarnings -{ - public static function from_json(mixed $json): AccessGrantWarnings|null +namespace Seam\Resources\AccessGrant\Warnings { + /** + * 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). + */ + class FailedDevices { - if (!$json) { - return null; + public static function from_json(mixed $json): FailedDevices|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - failed_devices: array_map( - fn($f) => AccessGrantFailedDevices::from_json($f), - $json->failed_devices ?? [], - ), - message: $json->message ?? null, - new_code: $json->new_code ?? null, - original_code: $json->original_code ?? null, - reason: $json->reason ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * ID of the device where the requested code was unavailable. - */ - public string|null $device_id, - /** - * 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). - */ - public array $failed_devices, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * The new PIN code that was assigned instead. - */ - public string|null $new_code, - /** - * The originally requested PIN code that was unavailable. - */ - public string|null $original_code, - /** - * Specific reason why the grant's times are not programmable on the device. - */ - public string|null $reason, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Device whose access code could not be revoked. + */ + public string|null $device_id, + /** + * Reason the access code could not be revoked (e.g. `offline_access_code_not_revocable`). + */ + public string|null $error_code, + /** + * Human-readable description of why revocation failed. + */ + public string|null $message, + ) {} + } } diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index c4be2ae1..7745b2f6 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -1,303 +1,289 @@ access_method_id ?? null, + client_session_token: $json->client_session_token ?? null, + code: $json->code ?? null, + created_at: $json->created_at ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => AccessMethod\Errors::from_json($e), + $json->errors ?? [], + ), + instant_key_url: $json->instant_key_url ?? null, + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_issued: $json->is_issued ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, + issued_at: $json->issued_at ?? null, + mode: $json->mode ?? null, + pending_mutations: array_map( + fn($p) => AccessMethod\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + warnings: array_map( + fn($w) => AccessMethod\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_method_id: $json->access_method_id ?? null, - client_session_token: $json->client_session_token ?? null, - code: $json->code ?? null, - created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => AccessMethodErrors::from_json($e), - $json->errors ?? [], - ), - instant_key_url: $json->instant_key_url ?? null, - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, - is_issued: $json->is_issued ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, - issued_at: $json->issued_at ?? null, - mode: $json->mode ?? null, - pending_mutations: array_map( - fn($p) => AccessMethodPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - warnings: array_map( - fn($w) => AccessMethodWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the access method. - */ - public string|null $access_method_id, - /** - * Token of the client session associated with the access method. - */ - public string|null $client_session_token, - /** - * The actual PIN code for code access methods. - */ - public string|null $code, - /** - * Date and time at which the access method was created. - */ - public string|null $created_at, - /** - * ID of the customization profile associated with the access method. - */ - public string|null $customization_profile_id, - /** - * Display name of the access method. - */ - public string|null $display_name, - /** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ - public array $errors, - /** - * URL of the Instant Key for mobile key access methods. - */ - public string|null $instant_key_url, - /** - * 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. - */ - public bool|null $is_assignment_required, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. - */ - public bool|null $is_encoding_required, - /** - * Indicates whether the access method has been issued. - */ - public bool|null $is_issued, - /** - * 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. - */ - public bool|null $is_ready_for_assignment, - /** - * 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. - */ - public bool|null $is_ready_for_encoding, - /** - * Date and time at which the access method was issued. - */ - public string|null $issued_at, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - public array $pending_mutations, - /** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ - public array $warnings, - /** - * ID of the Seam workspace associated with the access method. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the access method. + */ + public string|null $access_method_id, + /** + * Token of the client session associated with the access method. + */ + public string|null $client_session_token, + /** + * The actual PIN code for code access methods. + */ + public string|null $code, + /** + * Date and time at which the access method was created. + */ + public string|null $created_at, + /** + * ID of the customization profile associated with the access method. + */ + public string|null $customization_profile_id, + /** + * Display name of the access method. + */ + public string|null $display_name, + /** + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + public array $errors, + /** + * URL of the Instant Key for mobile key access methods. + */ + public string|null $instant_key_url, + /** + * 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. + */ + public bool|null $is_assignment_required, + /** + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + */ + public bool|null $is_encoding_required, + /** + * Indicates whether the access method has been issued. + */ + public bool|null $is_issued, + /** + * 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. + */ + public bool|null $is_ready_for_assignment, + /** + * 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. + */ + public bool|null $is_ready_for_encoding, + /** + * Date and time at which the access method was issued. + */ + public string|null $issued_at, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + public array $pending_mutations, + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + public array $warnings, + /** + * ID of the Seam workspace associated with the access method. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ -class AccessMethodErrors -{ - public static function from_json(mixed $json): AccessMethodErrors|null +namespace Seam\Resources\AccessMethod { + /** + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Previous device configuration. - */ -class AccessMethodFrom -{ - public static function from_json(mixed $json): AccessMethodFrom|null + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous device IDs where access was provisioned. - */ - public array|null $device_ids, - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + ) {} + } -/** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ -class AccessMethodPendingMutations -{ - public static function from_json( - mixed $json, - ): AccessMethodPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AccessMethodFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) ? AccessMethodTo::from_json($json->to) : null, - ); - } - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous device configuration. - */ - public AccessMethodFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. - */ - public string|null $mutation_code, - /** - * New device configuration. - */ - public AccessMethodTo|null $to, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * New device configuration. - */ -class AccessMethodTo -{ - public static function from_json(mixed $json): AccessMethodTo|null +namespace Seam\Resources\AccessMethod\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New device IDs where access is being provisioned. - */ - public array|null $device_ids, - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + public array|null $device_ids, + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ -class AccessMethodWarnings -{ - public static function from_json(mixed $json): AccessMethodWarnings|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - original_access_method_id: $json->original_access_method_id ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + public array|null $device_ids, + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index 9d8d7bc4..b51f042d 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -1,359 +1,346 @@ access_group_type ?? null, + access_group_type_display_name: $json->access_group_type_display_name ?? + null, + access_schedule: isset($json->access_schedule) + ? AcsAccessGroup\AccessSchedule::from_json( + $json->access_schedule, + ) + : null, + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => AcsAccessGroup\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + is_managed: $json->is_managed ?? null, + name: $json->name ?? null, + pending_mutations: array_map( + fn($p) => AcsAccessGroup\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + warnings: array_map( + fn($w) => AcsAccessGroup\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_group_type: $json->access_group_type ?? null, - access_group_type_display_name: $json->access_group_type_display_name ?? - null, - access_schedule: isset($json->access_schedule) - ? AcsAccessGroupAccessSchedule::from_json( - $json->access_schedule, - ) - : null, - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => AcsAccessGroupErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - is_managed: $json->is_managed ?? null, - name: $json->name ?? null, - pending_mutations: array_map( - fn($p) => AcsAccessGroupPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - warnings: array_map( - fn($w) => AcsAccessGroupWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * @deprecated Use `external_type`. - */ - public string|null $access_group_type, - /** - * @deprecated Use `external_type_display_name`. - */ - public string|null $access_group_type_display_name, - /** - * `starts_at` and `ends_at` timestamps for the access group's access. - */ - public AcsAccessGroupAccessSchedule|null $access_schedule, - /** - * ID of the access group. - */ - public string|null $acs_access_group_id, - /** - * ID of the access control system that contains the access group. - */ - public string|null $acs_system_id, - /** - * ID of the connected account that contains the access group. - */ - public string|null $connected_account_id, - /** - * Date and time at which the access group was created. - */ - public string|null $created_at, - /** - * Display name for the access group. - */ - public string|null $display_name, - /** - * Errors associated with the `acs_access_group`. - */ - public array $errors, - /** - * Brand-specific terminology for the access group type. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the access group type. - */ - public string|null $external_type_display_name, - /** - * Indicates whether Seam manages the access group. - */ - public bool|null $is_managed, - /** - * Name of the access group. - */ - public string|null $name, - /** - * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. - */ - public array $pending_mutations, - /** - * Warnings associated with the `acs_access_group`. - */ - public array $warnings, - /** - * ID of the workspace that contains the access group. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * @deprecated Use `external_type`. + */ + public string|null $access_group_type, + /** + * @deprecated Use `external_type_display_name`. + */ + public string|null $access_group_type_display_name, + /** + * `starts_at` and `ends_at` timestamps for the access group's access. + */ + public AcsAccessGroup\AccessSchedule|null $access_schedule, + /** + * ID of the access group. + */ + public string|null $acs_access_group_id, + /** + * ID of the access control system that contains the access group. + */ + public string|null $acs_system_id, + /** + * ID of the connected account that contains the access group. + */ + public string|null $connected_account_id, + /** + * Date and time at which the access group was created. + */ + public string|null $created_at, + /** + * Display name for the access group. + */ + public string|null $display_name, + /** + * Errors associated with the `acs_access_group`. + */ + public array $errors, + /** + * Brand-specific terminology for the access group type. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the access group type. + */ + public string|null $external_type_display_name, + /** + * Indicates whether Seam manages the access group. + */ + public bool|null $is_managed, + /** + * Name of the access group. + */ + public string|null $name, + /** + * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. + */ + public array $pending_mutations, + /** + * Warnings associated with the `acs_access_group`. + */ + public array $warnings, + /** + * ID of the workspace that contains the access group. + */ + public string|null $workspace_id, + ) {} + } } -/** - * `starts_at` and `ends_at` timestamps for the access group's access. - */ -class AcsAccessGroupAccessSchedule -{ - public static function from_json( - mixed $json, - ): AcsAccessGroupAccessSchedule|null { - if (!$json) { - return null; +namespace Seam\Resources\AcsAccessGroup { + /** + * `starts_at` and `ends_at` timestamps for the access group's access. + */ + class AccessSchedule + { + public static function from_json(mixed $json): AccessSchedule|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Date and time at which the user's access ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Date and time at which the user's access starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the user's access ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Date and time at which the user's access starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + ) {} + } -/** - * Errors associated with the `acs_access_group`. - */ -class AcsAccessGroupErrors -{ - public static function from_json(mixed $json): AcsAccessGroupErrors|null + /** + * Errors associated with the `acs_access_group`. + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Old access group information. - */ -class AcsAccessGroupFrom -{ - public static function from_json(mixed $json): AcsAccessGroupFrom|null + /** + * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + variant: $json->variant ?? null, + ); } - return new self( - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Old entrance ID. - */ - public string|null $acs_entrance_id, - /** - * Old user ID. - */ - public string|null $acs_user_id, - /** - * Ending time for the access schedule. - */ - public string|null $ends_at, - /** - * Name of the access group. - */ - public string|null $name, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * ID of the user involved in the scheduled change. + */ + public string|null $acs_user_id, + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + /** + * Whether the user is scheduled to be added to or removed from this access group. + */ + public string|null $variant, + ) {} + } -/** - * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. - */ -class AcsAccessGroupPendingMutations -{ - public static function from_json( - mixed $json, - ): AcsAccessGroupPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the `acs_access_group`. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AcsAccessGroupFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? AcsAccessGroupTo::from_json($json->to) - : null, - variant: $json->variant ?? null, - ); - } - public function __construct( - /** - * ID of the user involved in the scheduled change. - */ - public string|null $acs_user_id, - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Old access group information. - */ - public AcsAccessGroupFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. - */ - public string|null $mutation_code, - /** - * New access group information. - */ - public AcsAccessGroupTo|null $to, - /** - * Whether the user is scheduled to be added to or removed from this access group. - */ - public string|null $variant, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * New access group information. - */ -class AcsAccessGroupTo -{ - public static function from_json(mixed $json): AcsAccessGroupTo|null +namespace Seam\Resources\AcsAccessGroup\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New entrance ID. - */ - public string|null $acs_entrance_id, - /** - * New user ID. - */ - public string|null $acs_user_id, - /** - * Ending time for the access schedule. - */ - public string|null $ends_at, - /** - * Name of the access group. - */ - public string|null $name, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Old entrance ID. + */ + public string|null $acs_entrance_id, + /** + * Old user ID. + */ + public string|null $acs_user_id, + /** + * Ending time for the access schedule. + */ + public string|null $ends_at, + /** + * Name of the access group. + */ + public string|null $name, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the `acs_access_group`. - */ -class AcsAccessGroupWarnings -{ - public static function from_json(mixed $json): AcsAccessGroupWarnings|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * New entrance ID. + */ + public string|null $acs_entrance_id, + /** + * New user ID. + */ + public string|null $acs_user_id, + /** + * Ending time for the access schedule. + */ + public string|null $ends_at, + /** + * Name of the access group. + */ + public string|null $name, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index 6ec383e7..f62fceda 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -1,383 +1,389 @@ access_method ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - akiles_metadata: isset($json->akiles_metadata) - ? AcsCredentialAkilesMetadata::from_json($json->akiles_metadata) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? AcsCredentialAssaAbloyVostioMetadata::from_json( + public static function from_json(mixed $json): AcsCredential|null + { + if (!$json) { + return null; + } + return new self( + access_method: $json->access_method ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? AcsCredential\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - : null, - card_number: $json->card_number ?? null, - code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => AcsCredentialErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - is_issued: $json->is_issued ?? null, - is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? - null, - is_managed: $json->is_managed ?? null, - is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? - null, - is_one_time_use: $json->is_one_time_use ?? null, - issued_at: $json->issued_at ?? null, - latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? - null, - parent_acs_credential_id: $json->parent_acs_credential_id ?? null, - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? AcsCredentialVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - warnings: array_map( - fn($w) => AcsCredentialWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + ? AcsCredential\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + card_number: $json->card_number ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => AcsCredential\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + is_issued: $json->is_issued ?? null, + is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? + null, + is_managed: $json->is_managed ?? null, + is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? + null, + is_one_time_use: $json->is_one_time_use ?? null, + issued_at: $json->issued_at ?? null, + latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? + null, + parent_acs_credential_id: $json->parent_acs_credential_id ?? + null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsCredential\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => AcsCredential\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $access_method, - /** - * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_credential_id, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_system_id, - /** - * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $acs_user_id, - /** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public AcsCredentialAkilesMetadata|null $akiles_metadata, - /** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public AcsCredentialAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $code, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $display_name, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. - */ - public string|null $ends_at, - /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $errors, - /** - * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $external_type_display_name, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. - */ - public bool|null $is_issued, - /** - * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. - */ - public bool|null $is_latest_desired_state_synced_with_provider, - /** - * Indicates whether Seam manages the credential. - */ - public bool|null $is_managed, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). - */ - public bool|null $is_multi_phone_sync_credential, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. - */ - public bool|null $is_one_time_use, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. - */ - public string|null $issued_at, - /** - * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. - */ - public string|null $latest_desired_state_synced_with_provider_at, - /** - * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $parent_acs_credential_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $user_identity_id, - /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public AcsCredentialVisionlineMetadata|null $visionline_metadata, - /** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $warnings, - /** - * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $access_method, + /** + * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_credential_id, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_system_id, + /** + * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $acs_user_id, + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredential\AkilesMetadata|null $akiles_metadata, + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredential\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $code, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $display_name, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + */ + public string|null $ends_at, + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $errors, + /** + * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $external_type_display_name, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. + */ + public bool|null $is_issued, + /** + * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. + */ + public bool|null $is_latest_desired_state_synced_with_provider, + /** + * Indicates whether Seam manages the credential. + */ + public bool|null $is_managed, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). + */ + public bool|null $is_multi_phone_sync_credential, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. + */ + public string|null $issued_at, + /** + * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. + */ + public string|null $latest_desired_state_synced_with_provider_at, + /** + * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $parent_acs_credential_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $user_identity_id, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredential\VisionlineMetadata|null $visionline_metadata, + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $warnings, + /** + * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $workspace_id, + ) {} + } } -/** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialAkilesMetadata -{ - public static function from_json( - mixed $json, - ): AcsCredentialAkilesMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\AcsCredential { + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AkilesMetadata + { + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self(member_pin_id: $json->member_pin_id ?? null); } - return new self(member_pin_id: $json->member_pin_id ?? null); - } - public function __construct( - /** - * ID of the Akiles member PIN. - */ - public string|null $member_pin_id, - ) {} -} + public function __construct( + /** + * ID of the Akiles member PIN. + */ + public string|null $member_pin_id, + ) {} + } -/** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialAssaAbloyVostioMetadata -{ - public static function from_json( - mixed $json, - ): AcsCredentialAssaAbloyVostioMetadata|null { - if (!$json) { - return null; + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AssaAbloyVostioMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + door_names: $json->door_names ?? null, + endpoint_id: $json->endpoint_id ?? null, + key_id: $json->key_id ?? null, + key_issuing_request_id: $json->key_issuing_request_id ?? null, + override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? + null, + ); } - return new self( - auto_join: $json->auto_join ?? null, - door_names: $json->door_names ?? null, - endpoint_id: $json->endpoint_id ?? null, - key_id: $json->key_id ?? null, - key_issuing_request_id: $json->key_issuing_request_id ?? null, - override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? - null, - ); - } - public function __construct( - /** - * 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. - */ - public bool|null $auto_join, - /** - * Names of the doors to which to grant access in the Vostio access system. - */ - public array|null $door_names, - /** - * Endpoint ID in the Vostio access system. - */ - public string|null $endpoint_id, - /** - * Key ID in the Vostio access system. - */ - public string|null $key_id, - /** - * Key issuing request ID in the Vostio access system. - */ - public string|null $key_issuing_request_id, - /** - * IDs of the guest entrances to override in the Vostio access system. - */ - public array|null $override_guest_acs_entrance_ids, - ) {} -} + public function __construct( + /** + * 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. + */ + public bool|null $auto_join, + /** + * Names of the doors to which to grant access in the Vostio access system. + */ + public array|null $door_names, + /** + * Endpoint ID in the Vostio access system. + */ + public string|null $endpoint_id, + /** + * Key ID in the Vostio access system. + */ + public string|null $key_id, + /** + * Key issuing request ID in the Vostio access system. + */ + public string|null $key_issuing_request_id, + /** + * IDs of the guest entrances to override in the Vostio access system. + */ + public array|null $override_guest_acs_entrance_ids, + ) {} + } -/** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialErrors -{ - public static function from_json(mixed $json): AcsCredentialErrors|null + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - public string|null $error_code, - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + public string|null $error_code, + public string|null $message, + ) {} + } -/** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialVisionlineMetadata -{ - public static function from_json( - mixed $json, - ): AcsCredentialVisionlineMetadata|null { - if (!$json) { - return null; + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + card_function_type: $json->card_function_type ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + credential_id: $json->credential_id ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + is_valid: $json->is_valid ?? null, + joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? + null, + ); } - return new self( - auto_join: $json->auto_join ?? null, - card_function_type: $json->card_function_type ?? null, - card_id: $json->card_id ?? null, - common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, - credential_id: $json->credential_id ?? null, - guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, - is_valid: $json->is_valid ?? null, - joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? null, - ); - } - public function __construct( - /** - * 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. - */ - public bool|null $auto_join, - /** - * Card function type in the Visionline access system. - */ - public string|null $card_function_type, - /** - * ID of the card in the Visionline access system. - */ - public string|null $card_id, - /** - * Common entrance IDs in the Visionline access system. - */ - public array|null $common_acs_entrance_ids, - /** - * ID of the credential in the Visionline access system. - */ - public string|null $credential_id, - /** - * Guest entrance IDs in the Visionline access system. - */ - public array|null $guest_acs_entrance_ids, - /** - * Indicates whether the credential is valid. - */ - public bool|null $is_valid, - /** - * IDs of the credentials to which you want to join. - */ - public array|null $joiner_acs_credential_ids, - ) {} -} + public function __construct( + /** + * 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. + */ + public bool|null $auto_join, + /** + * Card function type in the Visionline access system. + */ + public string|null $card_function_type, + /** + * ID of the card in the Visionline access system. + */ + public string|null $card_id, + /** + * Common entrance IDs in the Visionline access system. + */ + public array|null $common_acs_entrance_ids, + /** + * ID of the credential in the Visionline access system. + */ + public string|null $credential_id, + /** + * Guest entrance IDs in the Visionline access system. + */ + public array|null $guest_acs_entrance_ids, + /** + * Indicates whether the credential is valid. + */ + public bool|null $is_valid, + /** + * IDs of the credentials to which you want to join. + */ + public array|null $joiner_acs_credential_ids, + ) {} + } -/** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialWarnings -{ - public static function from_json(mixed $json): AcsCredentialWarnings|null + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } diff --git a/src/Resources/AcsEncoder.php b/src/Resources/AcsEncoder.php index 2375f65d..fdb6ee77 100644 --- a/src/Resources/AcsEncoder.php +++ b/src/Resources/AcsEncoder.php @@ -1,105 +1,107 @@ acs_encoder_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => AcsEncoder\Errors::from_json($e), + $json->errors ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_encoder_id: $json->acs_encoder_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => AcsEncoderErrors::from_json($e), - $json->errors ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $acs_encoder_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $acs_system_id, - /** - * ID of the connected account that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $connected_account_id, - /** - * Date and time at which the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners) was created. - */ - public string|null $created_at, - /** - * Display name for the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $display_name, - /** - * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public array $errors, - /** - * ID of the workspace that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $acs_encoder_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $acs_system_id, + /** + * ID of the connected account that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $connected_account_id, + /** + * Date and time at which the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners) was created. + */ + public string|null $created_at, + /** + * Display name for the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $display_name, + /** + * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public array $errors, + /** + * ID of the workspace that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ -class AcsEncoderErrors -{ - public static function from_json(mixed $json): AcsEncoderErrors|null +namespace Seam\Resources\AcsEncoder { + /** + * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } } diff --git a/src/Resources/AcsEntrance.php b/src/Resources/AcsEntrance.php index 870e7b6a..59fdad0d 100644 --- a/src/Resources/AcsEntrance.php +++ b/src/Resources/AcsEntrance.php @@ -1,751 +1,767 @@ acs_entrance_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - akiles_metadata: isset($json->akiles_metadata) - ? AcsEntranceAkilesMetadata::from_json($json->akiles_metadata) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? AcsEntranceAssaAbloyVostioMetadata::from_json( + public static function from_json(mixed $json): AcsEntrance|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? AcsEntrance\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - : null, - avigilon_alta_metadata: isset($json->avigilon_alta_metadata) - ? AcsEntranceAvigilonAltaMetadata::from_json( - $json->avigilon_alta_metadata, - ) - : null, - brivo_metadata: isset($json->brivo_metadata) - ? AcsEntranceBrivoMetadata::from_json($json->brivo_metadata) - : null, - can_belong_to_reservation: $json->can_belong_to_reservation ?? null, - can_unlock_with_card: $json->can_unlock_with_card ?? null, - can_unlock_with_cloud_key: $json->can_unlock_with_cloud_key ?? null, - can_unlock_with_code: $json->can_unlock_with_code ?? null, - can_unlock_with_mobile_key: $json->can_unlock_with_mobile_key ?? - null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - dormakaba_ambiance_metadata: isset( - $json->dormakaba_ambiance_metadata, - ) - ? AcsEntranceDormakabaAmbianceMetadata::from_json( + ? AcsEntrance\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + avigilon_alta_metadata: isset($json->avigilon_alta_metadata) + ? AcsEntrance\AvigilonAltaMetadata::from_json( + $json->avigilon_alta_metadata, + ) + : null, + brivo_metadata: isset($json->brivo_metadata) + ? AcsEntrance\BrivoMetadata::from_json( + $json->brivo_metadata, + ) + : null, + can_belong_to_reservation: $json->can_belong_to_reservation ?? + null, + can_unlock_with_card: $json->can_unlock_with_card ?? null, + can_unlock_with_cloud_key: $json->can_unlock_with_cloud_key ?? + null, + can_unlock_with_code: $json->can_unlock_with_code ?? null, + can_unlock_with_mobile_key: $json->can_unlock_with_mobile_key ?? + null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + dormakaba_ambiance_metadata: isset( $json->dormakaba_ambiance_metadata, ) - : null, - dormakaba_community_metadata: isset( - $json->dormakaba_community_metadata, - ) - ? AcsEntranceDormakabaCommunityMetadata::from_json( + ? AcsEntrance\DormakabaAmbianceMetadata::from_json( + $json->dormakaba_ambiance_metadata, + ) + : null, + dormakaba_community_metadata: isset( $json->dormakaba_community_metadata, ) - : null, - errors: array_map( - fn($e) => AcsEntranceErrors::from_json($e), - $json->errors ?? [], - ), - hotek_metadata: isset($json->hotek_metadata) - ? AcsEntranceHotekMetadata::from_json($json->hotek_metadata) - : null, - is_locked: $json->is_locked ?? null, - latch_metadata: isset($json->latch_metadata) - ? AcsEntranceLatchMetadata::from_json($json->latch_metadata) - : null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? AcsEntranceSaltoKsMetadata::from_json( - $json->salto_ks_metadata, - ) - : null, - salto_space_metadata: isset($json->salto_space_metadata) - ? AcsEntranceSaltoSpaceMetadata::from_json( - $json->salto_space_metadata, - ) - : null, - space_ids: $json->space_ids ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? AcsEntranceVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - warnings: array_map( - fn($w) => AcsEntranceWarnings::from_json($w), - $json->warnings ?? [], - ), - ); - } + ? AcsEntrance\DormakabaCommunityMetadata::from_json( + $json->dormakaba_community_metadata, + ) + : null, + errors: array_map( + fn($e) => AcsEntrance\Errors::from_json($e), + $json->errors ?? [], + ), + hotek_metadata: isset($json->hotek_metadata) + ? AcsEntrance\HotekMetadata::from_json( + $json->hotek_metadata, + ) + : null, + is_locked: $json->is_locked ?? null, + latch_metadata: isset($json->latch_metadata) + ? AcsEntrance\LatchMetadata::from_json( + $json->latch_metadata, + ) + : null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? AcsEntrance\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + salto_space_metadata: isset($json->salto_space_metadata) + ? AcsEntrance\SaltoSpaceMetadata::from_json( + $json->salto_space_metadata, + ) + : null, + space_ids: $json->space_ids ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsEntrance\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => AcsEntrance\Warnings::from_json($w), + $json->warnings ?? [], + ), + ); + } - public function __construct( - /** - * ID of the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $acs_entrance_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $acs_system_id, - /** - * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceAkilesMetadata|null $akiles_metadata, - /** - * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceAvigilonAltaMetadata|null $avigilon_alta_metadata, - /** - * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceBrivoMetadata|null $brivo_metadata, - /** - * Indicates whether the ACS entrance can belong to a reservation via an access_grant.reservation_key. - */ - public bool|null $can_belong_to_reservation, - /** - * Indicates whether the ACS entrance can be unlocked with card credentials. - */ - public bool|null $can_unlock_with_card, - /** - * Indicates whether the ACS entrance can be unlocked with cloud key credentials. - */ - public bool|null $can_unlock_with_cloud_key, - /** - * Indicates whether the ACS entrance can be unlocked with pin codes. - */ - public bool|null $can_unlock_with_code, - /** - * Indicates whether the ACS entrance can be unlocked with mobile key credentials. - */ - public bool|null $can_unlock_with_mobile_key, - /** - * ID of the [connected account](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $connected_account_id, - /** - * Date and time at which the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) was created. - */ - public string|null $created_at, - /** - * Display name for the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $display_name, - /** - * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceDormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata, - /** - * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceDormakabaCommunityMetadata|null $dormakaba_community_metadata, - /** - * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public array $errors, - /** - * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceHotekMetadata|null $hotek_metadata, - /** - * Indicates whether the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) is currently locked. - */ - public bool|null $is_locked, - /** - * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceLatchMetadata|null $latch_metadata, - /** - * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceSaltoKsMetadata|null $salto_ks_metadata, - /** - * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceSaltoSpaceMetadata|null $salto_space_metadata, - /** - * IDs of the spaces that the entrance is in. - */ - public array|null $space_ids, - /** - * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceVisionlineMetadata|null $visionline_metadata, - /** - * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public array $warnings, - ) {} + public function __construct( + /** + * ID of the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $acs_entrance_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $acs_system_id, + /** + * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\AkilesMetadata|null $akiles_metadata, + /** + * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\AvigilonAltaMetadata|null $avigilon_alta_metadata, + /** + * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\BrivoMetadata|null $brivo_metadata, + /** + * Indicates whether the ACS entrance can belong to a reservation via an access_grant.reservation_key. + */ + public bool|null $can_belong_to_reservation, + /** + * Indicates whether the ACS entrance can be unlocked with card credentials. + */ + public bool|null $can_unlock_with_card, + /** + * Indicates whether the ACS entrance can be unlocked with cloud key credentials. + */ + public bool|null $can_unlock_with_cloud_key, + /** + * Indicates whether the ACS entrance can be unlocked with pin codes. + */ + public bool|null $can_unlock_with_code, + /** + * Indicates whether the ACS entrance can be unlocked with mobile key credentials. + */ + public bool|null $can_unlock_with_mobile_key, + /** + * ID of the [connected account](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $connected_account_id, + /** + * Date and time at which the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) was created. + */ + public string|null $created_at, + /** + * Display name for the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $display_name, + /** + * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\DormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata, + /** + * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata, + /** + * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public array $errors, + /** + * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\HotekMetadata|null $hotek_metadata, + /** + * Indicates whether the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) is currently locked. + */ + public bool|null $is_locked, + /** + * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\LatchMetadata|null $latch_metadata, + /** + * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata, + /** + * IDs of the spaces that the entrance is in. + */ + public array|null $space_ids, + /** + * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\VisionlineMetadata|null $visionline_metadata, + /** + * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public array $warnings, + ) {} + } } -/** - * Actions the gadget exposes (for example, open). - */ -class AcsEntranceActions -{ - public static function from_json(mixed $json): AcsEntranceActions|null +namespace Seam\Resources\AcsEntrance { + /** + * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class AkilesMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self( + actions: array_map( + fn($a) => AkilesMetadata\Actions::from_json($a), + $json->actions ?? [], + ), + gadget_id: $json->gadget_id ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); } - return new self(id: $json->id ?? null, name: $json->name ?? null); - } - public function __construct( - /** - * ID of the gadget action. - */ - public string|null $id, - /** - * Name of the gadget action. - */ - public string|null $name, - ) {} -} + public function __construct( + /** + * Actions the gadget exposes (for example, open). + */ + public array $actions, + /** + * ID of the Akiles gadget. + */ + public string|null $gadget_id, + /** + * ID of the Akiles site the gadget belongs to. + */ + public string|null $site_id, + /** + * Name of the Akiles site the gadget belongs to. + */ + public string|null $site_name, + ) {} + } -/** - * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceAkilesMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceAkilesMetadata|null { - if (!$json) { - return null; + /** + * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class AssaAbloyVostioMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + door_name: $json->door_name ?? null, + door_number: $json->door_number ?? null, + door_type: $json->door_type ?? null, + pms_id: $json->pms_id ?? null, + stand_open: $json->stand_open ?? null, + ); } - return new self( - actions: array_map( - fn($a) => AcsEntranceActions::from_json($a), - $json->actions ?? [], - ), - gadget_id: $json->gadget_id ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - public function __construct( - /** - * Actions the gadget exposes (for example, open). - */ - public array $actions, - /** - * ID of the Akiles gadget. - */ - public string|null $gadget_id, - /** - * ID of the Akiles site the gadget belongs to. - */ - public string|null $site_id, - /** - * Name of the Akiles site the gadget belongs to. - */ - public string|null $site_name, - ) {} -} + public function __construct( + /** + * Name of the door in the Vostio access system. + */ + public string|null $door_name, + /** + * Number of the door in the Vostio access system. + */ + public float|null $door_number, + /** + * Type of the door in the Vostio access system. + */ + public string|null $door_type, + /** + * PMS ID of the door in the Vostio access system. + */ + public string|null $pms_id, + /** + * Indicates whether keys are allowed to set the door in stand open mode in the Vostio access system. + */ + public bool|null $stand_open, + ) {} + } -/** - * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceAssaAbloyVostioMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceAssaAbloyVostioMetadata|null { - if (!$json) { - return null; + /** + * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class AvigilonAltaMetadata + { + public static function from_json(mixed $json): AvigilonAltaMetadata|null + { + if (!$json) { + return null; + } + return new self( + entry_name: $json->entry_name ?? null, + entry_relays_total_count: $json->entry_relays_total_count ?? + null, + org_name: $json->org_name ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + zone_id: $json->zone_id ?? null, + zone_name: $json->zone_name ?? null, + ); } - return new self( - door_name: $json->door_name ?? null, - door_number: $json->door_number ?? null, - door_type: $json->door_type ?? null, - pms_id: $json->pms_id ?? null, - stand_open: $json->stand_open ?? null, - ); - } - public function __construct( - /** - * Name of the door in the Vostio access system. - */ - public string|null $door_name, - /** - * Number of the door in the Vostio access system. - */ - public float|null $door_number, - /** - * Type of the door in the Vostio access system. - */ - public string|null $door_type, - /** - * PMS ID of the door in the Vostio access system. - */ - public string|null $pms_id, - /** - * Indicates whether keys are allowed to set the door in stand open mode in the Vostio access system. - */ - public bool|null $stand_open, - ) {} -} + public function __construct( + /** + * Entry name for an Avigilon Alta system. + */ + public string|null $entry_name, + /** + * Total count of entry relays for an Avigilon Alta system. + */ + public float|null $entry_relays_total_count, + /** + * Organization name for an Avigilon Alta system. + */ + public string|null $org_name, + /** + * Site ID for an Avigilon Alta system. + */ + public float|null $site_id, + /** + * Site name for an Avigilon Alta system. + */ + public string|null $site_name, + /** + * Zone ID for an Avigilon Alta system. + */ + public float|null $zone_id, + /** + * Zone name for an Avigilon Alta system. + */ + public string|null $zone_name, + ) {} + } -/** - * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceAvigilonAltaMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceAvigilonAltaMetadata|null { - if (!$json) { - return null; + /** + * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class BrivoMetadata + { + public static function from_json(mixed $json): BrivoMetadata|null + { + if (!$json) { + return null; + } + return new self( + access_point_id: $json->access_point_id ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); } - return new self( - entry_name: $json->entry_name ?? null, - entry_relays_total_count: $json->entry_relays_total_count ?? null, - org_name: $json->org_name ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - zone_id: $json->zone_id ?? null, - zone_name: $json->zone_name ?? null, - ); - } - public function __construct( - /** - * Entry name for an Avigilon Alta system. - */ - public string|null $entry_name, - /** - * Total count of entry relays for an Avigilon Alta system. - */ - public float|null $entry_relays_total_count, - /** - * Organization name for an Avigilon Alta system. - */ - public string|null $org_name, - /** - * Site ID for an Avigilon Alta system. - */ - public float|null $site_id, - /** - * Site name for an Avigilon Alta system. - */ - public string|null $site_name, - /** - * Zone ID for an Avigilon Alta system. - */ - public float|null $zone_id, - /** - * Zone name for an Avigilon Alta system. - */ - public string|null $zone_name, - ) {} -} + public function __construct( + /** + * ID of the access point in the Brivo access system. + */ + public string|null $access_point_id, + /** + * ID of the site that the access point belongs to. + */ + public float|null $site_id, + /** + * Name of the site that the access point belongs to. + */ + public string|null $site_name, + ) {} + } -/** - * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceBrivoMetadata -{ - public static function from_json(mixed $json): AcsEntranceBrivoMetadata|null + /** + * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class DormakabaAmbianceMetadata { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): DormakabaAmbianceMetadata|null { + if (!$json) { + return null; + } + return new self( + access_point_name: $json->access_point_name ?? null, + ); } - return new self( - access_point_id: $json->access_point_id ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - public function __construct( - /** - * ID of the access point in the Brivo access system. - */ - public string|null $access_point_id, - /** - * ID of the site that the access point belongs to. - */ - public float|null $site_id, - /** - * Name of the site that the access point belongs to. - */ - public string|null $site_name, - ) {} -} + public function __construct( + /** + * Name of the access point in the dormakaba Ambiance access system. + */ + public string|null $access_point_name, + ) {} + } -/** - * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceDormakabaAmbianceMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceDormakabaAmbianceMetadata|null { - if (!$json) { - return null; + /** + * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class DormakabaCommunityMetadata + { + public static function from_json( + mixed $json, + ): DormakabaCommunityMetadata|null { + if (!$json) { + return null; + } + return new self( + access_point_profile: $json->access_point_profile ?? null, + ); } - return new self(access_point_name: $json->access_point_name ?? null); - } - public function __construct( - /** - * Name of the access point in the dormakaba Ambiance access system. - */ - public string|null $access_point_name, - ) {} -} + public function __construct( + /** + * Type of access point profile in the dormakaba Community access system. + */ + public string|null $access_point_profile, + ) {} + } -/** - * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceDormakabaCommunityMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceDormakabaCommunityMetadata|null { - if (!$json) { - return null; + /** + * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - access_point_profile: $json->access_point_profile ?? null, - ); - } - public function __construct( - /** - * Type of access point profile in the dormakaba Community access system. - */ - public string|null $access_point_profile, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceErrors -{ - public static function from_json(mixed $json): AcsEntranceErrors|null + /** + * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class HotekMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): HotekMetadata|null + { + if (!$json) { + return null; + } + return new self( + common_area_name: $json->common_area_name ?? null, + common_area_number: $json->common_area_number ?? null, + room_number: $json->room_number ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Display name of the entrance. + */ + public string|null $common_area_name, + /** + * Display name of the entrance. + */ + public string|null $common_area_number, + /** + * Room number of the entrance. + */ + public string|null $room_number, + ) {} + } -/** - * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceHotekMetadata -{ - public static function from_json(mixed $json): AcsEntranceHotekMetadata|null + /** + * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class LatchMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): LatchMetadata|null + { + if (!$json) { + return null; + } + return new self( + accessibility_type: $json->accessibility_type ?? null, + door_name: $json->door_name ?? null, + door_type: $json->door_type ?? null, + is_connected: $json->is_connected ?? null, + ); } - return new self( - common_area_name: $json->common_area_name ?? null, - common_area_number: $json->common_area_number ?? null, - room_number: $json->room_number ?? null, - ); - } - public function __construct( - /** - * Display name of the entrance. - */ - public string|null $common_area_name, - /** - * Display name of the entrance. - */ - public string|null $common_area_number, - /** - * Room number of the entrance. - */ - public string|null $room_number, - ) {} -} + public function __construct( + /** + * Accessibility type in the Latch access system. + */ + public string|null $accessibility_type, + /** + * Name of the door in the Latch access system. + */ + public string|null $door_name, + /** + * Type of the door in the Latch access system. + */ + public string|null $door_type, + /** + * Indicates whether the entrance is connected. + */ + public bool|null $is_connected, + ) {} + } -/** - * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceLatchMetadata -{ - public static function from_json(mixed $json): AcsEntranceLatchMetadata|null + /** + * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class SaltoKsMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self( + battery_level: $json->battery_level ?? null, + door_name: $json->door_name ?? null, + intrusion_alarm: $json->intrusion_alarm ?? null, + left_open_alarm: $json->left_open_alarm ?? null, + lock_type: $json->lock_type ?? null, + locked_state: $json->locked_state ?? null, + online: $json->online ?? null, + privacy_mode: $json->privacy_mode ?? null, + ); } - return new self( - accessibility_type: $json->accessibility_type ?? null, - door_name: $json->door_name ?? null, - door_type: $json->door_type ?? null, - is_connected: $json->is_connected ?? null, - ); - } - public function __construct( - /** - * Accessibility type in the Latch access system. - */ - public string|null $accessibility_type, - /** - * Name of the door in the Latch access system. - */ - public string|null $door_name, - /** - * Type of the door in the Latch access system. - */ - public string|null $door_type, - /** - * Indicates whether the entrance is connected. - */ - public bool|null $is_connected, - ) {} -} + public function __construct( + /** + * Battery level of the door access device. + */ + public string|null $battery_level, + /** + * Name of the door in the Salto KS access system. + */ + public string|null $door_name, + /** + * Indicates whether an intrusion alarm is active on the door. + */ + public bool|null $intrusion_alarm, + /** + * Indicates whether the door is left open. + */ + public bool|null $left_open_alarm, + /** + * Type of the lock in the Salto KS access system. + */ + public string|null $lock_type, + /** + * Locked state of the door in the Salto KS access system. + */ + public string|null $locked_state, + /** + * Indicates whether the door access device is online. + */ + public bool|null $online, + /** + * Indicates whether privacy mode is enabled for the lock. + */ + public bool|null $privacy_mode, + ) {} + } -/** - * Profile for the door in the Visionline access system. - */ -class AcsEntranceProfiles -{ - public static function from_json(mixed $json): AcsEntranceProfiles|null + /** + * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class SaltoSpaceMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoSpaceMetadata|null + { + if (!$json) { + return null; + } + return new self( + audit_on_keys: $json->audit_on_keys ?? null, + door_description: $json->door_description ?? null, + door_id: $json->door_id ?? null, + door_name: $json->door_name ?? null, + room_description: $json->room_description ?? null, + room_name: $json->room_name ?? null, + ); } - return new self( - visionline_door_profile_id: $json->visionline_door_profile_id ?? - null, - visionline_door_profile_type: $json->visionline_door_profile_type ?? - null, - ); - } - public function __construct( - /** - * Door profile ID in the Visionline access system. - */ - public string|null $visionline_door_profile_id, - /** - * Door profile type in the Visionline access system. - */ - public string|null $visionline_door_profile_type, - ) {} -} + public function __construct( + /** + * Indicates whether AuditOnKeys is enabled for the door in the Salto Space access system. + */ + public bool|null $audit_on_keys, + /** + * Description of the door in the Salto Space access system. + */ + public string|null $door_description, + /** + * Door ID in the Salto Space access system. + */ + public string|null $door_id, + /** + * Name of the door in the Salto Space access system. + */ + public string|null $door_name, + /** + * Description of the room in the Salto Space access system. + */ + public string|null $room_description, + /** + * Name of the room in the Salto Space access system. + */ + public string|null $room_name, + ) {} + } -/** - * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceSaltoKsMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceSaltoKsMetadata|null { - if (!$json) { - return null; + /** + * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + door_category: $json->door_category ?? null, + door_name: $json->door_name ?? null, + profiles: array_map( + fn($p) => VisionlineMetadata\Profiles::from_json($p), + $json->profiles ?? [], + ), + ); } - return new self( - battery_level: $json->battery_level ?? null, - door_name: $json->door_name ?? null, - intrusion_alarm: $json->intrusion_alarm ?? null, - left_open_alarm: $json->left_open_alarm ?? null, - lock_type: $json->lock_type ?? null, - locked_state: $json->locked_state ?? null, - online: $json->online ?? null, - privacy_mode: $json->privacy_mode ?? null, - ); - } - public function __construct( - /** - * Battery level of the door access device. - */ - public string|null $battery_level, - /** - * Name of the door in the Salto KS access system. - */ - public string|null $door_name, - /** - * Indicates whether an intrusion alarm is active on the door. - */ - public bool|null $intrusion_alarm, - /** - * Indicates whether the door is left open. - */ - public bool|null $left_open_alarm, - /** - * Type of the lock in the Salto KS access system. - */ - public string|null $lock_type, - /** - * Locked state of the door in the Salto KS access system. - */ - public string|null $locked_state, - /** - * Indicates whether the door access device is online. - */ - public bool|null $online, - /** - * Indicates whether privacy mode is enabled for the lock. - */ - public bool|null $privacy_mode, - ) {} -} + public function __construct( + /** + * Category of the door in the Visionline access system. + */ + public string|null $door_category, + /** + * Name of the door in the Visionline access system. + */ + public string|null $door_name, + /** + * Profile for the door in the Visionline access system. + */ + public array $profiles, + ) {} + } -/** - * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceSaltoSpaceMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceSaltoSpaceMetadata|null { - if (!$json) { - return null; + /** + * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - audit_on_keys: $json->audit_on_keys ?? null, - door_description: $json->door_description ?? null, - door_id: $json->door_id ?? null, - door_name: $json->door_name ?? null, - room_description: $json->room_description ?? null, - room_name: $json->room_name ?? null, - ); - } - public function __construct( - /** - * Indicates whether AuditOnKeys is enabled for the door in the Salto Space access system. - */ - public bool|null $audit_on_keys, - /** - * Description of the door in the Salto Space access system. - */ - public string|null $door_description, - /** - * Door ID in the Salto Space access system. - */ - public string|null $door_id, - /** - * Name of the door in the Salto Space access system. - */ - public string|null $door_name, - /** - * Description of the room in the Salto Space access system. - */ - public string|null $room_description, - /** - * Name of the room in the Salto Space access system. - */ - public string|null $room_name, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceVisionlineMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceVisionlineMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\AcsEntrance\AkilesMetadata { + /** + * Actions the gadget exposes (for example, open). + */ + class Actions + { + public static function from_json(mixed $json): Actions|null + { + if (!$json) { + return null; + } + return new self(id: $json->id ?? null, name: $json->name ?? null); } - return new self( - door_category: $json->door_category ?? null, - door_name: $json->door_name ?? null, - profiles: array_map( - fn($p) => AcsEntranceProfiles::from_json($p), - $json->profiles ?? [], - ), - ); - } - public function __construct( - /** - * Category of the door in the Visionline access system. - */ - public string|null $door_category, - /** - * Name of the door in the Visionline access system. - */ - public string|null $door_name, - /** - * Profile for the door in the Visionline access system. - */ - public array $profiles, - ) {} + public function __construct( + /** + * ID of the gadget action. + */ + public string|null $id, + /** + * Name of the gadget action. + */ + public string|null $name, + ) {} + } } -/** - * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceWarnings -{ - public static function from_json(mixed $json): AcsEntranceWarnings|null +namespace Seam\Resources\AcsEntrance\VisionlineMetadata { + /** + * Profile for the door in the Visionline access system. + */ + class Profiles { - if (!$json) { - return null; + public static function from_json(mixed $json): Profiles|null + { + if (!$json) { + return null; + } + return new self( + visionline_door_profile_id: $json->visionline_door_profile_id ?? + null, + visionline_door_profile_type: $json->visionline_door_profile_type ?? + null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Door profile ID in the Visionline access system. + */ + public string|null $visionline_door_profile_id, + /** + * Door profile type in the Visionline access system. + */ + public string|null $visionline_door_profile_type, + ) {} + } } diff --git a/src/Resources/AcsSystem.php b/src/Resources/AcsSystem.php index 76b4f601..b00ec137 100644 --- a/src/Resources/AcsSystem.php +++ b/src/Resources/AcsSystem.php @@ -1,273 +1,275 @@ acs_access_group_count ?? null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_count: $json->acs_user_count ?? null, + connected_account_id: $json->connected_account_id ?? null, + connected_account_ids: $json->connected_account_ids ?? null, + created_at: $json->created_at ?? null, + default_credential_manager_acs_system_id: $json->default_credential_manager_acs_system_id ?? + null, + errors: array_map( + fn($e) => AcsSystem\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + image_alt_text: $json->image_alt_text ?? null, + image_url: $json->image_url ?? null, + is_credential_manager: $json->is_credential_manager ?? null, + location: isset($json->location) + ? AcsSystem\Location::from_json($json->location) + : null, + name: $json->name ?? null, + system_type: $json->system_type ?? null, + system_type_display_name: $json->system_type_display_name ?? + null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsSystem\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => AcsSystem\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_access_group_count: $json->acs_access_group_count ?? null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_count: $json->acs_user_count ?? null, - connected_account_id: $json->connected_account_id ?? null, - connected_account_ids: $json->connected_account_ids ?? null, - created_at: $json->created_at ?? null, - default_credential_manager_acs_system_id: $json->default_credential_manager_acs_system_id ?? - null, - errors: array_map( - fn($e) => AcsSystemErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - image_alt_text: $json->image_alt_text ?? null, - image_url: $json->image_url ?? null, - is_credential_manager: $json->is_credential_manager ?? null, - location: isset($json->location) - ? AcsSystemLocation::from_json($json->location) - : null, - name: $json->name ?? null, - system_type: $json->system_type ?? null, - system_type_display_name: $json->system_type_display_name ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? AcsSystemVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - warnings: array_map( - fn($w) => AcsSystemWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Number of access groups in the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public float|null $acs_access_group_count, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $acs_system_id, - /** - * Number of users in the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public float|null $acs_user_count, - /** - * ID of the connected account associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $connected_account_id, - /** - * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * @deprecated Use `connected_account_id`. - */ - public array|null $connected_account_ids, - /** - * Date and time at which the [access control system](https://docs.seam.co/low-level-apis/access-systems) was created. - */ - public string|null $created_at, - /** - * ID of the default credential manager `acs_system` for this [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $default_credential_manager_acs_system_id, - /** - * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public array $errors, - /** - * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. - */ - public string|null $external_type_display_name, - /** - * Alternative text for the [access control system](https://docs.seam.co/low-level-apis/access-systems) image. - */ - public string|null $image_alt_text, - /** - * URL for the image that represents the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $image_url, - /** - * Indicates whether the `acs_system` is a credential manager. - */ - public bool|null $is_credential_manager, - /** - * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public AcsSystemLocation|null $location, - /** - * Name of the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $name, - /** - * @deprecated Use `external_type`. - */ - public string|null $system_type, - /** - * @deprecated Use `external_type_display_name`. - */ - public string|null $system_type_display_name, - /** - * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public AcsSystemVisionlineMetadata|null $visionline_metadata, - /** - * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public array $warnings, - /** - * ID of the workspace that contains the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Number of access groups in the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public float|null $acs_access_group_count, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $acs_system_id, + /** + * Number of users in the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public float|null $acs_user_count, + /** + * ID of the connected account associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $connected_account_id, + /** + * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * @deprecated Use `connected_account_id`. + */ + public array|null $connected_account_ids, + /** + * Date and time at which the [access control system](https://docs.seam.co/low-level-apis/access-systems) was created. + */ + public string|null $created_at, + /** + * ID of the default credential manager `acs_system` for this [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $default_credential_manager_acs_system_id, + /** + * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public array $errors, + /** + * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. + */ + public string|null $external_type_display_name, + /** + * Alternative text for the [access control system](https://docs.seam.co/low-level-apis/access-systems) image. + */ + public string|null $image_alt_text, + /** + * URL for the image that represents the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $image_url, + /** + * Indicates whether the `acs_system` is a credential manager. + */ + public bool|null $is_credential_manager, + /** + * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public AcsSystem\Location|null $location, + /** + * Name of the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $name, + /** + * @deprecated Use `external_type`. + */ + public string|null $system_type, + /** + * @deprecated Use `external_type_display_name`. + */ + public string|null $system_type_display_name, + /** + * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public AcsSystem\VisionlineMetadata|null $visionline_metadata, + /** + * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public array $warnings, + /** + * ID of the workspace that contains the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ -class AcsSystemErrors -{ - public static function from_json(mixed $json): AcsSystemErrors|null +namespace Seam\Resources\AcsSystem { + /** + * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ -class AcsSystemLocation -{ - public static function from_json(mixed $json): AcsSystemLocation|null + /** + * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + class Location { - if (!$json) { - return null; + public static function from_json(mixed $json): Location|null + { + if (!$json) { + return null; + } + return new self(time_zone: $json->time_zone ?? null); } - return new self(time_zone: $json->time_zone ?? null); - } - public function __construct( - /** - * Time zone in which the [access control system](https://docs.seam.co/low-level-apis/access-systems) is located. - */ - public string|null $time_zone, - ) {} -} + public function __construct( + /** + * Time zone in which the [access control system](https://docs.seam.co/low-level-apis/access-systems) is located. + */ + public string|null $time_zone, + ) {} + } -/** - * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ -class AcsSystemVisionlineMetadata -{ - public static function from_json( - mixed $json, - ): AcsSystemVisionlineMetadata|null { - if (!$json) { - return null; + /** + * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + lan_address: $json->lan_address ?? null, + mobile_access_uuid: $json->mobile_access_uuid ?? null, + system_id: $json->system_id ?? null, + ); } - return new self( - lan_address: $json->lan_address ?? null, - mobile_access_uuid: $json->mobile_access_uuid ?? null, - system_id: $json->system_id ?? null, - ); - } - public function __construct( - /** - * IP address or hostname of the main Visionline server relative to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge) on the local network. - */ - public string|null $lan_address, - /** - * Keyset loaded into a reader. Mobile keys and reader administration tools securely authenticate only with readers programmed with a matching keyset. - */ - public string|null $mobile_access_uuid, - /** - * Unique ID assigned by the ASSA ABLOY licensing team that identifies each hotel in your credential manager. - */ - public string|null $system_id, - ) {} -} + public function __construct( + /** + * IP address or hostname of the main Visionline server relative to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge) on the local network. + */ + public string|null $lan_address, + /** + * Keyset loaded into a reader. Mobile keys and reader administration tools securely authenticate only with readers programmed with a matching keyset. + */ + public string|null $mobile_access_uuid, + /** + * Unique ID assigned by the ASSA ABLOY licensing team that identifies each hotel in your credential manager. + */ + public string|null $system_id, + ) {} + } -/** - * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ -class AcsSystemWarnings -{ - public static function from_json(mixed $json): AcsSystemWarnings|null + /** + * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + misconfigured_acs_entrance_ids: $json->misconfigured_acs_entrance_ids ?? + null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - misconfigured_acs_entrance_ids: $json->misconfigured_acs_entrance_ids ?? - null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * @deprecated this field is deprecated. - */ - public array|null $misconfigured_acs_entrance_ids, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * @deprecated this field is deprecated. + */ + public array|null $misconfigured_acs_entrance_ids, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index b8df0217..e2bc4fbf 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -1,478 +1,470 @@ access_schedule) + ? AcsUser\AccessSchedule::from_json($json->access_schedule) + : null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + email: $json->email ?? null, + email_address: $json->email_address ?? null, + errors: array_map( + fn($e) => AcsUser\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + full_name: $json->full_name ?? null, + hid_acs_system_id: $json->hid_acs_system_id ?? null, + is_managed: $json->is_managed ?? null, + is_suspended: $json->is_suspended ?? null, + pending_mutations: array_map( + fn($p) => AcsUser\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + phone_number: $json->phone_number ?? null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? AcsUser\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + salto_space_metadata: isset($json->salto_space_metadata) + ? AcsUser\SaltoSpaceMetadata::from_json( + $json->salto_space_metadata, + ) + : null, + user_identity_email_address: $json->user_identity_email_address ?? + null, + user_identity_full_name: $json->user_identity_full_name ?? null, + user_identity_id: $json->user_identity_id ?? null, + user_identity_phone_number: $json->user_identity_phone_number ?? + null, + warnings: array_map( + fn($w) => AcsUser\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_schedule: isset($json->access_schedule) - ? AcsUserAccessSchedule::from_json($json->access_schedule) - : null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - email: $json->email ?? null, - email_address: $json->email_address ?? null, - errors: array_map( - fn($e) => AcsUserErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - full_name: $json->full_name ?? null, - hid_acs_system_id: $json->hid_acs_system_id ?? null, - is_managed: $json->is_managed ?? null, - is_suspended: $json->is_suspended ?? null, - pending_mutations: array_map( - fn($p) => AcsUserPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - phone_number: $json->phone_number ?? null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? AcsUserSaltoKsMetadata::from_json($json->salto_ks_metadata) - : null, - salto_space_metadata: isset($json->salto_space_metadata) - ? AcsUserSaltoSpaceMetadata::from_json( - $json->salto_space_metadata, - ) - : null, - user_identity_email_address: $json->user_identity_email_address ?? - null, - user_identity_full_name: $json->user_identity_full_name ?? null, - user_identity_id: $json->user_identity_id ?? null, - user_identity_phone_number: $json->user_identity_phone_number ?? - null, - warnings: array_map( - fn($w) => AcsUserWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. - */ - public AcsUserAccessSchedule|null $access_schedule, - /** - * ID of the [access system](https://docs.seam.co/low-level-apis/access-systems) that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $acs_system_id, - /** - * ID of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $acs_user_id, - /** - * The ID of the connected account that is associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $connected_account_id, - /** - * Date and time at which the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was created. - */ - public string|null $created_at, - /** - * Display name for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $display_name, - /** - * @deprecated use email_address. - */ - public string|null $email, - /** - * Email address of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $email_address, - /** - * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public array $errors, - /** - * Brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. - */ - public string|null $external_type_display_name, - /** - * Full name of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $full_name, - /** - * ID of the HID access control system associated with the user. - */ - public string|null $hid_acs_system_id, - /** - * Indicates whether Seam manages the access system user. - */ - public bool|null $is_managed, - /** - * Indicates whether the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is currently [suspended](https://docs.seam.co/low-level-apis/access-systems/user-management/suspending-and-unsuspending-users). - */ - public bool|null $is_suspended, - /** - * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. - */ - public array $pending_mutations, - /** - * Phone number of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). - */ - public string|null $phone_number, - /** - * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public AcsUserSaltoKsMetadata|null $salto_ks_metadata, - /** - * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public AcsUserSaltoSpaceMetadata|null $salto_space_metadata, - /** - * Email address of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $user_identity_email_address, - /** - * Full name of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $user_identity_full_name, - /** - * ID of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $user_identity_id, - /** - * Phone number of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). - */ - public string|null $user_identity_phone_number, - /** - * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public array $warnings, - /** - * ID of the workspace that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. + */ + public AcsUser\AccessSchedule|null $access_schedule, + /** + * ID of the [access system](https://docs.seam.co/low-level-apis/access-systems) that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $acs_system_id, + /** + * ID of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $acs_user_id, + /** + * The ID of the connected account that is associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $connected_account_id, + /** + * Date and time at which the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was created. + */ + public string|null $created_at, + /** + * Display name for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $display_name, + /** + * @deprecated use email_address. + */ + public string|null $email, + /** + * Email address of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $email_address, + /** + * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public array $errors, + /** + * Brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. + */ + public string|null $external_type_display_name, + /** + * Full name of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $full_name, + /** + * ID of the HID access control system associated with the user. + */ + public string|null $hid_acs_system_id, + /** + * Indicates whether Seam manages the access system user. + */ + public bool|null $is_managed, + /** + * Indicates whether the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is currently [suspended](https://docs.seam.co/low-level-apis/access-systems/user-management/suspending-and-unsuspending-users). + */ + public bool|null $is_suspended, + /** + * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. + */ + public array $pending_mutations, + /** + * Phone number of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). + */ + public string|null $phone_number, + /** + * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public AcsUser\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public AcsUser\SaltoSpaceMetadata|null $salto_space_metadata, + /** + * Email address of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $user_identity_email_address, + /** + * Full name of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $user_identity_full_name, + /** + * ID of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $user_identity_id, + /** + * Phone number of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). + */ + public string|null $user_identity_phone_number, + /** + * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public array $warnings, + /** + * ID of the workspace that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $workspace_id, + ) {} + } } -/** - * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. - */ -class AcsUserAccessSchedule -{ - public static function from_json(mixed $json): AcsUserAccessSchedule|null +namespace Seam\Resources\AcsUser { + /** + * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. + */ + class AccessSchedule { - if (!$json) { - return null; + public static function from_json(mixed $json): AccessSchedule|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Date and time at which the user's access ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Date and time at which the user's access starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the user's access ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Date and time at which the user's access starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + ) {} + } -/** - * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ -class AcsUserErrors -{ - public static function from_json(mixed $json): AcsUserErrors|null + /** + * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Old access system user information. - */ -class AcsUserFrom -{ - public static function from_json(mixed $json): AcsUserFrom|null + /** + * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + scheduled_at: $json->scheduled_at ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + variant: $json->variant ?? null, + ); } - return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - email_address: $json->email_address ?? null, - ends_at: $json->ends_at ?? null, - full_name: $json->full_name ?? null, - is_suspended: $json->is_suspended ?? null, - phone_number: $json->phone_number ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Old access group ID. - */ - public string|null $acs_access_group_id, - /** - * Previous credential ID. - */ - public string|null $acs_credential_id, - /** - * Email address of the access system user. - */ - public string|null $email_address, - /** - * Starting time for the access schedule. - */ - public string|null $ends_at, - /** - * Full name of the access system user. - */ - public string|null $full_name, - public bool|null $is_suspended, - /** - * Phone number of the access system user. - */ - public string|null $phone_number, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * ID of the access group involved in the scheduled change. + */ + public string|null $acs_access_group_id, + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + /** + * Optional: When the user creation is scheduled to occur. + */ + public string|null $scheduled_at, + public PendingMutations\To|null $to, + /** + * Whether the user is scheduled to be added to or removed from the access group. + */ + public string|null $variant, + ) {} + } -/** - * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. - */ -class AcsUserPendingMutations -{ - public static function from_json(mixed $json): AcsUserPendingMutations|null + /** + * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + class SaltoKsMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self(is_subscribed: $json->is_subscribed ?? null); } - return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AcsUserFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - scheduled_at: $json->scheduled_at ?? null, - to: isset($json->to) ? AcsUserTo::from_json($json->to) : null, - variant: $json->variant ?? null, - ); - } - public function __construct( - /** - * ID of the access group involved in the scheduled change. - */ - public string|null $acs_access_group_id, - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Old access system user information. - */ - public AcsUserFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. - */ - public string|null $mutation_code, - /** - * Optional: When the user creation is scheduled to occur. - */ - public string|null $scheduled_at, - /** - * New access system user information. - */ - public AcsUserTo|null $to, - /** - * Whether the user is scheduled to be added to or removed from the access group. - */ - public string|null $variant, - ) {} -} + public function __construct( + /** + * Indicates whether the user holds an active subscription slot on the Salto KS site. Only subscribed users can unlock doors and count against the site's user-subscription limit. A user may not be subscribed because their access schedule has not started or has ended, the site has reached its subscription limit, or they were manually unsubscribed. This is distinct from `is_suspended`, which reflects whether the user has been explicitly blocked. + */ + public bool|null $is_subscribed, + ) {} + } -/** - * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ -class AcsUserSaltoKsMetadata -{ - public static function from_json(mixed $json): AcsUserSaltoKsMetadata|null + /** + * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + class SaltoSpaceMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoSpaceMetadata|null + { + if (!$json) { + return null; + } + return new self( + audit_openings: $json->audit_openings ?? null, + user_id: $json->user_id ?? null, + ); } - return new self(is_subscribed: $json->is_subscribed ?? null); - } - public function __construct( - /** - * Indicates whether the user holds an active subscription slot on the Salto KS site. Only subscribed users can unlock doors and count against the site's user-subscription limit. A user may not be subscribed because their access schedule has not started or has ended, the site has reached its subscription limit, or they were manually unsubscribed. This is distinct from `is_suspended`, which reflects whether the user has been explicitly blocked. - */ - public bool|null $is_subscribed, - ) {} -} + public function __construct( + /** + * Indicates whether AuditOpenings is enabled for the user in the Salto Space access system. + */ + public bool|null $audit_openings, + /** + * User ID in the Salto Space access system. + */ + public string|null $user_id, + ) {} + } -/** - * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ -class AcsUserSaltoSpaceMetadata -{ - public static function from_json( - mixed $json, - ): AcsUserSaltoSpaceMetadata|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - audit_openings: $json->audit_openings ?? null, - user_id: $json->user_id ?? null, - ); - } - public function __construct( - /** - * Indicates whether AuditOpenings is enabled for the user in the Salto Space access system. - */ - public bool|null $audit_openings, - /** - * User ID in the Salto Space access system. - */ - public string|null $user_id, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + public string|null $warning_code, + ) {} + } } -/** - * New access system user information. - */ -class AcsUserTo -{ - public static function from_json(mixed $json): AcsUserTo|null +namespace Seam\Resources\AcsUser\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + email_address: $json->email_address ?? null, + ends_at: $json->ends_at ?? null, + full_name: $json->full_name ?? null, + is_suspended: $json->is_suspended ?? null, + phone_number: $json->phone_number ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - email_address: $json->email_address ?? null, - ends_at: $json->ends_at ?? null, - full_name: $json->full_name ?? null, - is_suspended: $json->is_suspended ?? null, - phone_number: $json->phone_number ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New access group ID. - */ - public string|null $acs_access_group_id, - /** - * New credential ID. - */ - public string|null $acs_credential_id, - /** - * Email address of the access system user. - */ - public string|null $email_address, - /** - * Starting time for the access schedule. - */ - public string|null $ends_at, - /** - * Full name of the access system user. - */ - public string|null $full_name, - public bool|null $is_suspended, - /** - * Phone number of the access system user. - */ - public string|null $phone_number, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Old access group ID. + */ + public string|null $acs_access_group_id, + /** + * Previous credential ID. + */ + public string|null $acs_credential_id, + /** + * Email address of the access system user. + */ + public string|null $email_address, + /** + * Starting time for the access schedule. + */ + public string|null $ends_at, + /** + * Full name of the access system user. + */ + public string|null $full_name, + public bool|null $is_suspended, + /** + * Phone number of the access system user. + */ + public string|null $phone_number, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ -class AcsUserWarnings -{ - public static function from_json(mixed $json): AcsUserWarnings|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + email_address: $json->email_address ?? null, + ends_at: $json->ends_at ?? null, + full_name: $json->full_name ?? null, + is_suspended: $json->is_suspended ?? null, + phone_number: $json->phone_number ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - public string|null $warning_code, - ) {} + public function __construct( + /** + * New access group ID. + */ + public string|null $acs_access_group_id, + /** + * New credential ID. + */ + public string|null $acs_credential_id, + /** + * Email address of the access system user. + */ + public string|null $email_address, + /** + * Starting time for the access schedule. + */ + public string|null $ends_at, + /** + * Full name of the access system user. + */ + public string|null $full_name, + public bool|null $is_suspended, + /** + * Phone number of the access system user. + */ + public string|null $phone_number, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 3448d210..3a471b9d 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -1,888 +1,1122 @@ action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? ActionAttempt\Error::from_json($json->error) + : null, + result: isset($json->result) + ? ActionAttempt\Result::from_json($json->result) + : null, + status: $json->status ?? null, + ); } - return new self( - action_attempt_id: $json->action_attempt_id ?? null, - action_type: $json->action_type ?? null, - error: isset($json->error) - ? ActionAttemptError::from_json($json->error) - : null, - result: isset($json->result) - ? ActionAttemptResult::from_json($json->result) - : null, - status: $json->status ?? null, - ); - } - public function __construct( - /** - * ID of the action attempt. - */ - public string|null $action_attempt_id, - /** - * Action attempt to track the status of locking a door. - */ - public string|null $action_type, - /** - * Error associated with the action. - */ - public ActionAttemptError|null $error, - /** - * Result of the action. - */ - public ActionAttemptResult|null $result, - public string|null $status, - ) {} + public function __construct( + /** + * ID of the action attempt. + */ + public string|null $action_attempt_id, + public string|null $action_type, + /** + * Error associated with the action. + */ + public ActionAttempt\Error|null $error, + public ActionAttempt\Result|null $result, + public string|null $status, + ) {} + } } -/** - * Snapshot of credential data read from the physical encoder. - */ -class ActionAttemptAcsCredentialOnEncoder -{ - public static function from_json( - mixed $json, - ): ActionAttemptAcsCredentialOnEncoder|null { - if (!$json) { - return null; +namespace Seam\Resources\ActionAttempt { + /** + * Error associated with the action. + */ + class Error + { + public static function from_json(mixed $json): Error|null + { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + type: $json->type ?? null, + ); } - return new self( - card_number: $json->card_number ?? null, - created_at: $json->created_at ?? null, - ends_at: $json->ends_at ?? null, - is_issued: $json->is_issued ?? null, - starts_at: $json->starts_at ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? ActionAttemptVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - ); - } - public function __construct( - /** - * A number or string that physically identifies the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) will stop being usable. - */ - public string|null $ends_at, - /** - * Indicates whether the credential has been issued (encoded onto a card). - */ - public bool|null $is_issued, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) becomes usable. - */ - public string|null $starts_at, - /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptVisionlineMetadata|null $visionline_metadata, - ) {} -} + public function __construct( + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + public string|null $type, + ) {} + } -/** - * Corresponding credential data as stored on Seam and the access system. - */ -class ActionAttemptAcsCredentialOnSeam -{ - public static function from_json( - mixed $json, - ): ActionAttemptAcsCredentialOnSeam|null { - if (!$json) { - return null; - } - return new self( - access_method: $json->access_method ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - akiles_metadata: isset($json->akiles_metadata) - ? ActionAttemptAkilesMetadata::from_json($json->akiles_metadata) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? ActionAttemptAssaAbloyVostioMetadata::from_json( - $json->assa_abloy_vostio_metadata, + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self( + access_code: $json->access_code ?? null, + access_method: $json->access_method ?? null, + access_method_id: $json->access_method_id ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_credential_on_encoder: isset( + $json->acs_credential_on_encoder, ) - : null, - card_number: $json->card_number ?? null, - code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => ActionAttemptErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - is_issued: $json->is_issued ?? null, - is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? - null, - is_managed: $json->is_managed ?? null, - is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? - null, - is_one_time_use: $json->is_one_time_use ?? null, - issued_at: $json->issued_at ?? null, - latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? - null, - parent_acs_credential_id: $json->parent_acs_credential_id ?? null, - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? ActionAttemptVisionlineMetadata::from_json( - $json->visionline_metadata, + ? Result\AcsCredentialOnEncoder::from_json( + $json->acs_credential_on_encoder, + ) + : null, + acs_credential_on_seam: isset($json->acs_credential_on_seam) + ? Result\AcsCredentialOnSeam::from_json( + $json->acs_credential_on_seam, + ) + : null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? Result\AkilesMetadata::from_json($json->akiles_metadata) + : null, + assa_abloy_vostio_metadata: isset( + $json->assa_abloy_vostio_metadata, ) - : null, - warnings: array_map( - fn($w) => ActionAttemptWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + ? Result\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + card_number: $json->card_number ?? null, + client_session_token: $json->client_session_token ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => Result\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + instant_key_url: $json->instant_key_url ?? null, + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_issued: $json->is_issued ?? null, + is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? + null, + is_managed: $json->is_managed ?? null, + is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? + null, + is_one_time_use: $json->is_one_time_use ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, + issued_at: $json->issued_at ?? null, + latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? + null, + mode: $json->mode ?? null, + noise_threshold: $json->noise_threshold ?? null, + parent_acs_credential_id: $json->parent_acs_credential_id ?? + null, + pending_mutations: array_map( + fn($p) => Result\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? Result\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => Result\Warnings::from_json($w), + $json->warnings ?? [], + ), + was_confirmed_by_device: $json->was_confirmed_by_device ?? null, + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $access_method, - /** - * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_credential_id, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_system_id, - /** - * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $acs_user_id, - /** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptAkilesMetadata|null $akiles_metadata, - /** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $code, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $display_name, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. - */ - public string|null $ends_at, - /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $errors, - /** - * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $external_type_display_name, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. - */ - public bool|null $is_issued, - /** - * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. - */ - public bool|null $is_latest_desired_state_synced_with_provider, - public bool|null $is_managed, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). - */ - public bool|null $is_multi_phone_sync_credential, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. - */ - public bool|null $is_one_time_use, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. - */ - public string|null $issued_at, - /** - * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. - */ - public string|null $latest_desired_state_synced_with_provider_at, - /** - * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $parent_acs_credential_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $user_identity_id, - /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptVisionlineMetadata|null $visionline_metadata, - /** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $warnings, - /** - * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $workspace_id, - ) {} + public function __construct( + public mixed $access_code, + /** + * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $access_method, + /** + * ID of the access method. + */ + public string|null $access_method_id, + /** + * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_credential_id, + /** + * Snapshot of credential data read from the physical encoder. + */ + public Result\AcsCredentialOnEncoder|null $acs_credential_on_encoder, + /** + * Corresponding credential data as stored on Seam and the access system. + */ + public Result\AcsCredentialOnSeam|null $acs_credential_on_seam, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_system_id, + /** + * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $acs_user_id, + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public Result\AkilesMetadata|null $akiles_metadata, + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Token of the client session associated with the access method. + */ + public string|null $client_session_token, + public string|null $code, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + public string|null $created_at, + /** + * ID of the customization profile associated with the access method. + */ + public string|null $customization_profile_id, + public string|null $display_name, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + */ + public string|null $ends_at, + public array $errors, + /** + * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $external_type_display_name, + /** + * URL of the Instant Key for mobile key access methods. + */ + public string|null $instant_key_url, + /** + * 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. + */ + public bool|null $is_assignment_required, + /** + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + */ + public bool|null $is_encoding_required, + public bool|null $is_issued, + /** + * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. + */ + public bool|null $is_latest_desired_state_synced_with_provider, + /** + * Indicates whether Seam manages the credential. + */ + public bool|null $is_managed, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). + */ + public bool|null $is_multi_phone_sync_credential, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use, + /** + * 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. + */ + public bool|null $is_ready_for_assignment, + /** + * 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. + */ + public bool|null $is_ready_for_encoding, + public string|null $issued_at, + /** + * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. + */ + public string|null $latest_desired_state_synced_with_provider_at, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + public mixed $noise_threshold, + /** + * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $parent_acs_credential_id, + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + public array $pending_mutations, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $user_identity_id, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public Result\VisionlineMetadata|null $visionline_metadata, + public array $warnings, + public bool|null $was_confirmed_by_device, + public string|null $workspace_id, + ) {} + } } -/** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptAkilesMetadata -{ - public static function from_json( - mixed $json, - ): ActionAttemptAkilesMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\ActionAttempt\Result { + /** + * Snapshot of credential data read from the physical encoder. + */ + class AcsCredentialOnEncoder + { + public static function from_json( + mixed $json, + ): AcsCredentialOnEncoder|null { + if (!$json) { + return null; + } + return new self( + card_number: $json->card_number ?? null, + created_at: $json->created_at ?? null, + ends_at: $json->ends_at ?? null, + is_issued: $json->is_issued ?? null, + starts_at: $json->starts_at ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsCredentialOnEncoder\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + ); } - return new self(member_pin_id: $json->member_pin_id ?? null); - } - public function __construct( - /** - * ID of the Akiles member PIN. - */ - public string|null $member_pin_id, - ) {} -} + public function __construct( + /** + * A number or string that physically identifies the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) will stop being usable. + */ + public string|null $ends_at, + /** + * Indicates whether the credential has been issued (encoded onto a card). + */ + public bool|null $is_issued, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) becomes usable. + */ + public string|null $starts_at, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredentialOnEncoder\VisionlineMetadata|null $visionline_metadata, + ) {} + } -/** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptAssaAbloyVostioMetadata -{ - public static function from_json( - mixed $json, - ): ActionAttemptAssaAbloyVostioMetadata|null { - if (!$json) { - return null; + /** + * Corresponding credential data as stored on Seam and the access system. + */ + class AcsCredentialOnSeam + { + public static function from_json(mixed $json): AcsCredentialOnSeam|null + { + if (!$json) { + return null; + } + return new self( + access_method: $json->access_method ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? AcsCredentialOnSeam\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( + $json->assa_abloy_vostio_metadata, + ) + ? AcsCredentialOnSeam\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + card_number: $json->card_number ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => AcsCredentialOnSeam\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + is_issued: $json->is_issued ?? null, + is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? + null, + is_managed: $json->is_managed ?? null, + is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? + null, + is_one_time_use: $json->is_one_time_use ?? null, + issued_at: $json->issued_at ?? null, + latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? + null, + parent_acs_credential_id: $json->parent_acs_credential_id ?? + null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsCredentialOnSeam\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => AcsCredentialOnSeam\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - auto_join: $json->auto_join ?? null, - door_names: $json->door_names ?? null, - endpoint_id: $json->endpoint_id ?? null, - key_id: $json->key_id ?? null, - key_issuing_request_id: $json->key_issuing_request_id ?? null, - override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? - null, - ); - } - public function __construct( - /** - * 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. - */ - public bool|null $auto_join, - /** - * Names of the doors to which to grant access in the Vostio access system. - */ - public array|null $door_names, - /** - * Endpoint ID in the Vostio access system. - */ - public string|null $endpoint_id, - /** - * Key ID in the Vostio access system. - */ - public string|null $key_id, - /** - * Key issuing request ID in the Vostio access system. - */ - public string|null $key_issuing_request_id, - /** - * IDs of the guest entrances to override in the Vostio access system. - */ - public array|null $override_guest_acs_entrance_ids, - ) {} -} + public function __construct( + /** + * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $access_method, + /** + * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_credential_id, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_system_id, + /** + * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $acs_user_id, + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredentialOnSeam\AkilesMetadata|null $akiles_metadata, + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredentialOnSeam\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $code, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $display_name, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + */ + public string|null $ends_at, + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $errors, + /** + * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $external_type_display_name, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. + */ + public bool|null $is_issued, + /** + * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. + */ + public bool|null $is_latest_desired_state_synced_with_provider, + public bool|null $is_managed, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). + */ + public bool|null $is_multi_phone_sync_credential, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. + */ + public string|null $issued_at, + /** + * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. + */ + public string|null $latest_desired_state_synced_with_provider_at, + /** + * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $parent_acs_credential_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $user_identity_id, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata, + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $warnings, + /** + * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $workspace_id, + ) {} + } -/** - * Error associated with the action. - */ -class ActionAttemptError -{ - public static function from_json(mixed $json): ActionAttemptError|null + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, + warning_code: $json->warning_code ?? null, + warning_message: $json->warning_message ?? null, + ); } - return new self( - message: $json->message ?? null, - type: $json->type ?? null, - ); + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id, + public string|null $warning_code, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $warning_message, + ) {} } - public function __construct( - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Type of the error. - */ - public string|null $type, - ) {} -} + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AkilesMetadata + { + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self(member_pin_id: $json->member_pin_id ?? null); + } -/** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptErrors -{ - public static function from_json(mixed $json): ActionAttemptErrors|null + public function __construct( + /** + * ID of the Akiles member PIN. + */ + public string|null $member_pin_id, + ) {} + } + + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AssaAbloyVostioMetadata { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + door_names: $json->door_names ?? null, + endpoint_id: $json->endpoint_id ?? null, + key_id: $json->key_id ?? null, + key_issuing_request_id: $json->key_issuing_request_id ?? null, + override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? + null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + + public function __construct( + /** + * 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. + */ + public bool|null $auto_join, + /** + * Names of the doors to which to grant access in the Vostio access system. + */ + public array|null $door_names, + /** + * Endpoint ID in the Vostio access system. + */ + public string|null $endpoint_id, + /** + * Key ID in the Vostio access system. + */ + public string|null $key_id, + /** + * Key issuing request ID in the Vostio access system. + */ + public string|null $key_issuing_request_id, + /** + * IDs of the guest entrances to override in the Vostio access system. + */ + public array|null $override_guest_acs_entrance_ids, + ) {} } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - public string|null $error_code, - public string|null $message, - ) {} -} + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } -/** - * Previous access time configuration. - */ -class ActionAttemptFrom -{ - public static function from_json(mixed $json): ActionAttemptFrom|null + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + card_function_type: $json->card_function_type ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + credential_id: $json->credential_id ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + is_valid: $json->is_valid ?? null, + joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? + null, + ); } - return new self( - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); + + public function __construct( + /** + * 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. + */ + public bool|null $auto_join, + /** + * Card function type in the Visionline access system. + */ + public string|null $card_function_type, + /** + * ID of the card in the Visionline access system. + */ + public string|null $card_id, + /** + * Common entrance IDs in the Visionline access system. + */ + public array|null $common_acs_entrance_ids, + /** + * ID of the credential in the Visionline access system. + */ + public string|null $credential_id, + /** + * Guest entrance IDs in the Visionline access system. + */ + public array|null $guest_acs_entrance_ids, + /** + * Indicates whether the credential is valid. + */ + public bool|null $is_valid, + /** + * IDs of the credentials to which you want to join. + */ + public array|null $joiner_acs_credential_ids, + ) {} } - public function __construct( - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + class PendingMutations + { + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + /** + * Previous access time configuration. + */ + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + /** + * Mutation code to indicate that Seam is in the process of updating the access times for this access method. + */ + public string|null $mutation_code, + /** + * New access time configuration. + */ + public PendingMutations\To|null $to, + ) {} + } } -/** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ -class ActionAttemptPendingMutations -{ - public static function from_json( - mixed $json, - ): ActionAttemptPendingMutations|null { - if (!$json) { - return null; +namespace Seam\Resources\ActionAttempt\Result\AcsCredentialOnEncoder { + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + cancelled: $json->cancelled ?? null, + card_format: $json->card_format ?? null, + card_holder: $json->card_holder ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + discarded: $json->discarded ?? null, + expired: $json->expired ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + number_of_issued_cards: $json->number_of_issued_cards ?? null, + overridden: $json->overridden ?? null, + overwritten: $json->overwritten ?? null, + pending_auto_update: $json->pending_auto_update ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? ActionAttemptFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) ? ActionAttemptTo::from_json($json->to) : null, - ); - } - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous access time configuration. - */ - public ActionAttemptFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of updating the access times for this access method. - */ - public string|null $mutation_code, - /** - * New access time configuration. - */ - public ActionAttemptTo|null $to, - ) {} + public function __construct( + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is cancelled. + */ + public bool|null $cancelled, + /** + * Format of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_format, + /** + * Holder of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_holder, + /** + * Card ID for the Visionline card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_id, + /** + * IDs of the common [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array|null $common_acs_entrance_ids, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is discarded. + */ + public bool|null $discarded, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is expired. + */ + public bool|null $expired, + /** + * IDs of the guest [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array|null $guest_acs_entrance_ids, + /** + * Number of issued cards associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public float|null $number_of_issued_cards, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overridden. + */ + public bool|null $overridden, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overwritten. + */ + public bool|null $overwritten, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is pending auto-update. + */ + public bool|null $pending_auto_update, + ) {} + } } -/** - * Result of the action. - */ -class ActionAttemptResult -{ - public static function from_json(mixed $json): ActionAttemptResult|null +namespace Seam\Resources\ActionAttempt\Result\AcsCredentialOnSeam { + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AkilesMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self(member_pin_id: $json->member_pin_id ?? null); } - return new self( - access_code: $json->access_code ?? null, - access_method: $json->access_method ?? null, - access_method_id: $json->access_method_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_on_encoder: isset($json->acs_credential_on_encoder) - ? ActionAttemptAcsCredentialOnEncoder::from_json( - $json->acs_credential_on_encoder, - ) - : null, - acs_credential_on_seam: isset($json->acs_credential_on_seam) - ? ActionAttemptAcsCredentialOnSeam::from_json( - $json->acs_credential_on_seam, - ) - : null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - akiles_metadata: isset($json->akiles_metadata) - ? ActionAttemptAkilesMetadata::from_json($json->akiles_metadata) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? ActionAttemptAssaAbloyVostioMetadata::from_json( - $json->assa_abloy_vostio_metadata, - ) - : null, - card_number: $json->card_number ?? null, - client_session_token: $json->client_session_token ?? null, - code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => ActionAttemptErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - instant_key_url: $json->instant_key_url ?? null, - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, - is_issued: $json->is_issued ?? null, - is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? - null, - is_managed: $json->is_managed ?? null, - is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? - null, - is_one_time_use: $json->is_one_time_use ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, - issued_at: $json->issued_at ?? null, - latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? - null, - mode: $json->mode ?? null, - noise_threshold: $json->noise_threshold ?? null, - parent_acs_credential_id: $json->parent_acs_credential_id ?? null, - pending_mutations: array_map( - fn($p) => ActionAttemptPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? ActionAttemptVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - warnings: array_map( - fn($w) => ActionAttemptWarnings::from_json($w), - $json->warnings ?? [], - ), - was_confirmed_by_device: $json->was_confirmed_by_device ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Created access code. - */ - public mixed $access_code, - /** - * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $access_method, - /** - * ID of the access method. - */ - public string|null $access_method_id, - /** - * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_credential_id, - /** - * Snapshot of credential data read from the physical encoder. - */ - public ActionAttemptAcsCredentialOnEncoder|null $acs_credential_on_encoder, - /** - * Corresponding credential data as stored on Seam and the access system. - */ - public ActionAttemptAcsCredentialOnSeam|null $acs_credential_on_seam, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_system_id, - /** - * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $acs_user_id, - /** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptAkilesMetadata|null $akiles_metadata, - /** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Token of the client session associated with the access method. - */ - public string|null $client_session_token, - /** - * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $code, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * ID of the customization profile associated with the access method. - */ - public string|null $customization_profile_id, - /** - * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $display_name, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. - */ - public string|null $ends_at, - /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $errors, - /** - * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $external_type_display_name, - /** - * URL of the Instant Key for mobile key access methods. - */ - public string|null $instant_key_url, - /** - * 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. - */ - public bool|null $is_assignment_required, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. - */ - public bool|null $is_encoding_required, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. - */ - public bool|null $is_issued, - /** - * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. - */ - public bool|null $is_latest_desired_state_synced_with_provider, - public bool|null $is_managed, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). - */ - public bool|null $is_multi_phone_sync_credential, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. - */ - public bool|null $is_one_time_use, - /** - * 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. - */ - public bool|null $is_ready_for_assignment, - /** - * 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. - */ - public bool|null $is_ready_for_encoding, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. - */ - public string|null $issued_at, - /** - * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. - */ - public string|null $latest_desired_state_synced_with_provider_at, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - /** - * Created noise threshold. - */ - public mixed $noise_threshold, - /** - * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $parent_acs_credential_id, - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - public array $pending_mutations, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $user_identity_id, - /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptVisionlineMetadata|null $visionline_metadata, - /** - * 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. - */ - public array $warnings, - /** - * Indicates whether the device confirmed that the lock action occurred. - */ - public bool|null $was_confirmed_by_device, - /** - * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $workspace_id, - ) {} -} + public function __construct( + /** + * ID of the Akiles member PIN. + */ + public string|null $member_pin_id, + ) {} + } -/** - * New access time configuration. - */ -class ActionAttemptTo -{ - public static function from_json(mixed $json): ActionAttemptTo|null + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AssaAbloyVostioMetadata { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + door_names: $json->door_names ?? null, + endpoint_id: $json->endpoint_id ?? null, + key_id: $json->key_id ?? null, + key_issuing_request_id: $json->key_issuing_request_id ?? null, + override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? + null, + ); } - return new self( - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); + + public function __construct( + /** + * 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. + */ + public bool|null $auto_join, + /** + * Names of the doors to which to grant access in the Vostio access system. + */ + public array|null $door_names, + /** + * Endpoint ID in the Vostio access system. + */ + public string|null $endpoint_id, + /** + * Key ID in the Vostio access system. + */ + public string|null $key_id, + /** + * Key issuing request ID in the Vostio access system. + */ + public string|null $key_issuing_request_id, + /** + * IDs of the guest entrances to override in the Vostio access system. + */ + public array|null $override_guest_acs_entrance_ids, + ) {} } - public function __construct( - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} -} + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } -/** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptVisionlineMetadata -{ - public static function from_json( - mixed $json, - ): ActionAttemptVisionlineMetadata|null { - if (!$json) { - return null; + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + public string|null $error_code, + public string|null $message, + ) {} + } + + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + card_function_type: $json->card_function_type ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + credential_id: $json->credential_id ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + is_valid: $json->is_valid ?? null, + joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? + null, + ); } - return new self( - cancelled: $json->cancelled ?? null, - card_format: $json->card_format ?? null, - card_holder: $json->card_holder ?? null, - card_id: $json->card_id ?? null, - common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, - discarded: $json->discarded ?? null, - expired: $json->expired ?? null, - guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, - number_of_issued_cards: $json->number_of_issued_cards ?? null, - overridden: $json->overridden ?? null, - overwritten: $json->overwritten ?? null, - pending_auto_update: $json->pending_auto_update ?? null, - ); + + public function __construct( + /** + * 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. + */ + public bool|null $auto_join, + /** + * Card function type in the Visionline access system. + */ + public string|null $card_function_type, + /** + * ID of the card in the Visionline access system. + */ + public string|null $card_id, + /** + * Common entrance IDs in the Visionline access system. + */ + public array|null $common_acs_entrance_ids, + /** + * ID of the credential in the Visionline access system. + */ + public string|null $credential_id, + /** + * Guest entrance IDs in the Visionline access system. + */ + public array|null $guest_acs_entrance_ids, + /** + * Indicates whether the credential is valid. + */ + public bool|null $is_valid, + /** + * IDs of the credentials to which you want to join. + */ + public array|null $joiner_acs_credential_ids, + ) {} } - public function __construct( - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is cancelled. - */ - public bool|null $cancelled, - /** - * Format of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_format, - /** - * Holder of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_holder, - /** - * Card ID for the Visionline card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_id, - /** - * IDs of the common [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array|null $common_acs_entrance_ids, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is discarded. - */ - public bool|null $discarded, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is expired. - */ - public bool|null $expired, - /** - * IDs of the guest [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array|null $guest_acs_entrance_ids, - /** - * Number of issued cards associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public float|null $number_of_issued_cards, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overridden. - */ - public bool|null $overridden, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overwritten. - */ - public bool|null $overwritten, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is pending auto-update. - */ - public bool|null $pending_auto_update, - ) {} + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptWarnings -{ - public static function from_json(mixed $json): ActionAttemptWarnings|null +namespace Seam\Resources\ActionAttempt\Result\PendingMutations { + /** + * Previous access time configuration. + */ + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); + + public function __construct( + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + /** + * New access time configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/Batch.php b/src/Resources/Batch.php index 1b32ca77..29a7b2da 100644 --- a/src/Resources/Batch.php +++ b/src/Resources/Batch.php @@ -1,218 +1,219 @@ access_codes ?? null, + access_grants: $json->access_grants ?? null, + access_methods: $json->access_methods ?? null, + acs_access_groups: $json->acs_access_groups ?? null, + acs_credentials: $json->acs_credentials ?? null, + acs_encoders: $json->acs_encoders ?? null, + acs_entrances: $json->acs_entrances ?? null, + acs_systems: $json->acs_systems ?? null, + acs_users: $json->acs_users ?? null, + action_attempts: $json->action_attempts ?? null, + client_sessions: $json->client_sessions ?? null, + connect_webviews: $json->connect_webviews ?? null, + connected_accounts: $json->connected_accounts ?? null, + devices: $json->devices ?? null, + events: $json->events ?? null, + instant_keys: $json->instant_keys ?? null, + noise_thresholds: $json->noise_thresholds ?? null, + spaces: $json->spaces ?? null, + thermostat_daily_programs: $json->thermostat_daily_programs ?? + null, + thermostat_schedules: $json->thermostat_schedules ?? null, + unmanaged_access_codes: $json->unmanaged_access_codes ?? null, + unmanaged_devices: $json->unmanaged_devices ?? null, + user_identities: $json->user_identities ?? null, + workspaces: $json->workspaces ?? null, + ); } - return new self( - access_codes: $json->access_codes ?? null, - access_grants: $json->access_grants ?? null, - access_methods: $json->access_methods ?? null, - acs_access_groups: $json->acs_access_groups ?? null, - acs_credentials: $json->acs_credentials ?? null, - acs_encoders: $json->acs_encoders ?? null, - acs_entrances: $json->acs_entrances ?? null, - acs_systems: $json->acs_systems ?? null, - acs_users: $json->acs_users ?? null, - action_attempts: $json->action_attempts ?? null, - client_sessions: $json->client_sessions ?? null, - connect_webviews: $json->connect_webviews ?? null, - connected_accounts: $json->connected_accounts ?? null, - devices: $json->devices ?? null, - events: $json->events ?? null, - instant_keys: $json->instant_keys ?? null, - noise_thresholds: $json->noise_thresholds ?? null, - spaces: $json->spaces ?? null, - thermostat_daily_programs: $json->thermostat_daily_programs ?? null, - thermostat_schedules: $json->thermostat_schedules ?? null, - unmanaged_access_codes: $json->unmanaged_access_codes ?? null, - unmanaged_devices: $json->unmanaged_devices ?? null, - user_identities: $json->user_identities ?? null, - workspaces: $json->workspaces ?? null, - ); - } - public function __construct( - /** - * Represents a smart lock [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - * - * An access code is a code used for a keypad or pinpad device. Unlike physical keys, which can easily be lost or duplicated, PIN codes can be customized, tracked, and altered on the fly. Using the Seam Access Code API, you can easily generate access codes on the hundreds of door lock models with which we integrate. - * - * Seam supports programming two types of access codes: [ongoing](https://docs.seam.co/low-level-apis/smart-locks/access-codes#ongoing-access-codes) and [time-bound](https://docs.seam.co/low-level-apis/smart-locks/access-codes#time-bound-access-codes). To differentiate between the two, refer to the `type` property of the access code. Ongoing codes display as `ongoing`, whereas time-bound codes are labeled `time_bound`. An ongoing access code is active, until it has been removed from the device. To specify an ongoing access code, leave both `starts_at` and `ends_at` empty. A time-bound access code will be programmed at the `starts_at` time and removed at the `ends_at` time. - * - * In addition, for certain devices, Seam also supports [offline access codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes#offline-access-codes). Offline access (PIN) codes are designed for door locks that might not always maintain an internet connection. For this type of access code, the device manufacturer uses encryption keys (tokens) to create server-based registries of algorithmically-generated offline PIN codes. Because the tokens remain synchronized with the managed devices, the locks do not require an active internet connection—and you do not need to be near the locks—to create an offline access code. Then, owners or managers can share these offline codes with users through a variety of mechanisms, such as messaging applications. That is, lock users do not need to install a smartphone application to receive an offline access code. - * - * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach and work across both standalone smart locks and access systems. Use the lower-level Access Codes API directly only when you specifically need to manage individual PIN codes. - */ - public mixed $access_codes, - /** - * 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. - */ - public mixed $access_grants, - /** - * 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. - */ - public mixed $access_methods, - /** - * 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. - * - * Some access control systems use [access group](https://docs.seam.co/low-level-apis/access-systems/user-management/assigning-users-to-access-groups), which are sets of users, combined with sets of permissions. These permissions include both the set of areas or assets that the users can access and the schedule during which the users can access these areas or assets. Instead of assigning access rights individually to each access control system user, which can be time-consuming and error-prone, administrators can assign users to an access group, thereby ensuring that the users inherit all the permissions associated with the access group. Using access groups streamlines the process of managing large numbers of access control system users, especially in bigger organizations or complexes. - * - * To learn whether your access control system supports access groups, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). - */ - public mixed $acs_access_groups, - /** - * Means by which an [access control system user](https://docs.seam.co/low-level-apis/access-systems/user-management) gains access at an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). The `acs_credential` object represents a [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) that provides an ACS user access within an [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * An access control system generally uses digital means of access to authorize a user trying to get through a specific entrance. Examples of credentials include plastic key cards, mobile keys, biometric identifiers, and PIN codes. The electronic nature of these credentials, as well as the fact that access is centralized, enables both the rapid provisioning and rescinding of access and the ability to compile access audit logs. - * - * For each `acs_credential`, you define the access method. You can also specify additional properties, such as a PIN code, depending on the credential type. - * - * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach. Use the lower-level ACS credential API directly only when you specifically need to manage individual credentials. - */ - public mixed $acs_credentials, - /** - * Represents a hardware device that encodes [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) data onto physical cards within an [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * Some access control systems require credentials to be encoded onto plastic key cards using a card encoder. This process involves the following two key steps: - * - * 1. Credential creation - * Configure the access parameters for the credential. - * 2. Card encoding - * Write the credential data onto the card using a compatible card encoder. - * - * Separately, the Seam API also supports card scanning, which enables you to scan and read the encoded data on a card. You can use this action to confirm consistency with access control system records or diagnose discrepancies if needed. - * - * See [Working with Card Encoders and Scanners](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - * - * To verify if your access control system requires a card encoder, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). - */ - public mixed $acs_encoders, - /** - * Represents an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) within an [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * In an access control system, an entrance is a secured door, gate, zone, or other method of entry. You can list details for all the `acs_entrance` resources in your workspace or get these details for a specific `acs_entrance`. You can also list all entrances associated with a specific credential, and you can list all credentials associated with a specific entrance. - */ - public mixed $acs_entrances, - /** - * Represents an [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * Within an `acs_system`, create [`acs_user`s](https://docs.seam.co/api/acs/users/object) and [`acs_credential`s](https://docs.seam.co/api/acs/credentials/object) to grant access to the `acs_user`s. - * - * For details about the resources associated with an access control system, see the [access control systems namespace](https://docs.seam.co/api/acs). - */ - public mixed $acs_systems, - /** - * Represents a [user](https://docs.seam.co/low-level-apis/access-systems/user-management) in an [access system](https://docs.seam.co/low-level-apis/access-systems). - * - * An access system user typically refers to an individual who requires access, like an employee or resident. Each user can possess multiple credentials that serve as their keys or identifiers for access. The type of credential can vary widely. For example, in the Salto system, a user can have a PIN code, a mobile app account, and a fob. In other platforms, it is not uncommon for a user to have more than one of the same credential type, such as multiple key cards. Additionally, these credentials can have a schedule or validity period. - * - * For details about how to configure users in your access system, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). - */ - public mixed $acs_users, - /** - * Represents an action attempt that enables you to keep track of the progress of your action that affects a physical device or system.actions against a device. Action attempts are useful because the physical world is intrinsically asynchronous. - * - * When you request for a device to perform an action, the Seam API immediately returns an action attempt object. In the background, the Seam API performs the action. - * - * See also [Action Attempts](https://docs.seam.co/core-concepts/action-attempts). - */ - public mixed $action_attempts, - /** - * Represents a [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). If you want to restrict your users' access to their own devices, use client sessions. - * - * You create each client session with a custom `user_identifier_key`. Normally, the `user_identifier_key` is a user ID that your application provides. - * - * When calling the Seam API from your backend using an API key, you can pass the `user_identifier_key` as a parameter to limit results to the associated client session. For example, `/devices/list?user_identifier_key=123` only returns devices associated with the client session created with the `user_identifier_key` `123`. - * - * A client session has a token that you can use with the Seam JavaScript SDK to make requests from the client (browser) directly to the Seam API. The token restricts the user's access to only the devices that they own. - * - * See also [Get Started with React](https://docs.seam.co/ui-components/overview/getting-started-with-seam-components/get-started-with-react-components-and-client-session-tokens). - */ - public mixed $client_sessions, - /** - * Represents a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews). - * - * Connect Webviews are fully-embedded client-side components that you add to your app. Your users interact with your embedded Connect Webviews to link their IoT device or system accounts to Seam. That is, Connect Webviews walk your users through the process of logging in to their device or system accounts. Seam handles all the authentication steps, and—once your user has completed the authorization through your app—you can access and control their devices or systems using the Seam API. - * - * Connect Webviews perform credential validation, multifactor authentication (when applicable), and error handling for each brand that Seam supports. Further, Connect Webviews work across all modern browsers and platforms, including Chrome, Safari, and Firefox. - * - * To enable a user to connect their device or system account to Seam through your app, first create a `connect_webview`. Once created, this `connect_webview` includes a URL that you can use to open an [iframe](https://www.w3schools.com/html/html_iframe.asp) or new window containing the Connect Webview for your user. - * - * When you create a Connect Webview, specify the desired provider category key in the `provider_category` parameter. Alternately, to specify a list of providers explicitly, use the `accepted_providers` parameter with a list of device provider keys. - * - * To list all providers within a category, use `/devices/list_device_providers` with the desired `provider_category` filter. To list all provider keys, use `/devices/list_device_providers` with no filters. - */ - public mixed $connect_webviews, - /** - * Represents a [connected account](https://docs.seam.co/core-concepts/connected-accounts). 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. - */ - public mixed $connected_accounts, - /** - * Represents a [device](https://docs.seam.co/core-concepts/devices) that has been connected to Seam. - */ - public mixed $devices, - /** - * Represents an event. Events let you know when something interesting happens in your workspace. For example, when a lock is unlocked, Seam creates a `lock.unlocked` event. When a device's battery level is low, Seam creates a `device.battery_low` event. - * - * As with other API resources, you can retrieve an individual event or a list of events. Seam also provides a separate webhook system for sending the event objects directly to an endpoint on your sever. Manage webhooks through [Seam Console](https://console.seam.co). You can also use the webhooks sandbox in Seam Console to see the different payloads for each event and test them against your own endpoints. - */ - public mixed $events, - /** - * Represents a Seam Instant Key. For issuing Bluetooth mobile keys, Instant Keys are the fastest way to share access. With a single API call, you can create a mobile key and send it through text or email or embed it in your own app. - * - * There’s no app to install, nor account to create. Your user just taps a link and gets a lightweight, native-feeling experience using iOS App Clip or Instant Apps on Android. Further, Instant Keys work offline, so even in areas with poor cellular or Wi-Fi, like elevator banks or concrete-walled hallways, the Instant Keys still work. - */ - public mixed $instant_keys, - /** - * Represents a [noise threshold](https://docs.seam.co/capability-guides/noise-sensors/configure-noise-threshold-settings) for a [noise sensor](https://docs.seam.co/capability-guides/noise-sensors). Thresholds represent the limits of noise tolerated at a property, which can be customized for each hour of the day. Each device has its own default thresholds, but you can use the Seam API to modify them. - */ - public mixed $noise_thresholds, - /** - * Represents a space that is a logical grouping of devices and entrances. You can assign access to an entire space, thereby making granting access more efficient. - */ - public mixed $spaces, - /** - * Represents a thermostat daily program, consisting of a set of periods, each of which has a starting time and the key that identifies the climate preset to apply at the starting time. - */ - public mixed $thermostat_daily_programs, - /** - * Represents a [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) that activates a configured [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) on a [thermostat](https://docs.seam.co/capability-guides/thermostats) at a specified starting time and deactivates the climate preset at a specified ending time. - */ - public mixed $thermostat_schedules, - /** - * Represents an [unmanaged smart lock access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes). - * - * An access code is a code used for a keypad or pinpad device. Unlike physical keys, which can easily be lost or duplicated, PIN codes can be customized, tracked, and altered on the fly. - * - * When you create an access code on a device in Seam, it is created as a managed access code. Access codes that exist on a device that were not created through Seam are considered unmanaged codes. We strictly limit the operations that can be performed on unmanaged codes. - * - * Prior to using Seam to manage your devices, you may have used another lock management system to manage the access codes on your devices. Where possible, we help you keep any existing access codes on devices and transition those codes to ones managed by your Seam workspace. - * - * Not all providers support unmanaged access codes. The following providers do not support unmanaged access codes: - * - * - [Kwikset](https://docs.seam.co/device-and-system-integration-guides/kwikset-locks) - */ - public mixed $unmanaged_access_codes, - /** - * Represents an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). 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](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) on an unmanaged device are unmanaged. To control an unmanaged device with Seam, [convert it to a managed device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices#convert-an-unmanaged-device-to-managed). - */ - public mixed $unmanaged_devices, - /** - * Represents a [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with an application user account. - */ - public mixed $user_identities, - /** - * Represents a Seam [workspace](https://docs.seam.co/core-concepts/workspaces). A workspace is a top-level entity that encompasses all other resources below it, such as devices, connected accounts, and Connect Webviews. Seam provides two types of workspaces. A [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is a special type of workspace designed for testing code. Sandbox workspaces offer test device accounts and virtual devices that you can connect and control. This ability to work with virtual devices is quite handy because it removes the need to own physical devices from multiple brands. To connect real devices and systems to Seam, use a [production workspace](https://docs.seam.co/core-concepts/workspaces#production-workspaces). - */ - public mixed $workspaces, - ) {} + public function __construct( + /** + * Represents a smart lock [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * + * An access code is a code used for a keypad or pinpad device. Unlike physical keys, which can easily be lost or duplicated, PIN codes can be customized, tracked, and altered on the fly. Using the Seam Access Code API, you can easily generate access codes on the hundreds of door lock models with which we integrate. + * + * Seam supports programming two types of access codes: [ongoing](https://docs.seam.co/low-level-apis/smart-locks/access-codes#ongoing-access-codes) and [time-bound](https://docs.seam.co/low-level-apis/smart-locks/access-codes#time-bound-access-codes). To differentiate between the two, refer to the `type` property of the access code. Ongoing codes display as `ongoing`, whereas time-bound codes are labeled `time_bound`. An ongoing access code is active, until it has been removed from the device. To specify an ongoing access code, leave both `starts_at` and `ends_at` empty. A time-bound access code will be programmed at the `starts_at` time and removed at the `ends_at` time. + * + * In addition, for certain devices, Seam also supports [offline access codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes#offline-access-codes). Offline access (PIN) codes are designed for door locks that might not always maintain an internet connection. For this type of access code, the device manufacturer uses encryption keys (tokens) to create server-based registries of algorithmically-generated offline PIN codes. Because the tokens remain synchronized with the managed devices, the locks do not require an active internet connection—and you do not need to be near the locks—to create an offline access code. Then, owners or managers can share these offline codes with users through a variety of mechanisms, such as messaging applications. That is, lock users do not need to install a smartphone application to receive an offline access code. + * + * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach and work across both standalone smart locks and access systems. Use the lower-level Access Codes API directly only when you specifically need to manage individual PIN codes. + */ + public mixed $access_codes, + /** + * 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. + */ + public mixed $access_grants, + /** + * 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. + */ + public mixed $access_methods, + /** + * 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. + * + * Some access control systems use [access group](https://docs.seam.co/low-level-apis/access-systems/user-management/assigning-users-to-access-groups), which are sets of users, combined with sets of permissions. These permissions include both the set of areas or assets that the users can access and the schedule during which the users can access these areas or assets. Instead of assigning access rights individually to each access control system user, which can be time-consuming and error-prone, administrators can assign users to an access group, thereby ensuring that the users inherit all the permissions associated with the access group. Using access groups streamlines the process of managing large numbers of access control system users, especially in bigger organizations or complexes. + * + * To learn whether your access control system supports access groups, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). + */ + public mixed $acs_access_groups, + /** + * Means by which an [access control system user](https://docs.seam.co/low-level-apis/access-systems/user-management) gains access at an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). The `acs_credential` object represents a [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) that provides an ACS user access within an [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * An access control system generally uses digital means of access to authorize a user trying to get through a specific entrance. Examples of credentials include plastic key cards, mobile keys, biometric identifiers, and PIN codes. The electronic nature of these credentials, as well as the fact that access is centralized, enables both the rapid provisioning and rescinding of access and the ability to compile access audit logs. + * + * For each `acs_credential`, you define the access method. You can also specify additional properties, such as a PIN code, depending on the credential type. + * + * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach. Use the lower-level ACS credential API directly only when you specifically need to manage individual credentials. + */ + public mixed $acs_credentials, + /** + * Represents a hardware device that encodes [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) data onto physical cards within an [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * Some access control systems require credentials to be encoded onto plastic key cards using a card encoder. This process involves the following two key steps: + * + * 1. Credential creation + * Configure the access parameters for the credential. + * 2. Card encoding + * Write the credential data onto the card using a compatible card encoder. + * + * Separately, the Seam API also supports card scanning, which enables you to scan and read the encoded data on a card. You can use this action to confirm consistency with access control system records or diagnose discrepancies if needed. + * + * See [Working with Card Encoders and Scanners](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + * + * To verify if your access control system requires a card encoder, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). + */ + public mixed $acs_encoders, + /** + * Represents an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) within an [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * In an access control system, an entrance is a secured door, gate, zone, or other method of entry. You can list details for all the `acs_entrance` resources in your workspace or get these details for a specific `acs_entrance`. You can also list all entrances associated with a specific credential, and you can list all credentials associated with a specific entrance. + */ + public mixed $acs_entrances, + /** + * Represents an [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * Within an `acs_system`, create [`acs_user`s](https://docs.seam.co/api/acs/users/object) and [`acs_credential`s](https://docs.seam.co/api/acs/credentials/object) to grant access to the `acs_user`s. + * + * For details about the resources associated with an access control system, see the [access control systems namespace](https://docs.seam.co/api/acs). + */ + public mixed $acs_systems, + /** + * Represents a [user](https://docs.seam.co/low-level-apis/access-systems/user-management) in an [access system](https://docs.seam.co/low-level-apis/access-systems). + * + * An access system user typically refers to an individual who requires access, like an employee or resident. Each user can possess multiple credentials that serve as their keys or identifiers for access. The type of credential can vary widely. For example, in the Salto system, a user can have a PIN code, a mobile app account, and a fob. In other platforms, it is not uncommon for a user to have more than one of the same credential type, such as multiple key cards. Additionally, these credentials can have a schedule or validity period. + * + * For details about how to configure users in your access system, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). + */ + public mixed $acs_users, + /** + * Represents an action attempt that enables you to keep track of the progress of your action that affects a physical device or system.actions against a device. Action attempts are useful because the physical world is intrinsically asynchronous. + * + * When you request for a device to perform an action, the Seam API immediately returns an action attempt object. In the background, the Seam API performs the action. + * + * See also [Action Attempts](https://docs.seam.co/core-concepts/action-attempts). + */ + public mixed $action_attempts, + /** + * Represents a [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). If you want to restrict your users' access to their own devices, use client sessions. + * + * You create each client session with a custom `user_identifier_key`. Normally, the `user_identifier_key` is a user ID that your application provides. + * + * When calling the Seam API from your backend using an API key, you can pass the `user_identifier_key` as a parameter to limit results to the associated client session. For example, `/devices/list?user_identifier_key=123` only returns devices associated with the client session created with the `user_identifier_key` `123`. + * + * A client session has a token that you can use with the Seam JavaScript SDK to make requests from the client (browser) directly to the Seam API. The token restricts the user's access to only the devices that they own. + * + * See also [Get Started with React](https://docs.seam.co/ui-components/overview/getting-started-with-seam-components/get-started-with-react-components-and-client-session-tokens). + */ + public mixed $client_sessions, + /** + * Represents a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews). + * + * Connect Webviews are fully-embedded client-side components that you add to your app. Your users interact with your embedded Connect Webviews to link their IoT device or system accounts to Seam. That is, Connect Webviews walk your users through the process of logging in to their device or system accounts. Seam handles all the authentication steps, and—once your user has completed the authorization through your app—you can access and control their devices or systems using the Seam API. + * + * Connect Webviews perform credential validation, multifactor authentication (when applicable), and error handling for each brand that Seam supports. Further, Connect Webviews work across all modern browsers and platforms, including Chrome, Safari, and Firefox. + * + * To enable a user to connect their device or system account to Seam through your app, first create a `connect_webview`. Once created, this `connect_webview` includes a URL that you can use to open an [iframe](https://www.w3schools.com/html/html_iframe.asp) or new window containing the Connect Webview for your user. + * + * When you create a Connect Webview, specify the desired provider category key in the `provider_category` parameter. Alternately, to specify a list of providers explicitly, use the `accepted_providers` parameter with a list of device provider keys. + * + * To list all providers within a category, use `/devices/list_device_providers` with the desired `provider_category` filter. To list all provider keys, use `/devices/list_device_providers` with no filters. + */ + public mixed $connect_webviews, + /** + * Represents a [connected account](https://docs.seam.co/core-concepts/connected-accounts). 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. + */ + public mixed $connected_accounts, + /** + * Represents a [device](https://docs.seam.co/core-concepts/devices) that has been connected to Seam. + */ + public mixed $devices, + /** + * Represents an event. Events let you know when something interesting happens in your workspace. For example, when a lock is unlocked, Seam creates a `lock.unlocked` event. When a device's battery level is low, Seam creates a `device.battery_low` event. + * + * As with other API resources, you can retrieve an individual event or a list of events. Seam also provides a separate webhook system for sending the event objects directly to an endpoint on your sever. Manage webhooks through [Seam Console](https://console.seam.co). You can also use the webhooks sandbox in Seam Console to see the different payloads for each event and test them against your own endpoints. + */ + public mixed $events, + /** + * Represents a Seam Instant Key. For issuing Bluetooth mobile keys, Instant Keys are the fastest way to share access. With a single API call, you can create a mobile key and send it through text or email or embed it in your own app. + * + * There’s no app to install, nor account to create. Your user just taps a link and gets a lightweight, native-feeling experience using iOS App Clip or Instant Apps on Android. Further, Instant Keys work offline, so even in areas with poor cellular or Wi-Fi, like elevator banks or concrete-walled hallways, the Instant Keys still work. + */ + public mixed $instant_keys, + /** + * Represents a [noise threshold](https://docs.seam.co/capability-guides/noise-sensors/configure-noise-threshold-settings) for a [noise sensor](https://docs.seam.co/capability-guides/noise-sensors). Thresholds represent the limits of noise tolerated at a property, which can be customized for each hour of the day. Each device has its own default thresholds, but you can use the Seam API to modify them. + */ + public mixed $noise_thresholds, + /** + * Represents a space that is a logical grouping of devices and entrances. You can assign access to an entire space, thereby making granting access more efficient. + */ + public mixed $spaces, + /** + * Represents a thermostat daily program, consisting of a set of periods, each of which has a starting time and the key that identifies the climate preset to apply at the starting time. + */ + public mixed $thermostat_daily_programs, + /** + * Represents a [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) that activates a configured [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) on a [thermostat](https://docs.seam.co/capability-guides/thermostats) at a specified starting time and deactivates the climate preset at a specified ending time. + */ + public mixed $thermostat_schedules, + /** + * Represents an [unmanaged smart lock access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes). + * + * An access code is a code used for a keypad or pinpad device. Unlike physical keys, which can easily be lost or duplicated, PIN codes can be customized, tracked, and altered on the fly. + * + * When you create an access code on a device in Seam, it is created as a managed access code. Access codes that exist on a device that were not created through Seam are considered unmanaged codes. We strictly limit the operations that can be performed on unmanaged codes. + * + * Prior to using Seam to manage your devices, you may have used another lock management system to manage the access codes on your devices. Where possible, we help you keep any existing access codes on devices and transition those codes to ones managed by your Seam workspace. + * + * Not all providers support unmanaged access codes. The following providers do not support unmanaged access codes: + * + * - [Kwikset](https://docs.seam.co/device-and-system-integration-guides/kwikset-locks) + */ + public mixed $unmanaged_access_codes, + /** + * Represents an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). 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](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) on an unmanaged device are unmanaged. To control an unmanaged device with Seam, [convert it to a managed device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices#convert-an-unmanaged-device-to-managed). + */ + public mixed $unmanaged_devices, + /** + * Represents a [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with an application user account. + */ + public mixed $user_identities, + /** + * Represents a Seam [workspace](https://docs.seam.co/core-concepts/workspaces). A workspace is a top-level entity that encompasses all other resources below it, such as devices, connected accounts, and Connect Webviews. Seam provides two types of workspaces. A [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is a special type of workspace designed for testing code. Sandbox workspaces offer test device accounts and virtual devices that you can connect and control. This ability to work with virtual devices is quite handy because it removes the need to own physical devices from multiple brands. To connect real devices and systems to Seam, use a [production workspace](https://docs.seam.co/core-concepts/workspaces#production-workspaces). + */ + public mixed $workspaces, + ) {} + } } diff --git a/src/Resources/ClientSession.php b/src/Resources/ClientSession.php index 92c77b9d..53ec9f7e 100644 --- a/src/Resources/ClientSession.php +++ b/src/Resources/ClientSession.php @@ -1,91 +1,91 @@ client_session_id ?? null, + connect_webview_ids: $json->connect_webview_ids ?? null, + connected_account_ids: $json->connected_account_ids ?? null, + created_at: $json->created_at ?? null, + customer_key: $json->customer_key ?? null, + device_count: $json->device_count ?? null, + expires_at: $json->expires_at ?? null, + token: $json->token ?? null, + user_identifier_key: $json->user_identifier_key ?? null, + user_identity_id: $json->user_identity_id ?? null, + user_identity_ids: $json->user_identity_ids ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - client_session_id: $json->client_session_id ?? null, - connect_webview_ids: $json->connect_webview_ids ?? null, - connected_account_ids: $json->connected_account_ids ?? null, - created_at: $json->created_at ?? null, - customer_key: $json->customer_key ?? null, - device_count: $json->device_count ?? null, - expires_at: $json->expires_at ?? null, - token: $json->token ?? null, - user_identifier_key: $json->user_identifier_key ?? null, - user_identity_id: $json->user_identity_id ?? null, - user_identity_ids: $json->user_identity_ids ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the client session. - */ - public string|null $client_session_id, - /** - * IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public array|null $connect_webview_ids, - /** - * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public array|null $connected_account_ids, - /** - * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) was created. - */ - public string|null $created_at, - /** - * Customer key associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public string|null $customer_key, - /** - * Number of devices associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public float|null $device_count, - /** - * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) expires. - */ - public string|null $expires_at, - /** - * Client session token associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public string|null $token, - /** - * Your user ID for the user associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public string|null $user_identifier_key, - /** - * ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. - */ - public string|null $user_identity_id, - /** - * IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. - * - * @deprecated Use `user_identity_id` instead. - */ - public array|null $user_identity_ids, - /** - * ID of the workspace associated with the client session. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the client session. + */ + public string|null $client_session_id, + /** + * IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public array|null $connect_webview_ids, + /** + * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public array|null $connected_account_ids, + /** + * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) was created. + */ + public string|null $created_at, + /** + * Customer key associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public string|null $customer_key, + /** + * Number of devices associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public float|null $device_count, + /** + * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) expires. + */ + public string|null $expires_at, + /** + * Client session token associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public string|null $token, + /** + * Your user ID for the user associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public string|null $user_identifier_key, + /** + * ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. + */ + public string|null $user_identity_id, + /** + * IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. + * + * @deprecated Use `user_identity_id` instead. + */ + public array|null $user_identity_ids, + /** + * ID of the workspace associated with the client session. + */ + public string|null $workspace_id, + ) {} + } } diff --git a/src/Resources/ConnectWebview.php b/src/Resources/ConnectWebview.php index 55a02bd2..224eb740 100644 --- a/src/Resources/ConnectWebview.php +++ b/src/Resources/ConnectWebview.php @@ -1,128 +1,129 @@ accepted_capabilities ?? null, + accepted_providers: $json->accepted_providers ?? null, + any_provider_allowed: $json->any_provider_allowed ?? null, + authorized_at: $json->authorized_at ?? null, + automatically_manage_new_devices: $json->automatically_manage_new_devices ?? + null, + connect_webview_id: $json->connect_webview_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + custom_redirect_failure_url: $json->custom_redirect_failure_url ?? + null, + custom_redirect_url: $json->custom_redirect_url ?? null, + customer_key: $json->customer_key ?? null, + device_selection_mode: $json->device_selection_mode ?? null, + login_successful: $json->login_successful ?? null, + selected_provider: $json->selected_provider ?? null, + status: $json->status ?? null, + url: $json->url ?? null, + wait_for_device_creation: $json->wait_for_device_creation ?? + null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - accepted_capabilities: $json->accepted_capabilities ?? null, - accepted_providers: $json->accepted_providers ?? null, - any_provider_allowed: $json->any_provider_allowed ?? null, - authorized_at: $json->authorized_at ?? null, - automatically_manage_new_devices: $json->automatically_manage_new_devices ?? - null, - connect_webview_id: $json->connect_webview_id ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - custom_redirect_failure_url: $json->custom_redirect_failure_url ?? - null, - custom_redirect_url: $json->custom_redirect_url ?? null, - customer_key: $json->customer_key ?? null, - device_selection_mode: $json->device_selection_mode ?? null, - login_successful: $json->login_successful ?? null, - selected_provider: $json->selected_provider ?? null, - status: $json->status ?? null, - url: $json->url ?? null, - wait_for_device_creation: $json->wait_for_device_creation ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * High-level device capabilities that the Connect Webview can accept. When creating a Connect Webview, you can specify the types of devices that it can connect to Seam. If you do not set custom `accepted_capabilities`, Seam uses a default set of `accepted_capabilities` for each provider. For example, if you create a Connect Webview that accepts SmartThing devices, without specifying `accepted_capabilities`, Seam accepts only SmartThings locks. To connect SmartThings thermostats and locks to Seam, create a Connect Webview and include both `thermostat` and `lock` in the `accepted_capabilities`. - */ - public array|null $accepted_capabilities, - /** - * List of accepted [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). - */ - public array|null $accepted_providers, - /** - * Indicates whether any provider is allowed. - */ - public bool|null $any_provider_allowed, - /** - * Date and time at which the user authorized (through the Connect Webview) the management of their devices. - */ - public string|null $authorized_at, - /** - * Indicates whether Seam should [import all new devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#automatically_manage_new_devices) for the connected account to make these devices available for use and management by the Seam API. - */ - public bool|null $automatically_manage_new_devices, - /** - * ID of the Connect Webview. - */ - public string|null $connect_webview_id, - /** - * ID of the connected account associated with the Connect Webview. - */ - public string|null $connected_account_id, - /** - * Date and time at which the Connect Webview was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - */ - public mixed $custom_metadata, - /** - * URL to which the Connect Webview should redirect when an unexpected error occurs. - */ - public string|null $custom_redirect_failure_url, - /** - * URL to which the Connect Webview should redirect when the user successfully pairs a device or system. If you do not set the `custom_redirect_failure_url`, the Connect Webview redirects to the `custom_redirect_url` when an unexpected error occurs. - */ - public string|null $custom_redirect_url, - /** - * The customer key associated with this webview, if any. - */ - public string|null $customer_key, - /** - * Device selection mode of the Connect Webview. Supported values: `none`, `single`, `multiple`. - */ - public string|null $device_selection_mode, - /** - * Indicates whether the user logged in successfully using the Connect Webview. - */ - public bool|null $login_successful, - /** - * Selected provider of the Connect Webview, one of the [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). - */ - public string|null $selected_provider, - /** - * Status of the Connect Webview. `authorized` indicates that the user has successfully logged into their device or system account, thereby completing the Connect Webview. - */ - public string|null $status, - /** - * URL for the Connect Webview. You use the URL to display the Connect Webview flow to your user. - */ - public string|null $url, - /** - * Indicates whether Seam should [finish syncing all devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#wait_for_device_creation) in a newly-connected account before completing the associated Connect Webview. - */ - public bool|null $wait_for_device_creation, - /** - * ID of the workspace that contains the Connect Webview. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * High-level device capabilities that the Connect Webview can accept. When creating a Connect Webview, you can specify the types of devices that it can connect to Seam. If you do not set custom `accepted_capabilities`, Seam uses a default set of `accepted_capabilities` for each provider. For example, if you create a Connect Webview that accepts SmartThing devices, without specifying `accepted_capabilities`, Seam accepts only SmartThings locks. To connect SmartThings thermostats and locks to Seam, create a Connect Webview and include both `thermostat` and `lock` in the `accepted_capabilities`. + */ + public array|null $accepted_capabilities, + /** + * List of accepted [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). + */ + public array|null $accepted_providers, + /** + * Indicates whether any provider is allowed. + */ + public bool|null $any_provider_allowed, + /** + * Date and time at which the user authorized (through the Connect Webview) the management of their devices. + */ + public string|null $authorized_at, + /** + * Indicates whether Seam should [import all new devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#automatically_manage_new_devices) for the connected account to make these devices available for use and management by the Seam API. + */ + public bool|null $automatically_manage_new_devices, + /** + * ID of the Connect Webview. + */ + public string|null $connect_webview_id, + /** + * ID of the connected account associated with the Connect Webview. + */ + public string|null $connected_account_id, + /** + * Date and time at which the Connect Webview was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + */ + public mixed $custom_metadata, + /** + * URL to which the Connect Webview should redirect when an unexpected error occurs. + */ + public string|null $custom_redirect_failure_url, + /** + * URL to which the Connect Webview should redirect when the user successfully pairs a device or system. If you do not set the `custom_redirect_failure_url`, the Connect Webview redirects to the `custom_redirect_url` when an unexpected error occurs. + */ + public string|null $custom_redirect_url, + /** + * The customer key associated with this webview, if any. + */ + public string|null $customer_key, + /** + * Device selection mode of the Connect Webview. Supported values: `none`, `single`, `multiple`. + */ + public string|null $device_selection_mode, + /** + * Indicates whether the user logged in successfully using the Connect Webview. + */ + public bool|null $login_successful, + /** + * Selected provider of the Connect Webview, one of the [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). + */ + public string|null $selected_provider, + /** + * Status of the Connect Webview. `authorized` indicates that the user has successfully logged into their device or system account, thereby completing the Connect Webview. + */ + public string|null $status, + /** + * URL for the Connect Webview. You use the URL to display the Connect Webview flow to your user. + */ + public string|null $url, + /** + * Indicates whether Seam should [finish syncing all devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#wait_for_device_creation) in a newly-connected account before completing the associated Connect Webview. + */ + public bool|null $wait_for_device_creation, + /** + * ID of the workspace that contains the Connect Webview. + */ + public string|null $workspace_id, + ) {} + } } diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index 245d3c25..3f6e237b 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -1,330 +1,407 @@ accepted_capabilities ?? null, + account_type: $json->account_type ?? null, + account_type_display_name: $json->account_type_display_name ?? + null, + automatically_manage_new_devices: $json->automatically_manage_new_devices ?? + null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + customer_key: $json->customer_key ?? null, + default_checkin_time: $json->default_checkin_time ?? null, + default_checkout_time: $json->default_checkout_time ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => ConnectedAccount\Errors::from_json($e), + $json->errors ?? [], + ), + ical_feed_origin: $json->ical_feed_origin ?? null, + ical_url: $json->ical_url ?? null, + image_url: $json->image_url ?? null, + time_zone: $json->time_zone ?? null, + user_identifier: isset($json->user_identifier) + ? ConnectedAccount\UserIdentifier::from_json( + $json->user_identifier, + ) + : null, + warnings: array_map( + fn($w) => ConnectedAccount\Warnings::from_json($w), + $json->warnings ?? [], + ), + ); } - return new self( - accepted_capabilities: $json->accepted_capabilities ?? null, - account_type: $json->account_type ?? null, - account_type_display_name: $json->account_type_display_name ?? null, - automatically_manage_new_devices: $json->automatically_manage_new_devices ?? - null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - customer_key: $json->customer_key ?? null, - default_checkin_time: $json->default_checkin_time ?? null, - default_checkout_time: $json->default_checkout_time ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => ConnectedAccountErrors::from_json($e), - $json->errors ?? [], - ), - ical_feed_origin: $json->ical_feed_origin ?? null, - ical_url: $json->ical_url ?? null, - image_url: $json->image_url ?? null, - time_zone: $json->time_zone ?? null, - user_identifier: isset($json->user_identifier) - ? ConnectedAccountUserIdentifier::from_json( - $json->user_identifier, - ) - : null, - warnings: array_map( - fn($w) => ConnectedAccountWarnings::from_json($w), - $json->warnings ?? [], - ), - ); - } - public function __construct( - /** - * List of capabilities that were accepted during the account connection process. - */ - public array|null $accepted_capabilities, - /** - * Type of connected account. - */ - public string|null $account_type, - /** - * Display name for the connected account type. - */ - public string|null $account_type_display_name, - /** - * Indicates whether Seam should [import all new devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#automatically_manage_new_devices) for the connected account to make these devices available for management by the Seam API. - */ - public bool|null $automatically_manage_new_devices, - /** - * ID of the connected account. - */ - public string|null $connected_account_id, - /** - * Date and time at which the connected account was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - */ - public mixed $custom_metadata, - /** - * Your unique key for the customer associated with this connected account. - */ - public string|null $customer_key, - /** - * Default reservation check-in time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration — set during the connect_webview for providers like Lodgify whose API does not expose check-in times. - */ - public string|null $default_checkin_time, - /** - * Default reservation check-out time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration. - */ - public string|null $default_checkout_time, - /** - * Display name for the connected account. - */ - public string|null $display_name, - /** - * Errors associated with the connected account. - */ - public array $errors, - /** - * For iCal connected accounts, the platform that produced the feed (for example, `airbnb`, `vrbo`, or `booking`), or `unknown` when it could not be determined. Intended for rendering the source platform's logo. - */ - public string|null $ical_feed_origin, - /** - * For iCal connected accounts, the feed URL for the connection. Sourced from the connector configuration. - */ - public string|null $ical_url, - /** - * Logo URL for the connected account provider. - */ - public string|null $image_url, - /** - * IANA time zone (e.g. America/Los_Angeles) for this connected account. Sourced from the connector configuration. - */ - public string|null $time_zone, - /** - * User identifier associated with the connected account. - * - * @deprecated Use `display_name` instead. - */ - public ConnectedAccountUserIdentifier|null $user_identifier, - /** - * Warnings associated with the connected account. - */ - public array $warnings, - ) {} + public function __construct( + /** + * List of capabilities that were accepted during the account connection process. + */ + public array|null $accepted_capabilities, + /** + * Type of connected account. + */ + public string|null $account_type, + /** + * Display name for the connected account type. + */ + public string|null $account_type_display_name, + /** + * Indicates whether Seam should [import all new devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#automatically_manage_new_devices) for the connected account to make these devices available for management by the Seam API. + */ + public bool|null $automatically_manage_new_devices, + /** + * ID of the connected account. + */ + public string|null $connected_account_id, + /** + * Date and time at which the connected account was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + */ + public mixed $custom_metadata, + /** + * Your unique key for the customer associated with this connected account. + */ + public string|null $customer_key, + /** + * Default reservation check-in time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration — set during the connect_webview for providers like Lodgify whose API does not expose check-in times. + */ + public string|null $default_checkin_time, + /** + * Default reservation check-out time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration. + */ + public string|null $default_checkout_time, + /** + * Display name for the connected account. + */ + public string|null $display_name, + /** + * Errors associated with the connected account. + */ + public array $errors, + /** + * For iCal connected accounts, the platform that produced the feed (for example, `airbnb`, `vrbo`, or `booking`), or `unknown` when it could not be determined. Intended for rendering the source platform's logo. + */ + public string|null $ical_feed_origin, + /** + * For iCal connected accounts, the feed URL for the connection. Sourced from the connector configuration. + */ + public string|null $ical_url, + /** + * Logo URL for the connected account provider. + */ + public string|null $image_url, + /** + * IANA time zone (e.g. America/Los_Angeles) for this connected account. Sourced from the connector configuration. + */ + public string|null $time_zone, + /** + * User identifier associated with the connected account. + * + * @deprecated Use `display_name` instead. + */ + public ConnectedAccount\UserIdentifier|null $user_identifier, + /** + * Warnings associated with the connected account. + */ + public array $warnings, + ) {} + } } -/** - * Errors associated with the connected account. - */ -class ConnectedAccountErrors -{ - public static function from_json(mixed $json): ConnectedAccountErrors|null +namespace Seam\Resources\ConnectedAccount { + /** + * Errors associated with the connected account. + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + message: $json->message ?? null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? Errors\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - message: $json->message ?? null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? ConnectedAccountSaltoKsMetadata::from_json( - $json->salto_ks_metadata, - ) - : null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates whether the error is related specifically to the connected account. - */ - public bool|null $is_connected_account_error, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Salto KS metadata associated with the connected account that has an error. - */ - public ConnectedAccountSaltoKsMetadata|null $salto_ks_metadata, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + /** + * Indicates whether the error is related specifically to the connected account. + */ + public bool|null $is_connected_account_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Salto KS metadata associated with the connected account that has an error. + */ + public Errors\SaltoKsMetadata|null $salto_ks_metadata, + ) {} + } -/** - * Salto KS metadata associated with the connected account that has an error. - */ -class ConnectedAccountSaltoKsMetadata -{ - public static function from_json( - mixed $json, - ): ConnectedAccountSaltoKsMetadata|null { - if (!$json) { - return null; + /** + * User identifier associated with the connected account. + * + * @deprecated Use `display_name` instead. + */ + class UserIdentifier + { + public static function from_json(mixed $json): UserIdentifier|null + { + if (!$json) { + return null; + } + return new self( + api_url: $json->api_url ?? null, + email: $json->email ?? null, + exclusive: $json->exclusive ?? null, + phone: $json->phone ?? null, + username: $json->username ?? null, + ); } - return new self( - sites: array_map( - fn($s) => ConnectedAccountSites::from_json($s), - $json->sites ?? [], - ), - ); + + public function __construct( + /** + * API URL for the user identifier associated with the connected account. + */ + public string|null $api_url, + /** + * Email address of the user identifier associated with the connected account. + */ + public string|null $email, + /** + * Indicates whether the user identifier associated with the connected account is exclusive. + */ + public bool|null $exclusive, + /** + * Phone number of the user identifier associated with the connected account. + */ + public string|null $phone, + /** + * Username of the user identifier associated with the connected account. + */ + public string|null $username, + ) {} } - public function __construct( - /** - * Salto sites associated with the connected account that has an error. - */ - public array $sites, - ) {} + /** + * Warnings associated with the connected account. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? Warnings\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Salto KS metadata associated with the connected account that has a warning. + */ + public Warnings\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Salto sites associated with the connected account that has an error. - */ -class ConnectedAccountSites -{ - public static function from_json(mixed $json): ConnectedAccountSites|null +namespace Seam\Resources\ConnectedAccount\Errors { + /** + * Salto KS metadata associated with the connected account that has an error. + */ + class SaltoKsMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self( + sites: array_map( + fn($s) => SaltoKsMetadata\Sites::from_json($s), + $json->sites ?? [], + ), + ); } - return new self( - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - site_user_subscription_limit: $json->site_user_subscription_limit ?? - null, - subscribed_site_user_count: $json->subscribed_site_user_count ?? - null, - ); - } - public function __construct( - /** - * ID of a Salto site associated with the connected account that has an error. - */ - public string|null $site_id, - /** - * Name of a Salto site associated with the connected account that has an error. - */ - public string|null $site_name, - /** - * Subscription limit of site users for a Salto site associated with the connected account that has an error. - */ - public int|null $site_user_subscription_limit, - /** - * Count of subscribed site users for a Salto site associated with the connected account that has an error. - */ - public int|null $subscribed_site_user_count, - ) {} + public function __construct( + /** + * Salto sites associated with the connected account that has an error. + */ + public array $sites, + ) {} + } } -/** - * User identifier associated with the connected account. - */ -class ConnectedAccountUserIdentifier -{ - public static function from_json( - mixed $json, - ): ConnectedAccountUserIdentifier|null { - if (!$json) { - return null; +namespace Seam\Resources\ConnectedAccount\Errors\SaltoKsMetadata { + /** + * Salto sites associated with the connected account that has an error. + */ + class Sites + { + public static function from_json(mixed $json): Sites|null + { + if (!$json) { + return null; + } + return new self( + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + site_user_subscription_limit: $json->site_user_subscription_limit ?? + null, + subscribed_site_user_count: $json->subscribed_site_user_count ?? + null, + ); } - return new self( - api_url: $json->api_url ?? null, - email: $json->email ?? null, - exclusive: $json->exclusive ?? null, - phone: $json->phone ?? null, - username: $json->username ?? null, - ); - } - public function __construct( - /** - * API URL for the user identifier associated with the connected account. - */ - public string|null $api_url, - /** - * Email address of the user identifier associated with the connected account. - */ - public string|null $email, - /** - * Indicates whether the user identifier associated with the connected account is exclusive. - */ - public bool|null $exclusive, - /** - * Phone number of the user identifier associated with the connected account. - */ - public string|null $phone, - /** - * Username of the user identifier associated with the connected account. - */ - public string|null $username, - ) {} + public function __construct( + /** + * ID of a Salto site associated with the connected account that has an error. + */ + public string|null $site_id, + /** + * Name of a Salto site associated with the connected account that has an error. + */ + public string|null $site_name, + /** + * Subscription limit of site users for a Salto site associated with the connected account that has an error. + */ + public int|null $site_user_subscription_limit, + /** + * Count of subscribed site users for a Salto site associated with the connected account that has an error. + */ + public int|null $subscribed_site_user_count, + ) {} + } } -/** - * Warnings associated with the connected account. - */ -class ConnectedAccountWarnings -{ - public static function from_json(mixed $json): ConnectedAccountWarnings|null +namespace Seam\Resources\ConnectedAccount\Warnings { + /** + * Salto KS metadata associated with the connected account that has a warning. + */ + class SaltoKsMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self( + sites: array_map( + fn($s) => SaltoKsMetadata\Sites::from_json($s), + $json->sites ?? [], + ), + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? ConnectedAccountSaltoKsMetadata::from_json( - $json->salto_ks_metadata, - ) - : null, - warning_code: $json->warning_code ?? null, - ); + + public function __construct( + /** + * Salto sites associated with the connected account that has a warning. + */ + public array $sites, + ) {} } +} - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Salto KS metadata associated with the connected account that has a warning. - */ - public ConnectedAccountSaltoKsMetadata|null $salto_ks_metadata, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} +namespace Seam\Resources\ConnectedAccount\Warnings\SaltoKsMetadata { + /** + * Salto sites associated with the connected account that has a warning. + */ + class Sites + { + public static function from_json(mixed $json): Sites|null + { + if (!$json) { + return null; + } + return new self( + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + site_user_subscription_limit: $json->site_user_subscription_limit ?? + null, + subscribed_site_user_count: $json->subscribed_site_user_count ?? + null, + ); + } + + public function __construct( + /** + * ID of a Salto site associated with the connected account that has a warning. + */ + public string|null $site_id, + /** + * Name of a Salto site associated with the connected account that has a warning. + */ + public string|null $site_name, + /** + * Subscription limit of site users for a Salto site associated with the connected account that has a warning. + */ + public int|null $site_user_subscription_limit, + /** + * Count of subscribed site users for a Salto site associated with the connected account that has a warning. + */ + public int|null $subscribed_site_user_count, + ) {} + } } diff --git a/src/Resources/CustomerPortal.php b/src/Resources/CustomerPortal.php index 89029d27..0822f0ff 100644 --- a/src/Resources/CustomerPortal.php +++ b/src/Resources/CustomerPortal.php @@ -1,50 +1,50 @@ created_at ?? null, + customer_key: $json->customer_key ?? null, + expires_at: $json->expires_at ?? null, + url: $json->url ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - customer_key: $json->customer_key ?? null, - expires_at: $json->expires_at ?? null, - url: $json->url ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Date and time at which the customer portal link was created. - */ - public string|null $created_at, - /** - * Customer key for the customer portal. - */ - public string|null $customer_key, - /** - * Date and time at which the customer portal link expires. - */ - public string|null $expires_at, - /** - * URL for the customer portal. - */ - public string|null $url, - /** - * ID of the workspace associated with the customer portal. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Date and time at which the customer portal link was created. + */ + public string|null $created_at, + /** + * Customer key for the customer portal. + */ + public string|null $customer_key, + /** + * Date and time at which the customer portal link expires. + */ + public string|null $expires_at, + /** + * URL for the customer portal. + */ + public string|null $url, + /** + * ID of the workspace associated with the customer portal. + */ + public string|null $workspace_id, + ) {} + } } diff --git a/src/Resources/Device.php b/src/Resources/Device.php index 2b6d428c..9b01f398 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -1,4092 +1,4397 @@ can_configure_auto_lock ?? null, - can_hvac_cool: $json->can_hvac_cool ?? null, - can_hvac_heat: $json->can_hvac_heat ?? null, - can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, - can_program_offline_access_codes: $json->can_program_offline_access_codes ?? - null, - can_program_online_access_codes: $json->can_program_online_access_codes ?? - null, - can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? - null, - can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? - null, - can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? - null, - can_remotely_lock: $json->can_remotely_lock ?? null, - can_remotely_unlock: $json->can_remotely_unlock ?? null, - can_run_thermostat_programs: $json->can_run_thermostat_programs ?? - null, - can_simulate_connection: $json->can_simulate_connection ?? null, - can_simulate_disconnection: $json->can_simulate_disconnection ?? - null, - can_simulate_hub_connection: $json->can_simulate_hub_connection ?? - null, - can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? - null, - can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? - null, - can_simulate_removal: $json->can_simulate_removal ?? null, - can_turn_off_hvac: $json->can_turn_off_hvac ?? null, - can_unlock_with_code: $json->can_unlock_with_code ?? null, - capabilities_supported: $json->capabilities_supported ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - device_id: $json->device_id ?? null, - device_manufacturer: isset($json->device_manufacturer) - ? DeviceDeviceManufacturer::from_json( - $json->device_manufacturer, - ) - : null, - device_provider: isset($json->device_provider) - ? DeviceDeviceProvider::from_json($json->device_provider) - : null, - device_type: $json->device_type ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => DeviceErrors::from_json($e), - $json->errors ?? [], - ), - is_managed: $json->is_managed ?? null, - location: isset($json->location) - ? DeviceLocation::from_json($json->location) - : null, - nickname: $json->nickname ?? null, - properties: isset($json->properties) - ? DeviceProperties::from_json($json->properties) - : null, - space_ids: $json->space_ids ?? null, - warnings: array_map( - fn($w) => DeviceWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the lock supports configuring automatic locking. - */ - public bool|null $can_configure_auto_lock, - /** - * Indicates whether the thermostat supports cooling. - */ - public bool|null $can_hvac_cool, - /** - * Indicates whether the thermostat supports heating. - */ - public bool|null $can_hvac_heat, - /** - * Indicates whether the thermostat supports simultaneous heating and cooling. - */ - public bool|null $can_hvac_heat_cool, - /** - * Indicates whether the device supports programming offline access codes. - */ - public bool|null $can_program_offline_access_codes, - /** - * Indicates whether the device supports programming online access codes. - */ - public bool|null $can_program_online_access_codes, - /** - * Indicates whether the thermostat supports different climate programs for each day of the week. - */ - public bool|null $can_program_thermostat_programs_as_different_each_day, - /** - * Indicates whether the thermostat supports a single climate program applied to every day. - */ - public bool|null $can_program_thermostat_programs_as_same_each_day, - /** - * Indicates whether the thermostat supports weekday/weekend climate programs. - */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, - /** - * Indicates whether the device supports remote locking. - */ - public bool|null $can_remotely_lock, - /** - * Indicates whether the device supports remote unlocking. - */ - public bool|null $can_remotely_unlock, - /** - * Indicates whether the thermostat supports running climate programs. - */ - public bool|null $can_run_thermostat_programs, - /** - * Indicates whether the device supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_connection, - /** - * Indicates whether the device supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_disconnection, - /** - * Indicates whether the hub supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_hub_connection, - /** - * Indicates whether the hub supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_hub_disconnection, - /** - * Indicates whether the device supports simulating a paid subscription in a sandbox. - */ - public bool|null $can_simulate_paid_subscription, - /** - * Indicates whether the device supports simulating removal in a sandbox. - */ - public bool|null $can_simulate_removal, - /** - * Indicates whether the thermostat can be turned off. - */ - public bool|null $can_turn_off_hvac, - /** - * Indicates whether the lock supports unlocking with an access code. - */ - public bool|null $can_unlock_with_code, - /** - * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). - */ - public array|null $capabilities_supported, - /** - * Unique identifier for the account associated with the device. - */ - public string|null $connected_account_id, - /** - * Date and time at which the device object was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - */ - public mixed $custom_metadata, - /** - * ID of the device. - */ - public string|null $device_id, - /** - * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. - */ - public DeviceDeviceManufacturer|null $device_manufacturer, - /** - * Provider of the device. Represents the third-party service through which the device is controlled. - */ - public DeviceDeviceProvider|null $device_provider, - /** - * Type of the device. - */ - public string|null $device_type, - /** - * Display name of the device, defaults to nickname (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the device easily, especially when there are numerous devices. - */ - public string|null $display_name, - /** - * 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. - */ - public array $errors, - /** - * Indicates whether Seam manages the device. See also [Managed and Unmanaged Devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). - */ - public bool|null $is_managed, - /** - * Location information for the device. - */ - public DeviceLocation|null $location, - /** - * Optional nickname to describe the device, settable through Seam. - */ - public string|null $nickname, - /** - * Properties of the device. - */ - public DeviceProperties|null $properties, - /** - * IDs of the spaces the device is in. - */ - public array|null $space_ids, - /** - * 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. - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the device. - */ - public string|null $workspace_id, - ) {} -} +namespace Seam\Resources { + /** + * Represents a [device](https://docs.seam.co/core-concepts/devices) that has been connected to Seam. + */ + class Device + { + public static function from_json(mixed $json): Device|null + { + if (!$json) { + return null; + } + return new self( + can_configure_auto_lock: $json->can_configure_auto_lock ?? null, + can_hvac_cool: $json->can_hvac_cool ?? null, + can_hvac_heat: $json->can_hvac_heat ?? null, + can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, + can_program_offline_access_codes: $json->can_program_offline_access_codes ?? + null, + can_program_online_access_codes: $json->can_program_online_access_codes ?? + null, + can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? + null, + can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? + null, + can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? + null, + can_remotely_lock: $json->can_remotely_lock ?? null, + can_remotely_unlock: $json->can_remotely_unlock ?? null, + can_run_thermostat_programs: $json->can_run_thermostat_programs ?? + null, + can_simulate_connection: $json->can_simulate_connection ?? null, + can_simulate_disconnection: $json->can_simulate_disconnection ?? + null, + can_simulate_hub_connection: $json->can_simulate_hub_connection ?? + null, + can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? + null, + can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? + null, + can_simulate_removal: $json->can_simulate_removal ?? null, + can_turn_off_hvac: $json->can_turn_off_hvac ?? null, + can_unlock_with_code: $json->can_unlock_with_code ?? null, + capabilities_supported: $json->capabilities_supported ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + device_id: $json->device_id ?? null, + device_manufacturer: isset($json->device_manufacturer) + ? Device\DeviceManufacturer::from_json( + $json->device_manufacturer, + ) + : null, + device_provider: isset($json->device_provider) + ? Device\DeviceProvider::from_json($json->device_provider) + : null, + device_type: $json->device_type ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => Device\Errors::from_json($e), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + location: isset($json->location) + ? Device\Location::from_json($json->location) + : null, + nickname: $json->nickname ?? null, + properties: isset($json->properties) + ? Device\Properties::from_json($json->properties) + : null, + space_ids: $json->space_ids ?? null, + warnings: array_map( + fn($w) => Device\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } -/** - * Latest accelerometer Z-axis reading for a Minut device. - */ -class DeviceAccelerometerZ -{ - public static function from_json(mixed $json): DeviceAccelerometerZ|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest accelerometer Z-axis reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest accelerometer Z-axis reading for a Minut device. - */ - public float|null $value, - ) {} + public function __construct( + /** + * Indicates whether the lock supports configuring automatic locking. + */ + public bool|null $can_configure_auto_lock, + /** + * Indicates whether the thermostat supports cooling. + */ + public bool|null $can_hvac_cool, + /** + * Indicates whether the thermostat supports heating. + */ + public bool|null $can_hvac_heat, + /** + * Indicates whether the thermostat supports simultaneous heating and cooling. + */ + public bool|null $can_hvac_heat_cool, + /** + * Indicates whether the device supports programming offline access codes. + */ + public bool|null $can_program_offline_access_codes, + /** + * Indicates whether the device supports programming online access codes. + */ + public bool|null $can_program_online_access_codes, + /** + * Indicates whether the thermostat supports different climate programs for each day of the week. + */ + public bool|null $can_program_thermostat_programs_as_different_each_day, + /** + * Indicates whether the thermostat supports a single climate program applied to every day. + */ + public bool|null $can_program_thermostat_programs_as_same_each_day, + /** + * Indicates whether the thermostat supports weekday/weekend climate programs. + */ + public bool|null $can_program_thermostat_programs_as_weekday_weekend, + /** + * Indicates whether the device supports remote locking. + */ + public bool|null $can_remotely_lock, + /** + * Indicates whether the device supports remote unlocking. + */ + public bool|null $can_remotely_unlock, + /** + * Indicates whether the thermostat supports running climate programs. + */ + public bool|null $can_run_thermostat_programs, + /** + * Indicates whether the device supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_connection, + /** + * Indicates whether the device supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_disconnection, + /** + * Indicates whether the hub supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_hub_connection, + /** + * Indicates whether the hub supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_hub_disconnection, + /** + * Indicates whether the device supports simulating a paid subscription in a sandbox. + */ + public bool|null $can_simulate_paid_subscription, + /** + * Indicates whether the device supports simulating removal in a sandbox. + */ + public bool|null $can_simulate_removal, + /** + * Indicates whether the thermostat can be turned off. + */ + public bool|null $can_turn_off_hvac, + /** + * Indicates whether the lock supports unlocking with an access code. + */ + public bool|null $can_unlock_with_code, + /** + * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). + */ + public array|null $capabilities_supported, + /** + * Unique identifier for the account associated with the device. + */ + public string|null $connected_account_id, + /** + * Date and time at which the device object was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + */ + public mixed $custom_metadata, + /** + * ID of the device. + */ + public string|null $device_id, + /** + * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. + */ + public Device\DeviceManufacturer|null $device_manufacturer, + /** + * Provider of the device. Represents the third-party service through which the device is controlled. + */ + public Device\DeviceProvider|null $device_provider, + /** + * Type of the device. + */ + public string|null $device_type, + /** + * Display name of the device, defaults to nickname (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the device easily, especially when there are numerous devices. + */ + public string|null $display_name, + /** + * 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. + */ + public array $errors, + /** + * Indicates whether Seam manages the device. See also [Managed and Unmanaged Devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). + */ + public bool|null $is_managed, + /** + * Location information for the device. + */ + public Device\Location|null $location, + /** + * Optional nickname to describe the device, settable through Seam. + */ + public string|null $nickname, + /** + * Properties of the device. + */ + public Device\Properties|null $properties, + /** + * IDs of the spaces the device is in. + */ + public array|null $space_ids, + /** + * 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. + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the device. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Accessory keypad properties and state. - */ -class DeviceAccessoryKeypad -{ - public static function from_json(mixed $json): DeviceAccessoryKeypad|null - { - if (!$json) { - return null; - } - return new self( - battery: isset($json->battery) - ? DeviceBattery::from_json($json->battery) - : null, - is_connected: $json->is_connected ?? null, - ); - } - - public function __construct( - /** - * Keypad battery properties. - */ - public DeviceBattery|null $battery, - /** - * Indicates if an accessory keypad is connected to the device. - */ - public bool|null $is_connected, - ) {} -} +namespace Seam\Resources\Device { + /** + * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. + */ + class DeviceManufacturer + { + public static function from_json(mixed $json): DeviceManufacturer|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + image_url: $json->image_url ?? null, + manufacturer: $json->manufacturer ?? null, + ); + } -/** - * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ -class DeviceActiveThermostatSchedule -{ - public static function from_json( - mixed $json, - ): DeviceActiveThermostatSchedule|null { - if (!$json) { - return null; - } - return new self( - climate_preset_key: $json->climate_preset_key ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => DeviceErrors::from_json($e), - $json->errors ?? [], - ), - is_override_allowed: $json->is_override_allowed ?? null, - max_override_period_minutes: $json->max_override_period_minutes ?? - null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - thermostat_schedule_id: $json->thermostat_schedule_id ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - - public function __construct( - /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $climate_preset_key, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. - */ - public string|null $created_at, - /** - * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. - */ - public string|null $device_id, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public array $errors, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. - */ - public bool|null $is_override_allowed, - /** - * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - */ - public int|null $max_override_period_minutes, - /** - * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $name, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $thermostat_schedule_id, - /** - * ID of the workspace that contains the thermostat schedule. - */ - public string|null $workspace_id, - ) {} -} + public function __construct( + /** + * Display name for the manufacturer, such as `August`, `Yale`, `Salto`, and so on. + */ + public string|null $display_name, + /** + * Image URL for the manufacturer logo. + */ + public string|null $image_url, + /** + * Manufacturer identifier, such as `august`, `yale`, `salto`, and so on. + */ + public string|null $manufacturer, + ) {} + } -/** - * Metadata for an Akiles device. - */ -class DeviceAkilesMetadata -{ - public static function from_json(mixed $json): DeviceAkilesMetadata|null - { - if (!$json) { - return null; - } - return new self( - _member_group_id: $json->_member_group_id ?? null, - gadget_id: $json->gadget_id ?? null, - gadget_name: $json->gadget_name ?? null, - product_name: $json->product_name ?? null, - ); - } - - public function __construct( - /** - * Group ID to which to add users for an Akiles device. - */ - public string|null $_member_group_id, - /** - * Gadget ID for an Akiles device. - */ - public string|null $gadget_id, - /** - * Gadget name for an Akiles device. - */ - public string|null $gadget_name, - /** - * Product name for an Akiles device. - */ - public string|null $product_name, - ) {} -} + /** + * Provider of the device. Represents the third-party service through which the device is controlled. + */ + class DeviceProvider + { + public static function from_json(mixed $json): DeviceProvider|null + { + if (!$json) { + return null; + } + return new self( + device_provider_name: $json->device_provider_name ?? null, + display_name: $json->display_name ?? null, + image_url: $json->image_url ?? null, + provider_category: $json->provider_category ?? null, + ); + } -/** - * Appearance-related properties, as reported by the device. - */ -class DeviceAppearance -{ - public static function from_json(mixed $json): DeviceAppearance|null + public function __construct( + /** + * Device provider name. Corresponds to the integration type, such as `august`, `schlage`, `yale_access`, and so on. + */ + public string|null $device_provider_name, + /** + * Display name for the device provider type. + */ + public string|null $display_name, + /** + * Image URL for the device provider. + */ + public string|null $image_url, + /** + * Provider category. Indicates the third-party provider type, such as `stable`, for stable integrations, or `internal`, for internal integrations. + */ + public string|null $provider_category, + ) {} + } + + /** + * 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 Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); } - return new self(name: $json->name ?? null); + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} } - public function __construct( - /** - * Name of the device as seen from the provider API and application, not settable through Seam. - */ - public string|null $name, - ) {} -} + /** + * Location information for the device. + */ + class Location + { + public static function from_json(mixed $json): Location|null + { + if (!$json) { + return null; + } + return new self( + location_name: $json->location_name ?? null, + time_zone: $json->time_zone ?? null, + timezone: $json->timezone ?? null, + ); + } -/** - * Metadata for an Aqara device. - */ -class DeviceAqaraMetadata -{ - public static function from_json(mixed $json): DeviceAqaraMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_name: $json->device_name ?? null, - did: $json->did ?? null, - firmware_version: $json->firmware_version ?? null, - model: $json->model ?? null, - model_type: $json->model_type ?? null, - parent_did: $json->parent_did ?? null, - position_id: $json->position_id ?? null, - time_zone: $json->time_zone ?? null, - ); - } - - public function __construct( - /** - * Device name for an Aqara device. - */ - public string|null $device_name, - /** - * Device ID (did) for an Aqara device. - */ - public string|null $did, - /** - * Firmware version for an Aqara device. - */ - public string|null $firmware_version, - /** - * Model identifier for an Aqara device. - */ - public string|null $model, - /** - * Model type for an Aqara device. - */ - public float|null $model_type, - /** - * Parent gateway device ID for an Aqara device. - */ - public string|null $parent_did, - /** - * Position (room) ID for an Aqara device. - */ - public string|null $position_id, - /** - * Time zone reported for an Aqara device (e.g. GMT-07:00). - */ - public string|null $time_zone, - ) {} -} + public function __construct( + /** + * Name of the device location. + */ + public string|null $location_name, + /** + * Time zone of the device location. + */ + public string|null $time_zone, + /** + * Time zone of the device location. + * + * @deprecated Use `time_zone` instead. + */ + public string|null $timezone, + ) {} + } -/** - * ASSA ABLOY Credential Service metadata for the phone. - */ -class DeviceAssaAbloyCredentialServiceMetadata -{ - public static function from_json( - mixed $json, - ): DeviceAssaAbloyCredentialServiceMetadata|null { - if (!$json) { - return null; - } - return new self( - endpoints: array_map( - fn($e) => DeviceEndpoints::from_json($e), - $json->endpoints ?? [], - ), - has_active_endpoint: $json->has_active_endpoint ?? null, - ); - } - - public function __construct( - /** - * Endpoints associated with the phone. - */ - public array $endpoints, - /** - * Indicates whether the credential service has active endpoints associated with the phone. - */ - public bool|null $has_active_endpoint, - ) {} -} + /** + * Properties of the device. + */ + class Properties + { + public static function from_json(mixed $json): Properties|null + { + if (!$json) { + return null; + } + return new self( + accessory_keypad: isset($json->accessory_keypad) + ? Properties\AccessoryKeypad::from_json( + $json->accessory_keypad, + ) + : null, + active_thermostat_schedule: isset( + $json->active_thermostat_schedule, + ) + ? Properties\ActiveThermostatSchedule::from_json( + $json->active_thermostat_schedule, + ) + : null, + active_thermostat_schedule_id: $json->active_thermostat_schedule_id ?? + null, + akiles_metadata: isset($json->akiles_metadata) + ? Properties\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + appearance: isset($json->appearance) + ? Properties\Appearance::from_json($json->appearance) + : null, + aqara_metadata: isset($json->aqara_metadata) + ? Properties\AqaraMetadata::from_json($json->aqara_metadata) + : null, + assa_abloy_credential_service_metadata: isset( + $json->assa_abloy_credential_service_metadata, + ) + ? Properties\AssaAbloyCredentialServiceMetadata::from_json( + $json->assa_abloy_credential_service_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( + $json->assa_abloy_vostio_metadata, + ) + ? Properties\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + august_metadata: isset($json->august_metadata) + ? Properties\AugustMetadata::from_json( + $json->august_metadata, + ) + : null, + auto_lock_delay_seconds: $json->auto_lock_delay_seconds ?? null, + auto_lock_enabled: $json->auto_lock_enabled ?? null, + available_climate_preset_modes: $json->available_climate_preset_modes ?? + null, + available_climate_presets: array_map( + fn($a) => Properties\AvailableClimatePresets::from_json($a), + $json->available_climate_presets ?? [], + ), + available_fan_mode_settings: $json->available_fan_mode_settings ?? + null, + available_hvac_mode_settings: $json->available_hvac_mode_settings ?? + null, + avigilon_alta_metadata: isset($json->avigilon_alta_metadata) + ? Properties\AvigilonAltaMetadata::from_json( + $json->avigilon_alta_metadata, + ) + : null, + backup_access_code_pool_enabled: $json->backup_access_code_pool_enabled ?? + null, + battery: isset($json->battery) + ? Properties\Battery::from_json($json->battery) + : null, + battery_level: $json->battery_level ?? null, + brivo_metadata: isset($json->brivo_metadata) + ? Properties\BrivoMetadata::from_json($json->brivo_metadata) + : null, + code_constraints: array_map( + fn($c) => Properties\CodeConstraints::from_json($c), + $json->code_constraints ?? [], + ), + controlbyweb_metadata: isset($json->controlbyweb_metadata) + ? Properties\ControlbywebMetadata::from_json( + $json->controlbyweb_metadata, + ) + : null, + current_climate_setting: isset($json->current_climate_setting) + ? Properties\CurrentClimateSetting::from_json( + $json->current_climate_setting, + ) + : null, + currently_triggering_noise_threshold_ids: $json->currently_triggering_noise_threshold_ids ?? + null, + default_climate_setting: isset($json->default_climate_setting) + ? Properties\DefaultClimateSetting::from_json( + $json->default_climate_setting, + ) + : null, + door_open: $json->door_open ?? null, + dormakaba_oracode_metadata: isset( + $json->dormakaba_oracode_metadata, + ) + ? Properties\DormakabaOracodeMetadata::from_json( + $json->dormakaba_oracode_metadata, + ) + : null, + ecobee_metadata: isset($json->ecobee_metadata) + ? Properties\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fallback_climate_preset_key: $json->fallback_climate_preset_key ?? + null, + fan_mode_setting: $json->fan_mode_setting ?? null, + four_suites_metadata: isset($json->four_suites_metadata) + ? Properties\FourSuitesMetadata::from_json( + $json->four_suites_metadata, + ) + : null, + genie_metadata: isset($json->genie_metadata) + ? Properties\GenieMetadata::from_json($json->genie_metadata) + : null, + has_direct_power: $json->has_direct_power ?? null, + has_native_entry_events: $json->has_native_entry_events ?? null, + honeywell_resideo_metadata: isset( + $json->honeywell_resideo_metadata, + ) + ? Properties\HoneywellResideoMetadata::from_json( + $json->honeywell_resideo_metadata, + ) + : null, + igloo_metadata: isset($json->igloo_metadata) + ? Properties\IglooMetadata::from_json($json->igloo_metadata) + : null, + igloohome_metadata: isset($json->igloohome_metadata) + ? Properties\IgloohomeMetadata::from_json( + $json->igloohome_metadata, + ) + : null, + image_alt_text: $json->image_alt_text ?? null, + image_url: $json->image_url ?? null, + is_cooling: $json->is_cooling ?? null, + is_fan_running: $json->is_fan_running ?? null, + is_heating: $json->is_heating ?? null, + is_temporary_manual_override_active: $json->is_temporary_manual_override_active ?? + null, + keynest_metadata: isset($json->keynest_metadata) + ? Properties\KeynestMetadata::from_json( + $json->keynest_metadata, + ) + : null, + keypad_battery: isset($json->keypad_battery) + ? Properties\KeypadBattery::from_json($json->keypad_battery) + : null, + kisi_metadata: isset($json->kisi_metadata) + ? Properties\KisiMetadata::from_json($json->kisi_metadata) + : null, + korelock_metadata: isset($json->korelock_metadata) + ? Properties\KorelockMetadata::from_json( + $json->korelock_metadata, + ) + : null, + kwikset_metadata: isset($json->kwikset_metadata) + ? Properties\KwiksetMetadata::from_json( + $json->kwikset_metadata, + ) + : null, + locked: $json->locked ?? null, + lockly_metadata: isset($json->lockly_metadata) + ? Properties\LocklyMetadata::from_json( + $json->lockly_metadata, + ) + : null, + manufacturer: $json->manufacturer ?? null, + max_active_codes_supported: $json->max_active_codes_supported ?? + null, + max_cooling_set_point_celsius: $json->max_cooling_set_point_celsius ?? + null, + max_cooling_set_point_fahrenheit: $json->max_cooling_set_point_fahrenheit ?? + null, + max_heating_set_point_celsius: $json->max_heating_set_point_celsius ?? + null, + max_heating_set_point_fahrenheit: $json->max_heating_set_point_fahrenheit ?? + null, + max_thermostat_daily_program_periods_per_day: $json->max_thermostat_daily_program_periods_per_day ?? + null, + max_unique_climate_presets_per_thermostat_weekly_program: $json->max_unique_climate_presets_per_thermostat_weekly_program ?? + null, + min_cooling_set_point_celsius: $json->min_cooling_set_point_celsius ?? + null, + min_cooling_set_point_fahrenheit: $json->min_cooling_set_point_fahrenheit ?? + null, + min_heating_cooling_delta_celsius: $json->min_heating_cooling_delta_celsius ?? + null, + min_heating_cooling_delta_fahrenheit: $json->min_heating_cooling_delta_fahrenheit ?? + null, + min_heating_set_point_celsius: $json->min_heating_set_point_celsius ?? + null, + min_heating_set_point_fahrenheit: $json->min_heating_set_point_fahrenheit ?? + null, + minut_metadata: isset($json->minut_metadata) + ? Properties\MinutMetadata::from_json($json->minut_metadata) + : null, + model: isset($json->model) + ? Properties\Model::from_json($json->model) + : null, + name: $json->name ?? null, + nest_metadata: isset($json->nest_metadata) + ? Properties\NestMetadata::from_json($json->nest_metadata) + : null, + noise_level_decibels: $json->noise_level_decibels ?? null, + noiseaware_metadata: isset($json->noiseaware_metadata) + ? Properties\NoiseawareMetadata::from_json( + $json->noiseaware_metadata, + ) + : null, + nuki_metadata: isset($json->nuki_metadata) + ? Properties\NukiMetadata::from_json($json->nuki_metadata) + : null, + offline_access_codes_enabled: $json->offline_access_codes_enabled ?? + null, + offline_time_frame_options: array_map( + fn($o) => Properties\OfflineTimeFrameOptions::from_json($o), + $json->offline_time_frame_options ?? [], + ), + omnitec_metadata: isset($json->omnitec_metadata) + ? Properties\OmnitecMetadata::from_json( + $json->omnitec_metadata, + ) + : null, + online: $json->online ?? null, + online_access_codes_enabled: $json->online_access_codes_enabled ?? + null, + online_time_frame_options: array_map( + fn($o) => Properties\OnlineTimeFrameOptions::from_json($o), + $json->online_time_frame_options ?? [], + ), + relative_humidity: $json->relative_humidity ?? null, + ring_metadata: isset($json->ring_metadata) + ? Properties\RingMetadata::from_json($json->ring_metadata) + : null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? Properties\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + salto_metadata: isset($json->salto_metadata) + ? Properties\SaltoMetadata::from_json($json->salto_metadata) + : null, + salto_space_credential_service_metadata: isset( + $json->salto_space_credential_service_metadata, + ) + ? Properties\SaltoSpaceCredentialServiceMetadata::from_json( + $json->salto_space_credential_service_metadata, + ) + : null, + schlage_metadata: isset($json->schlage_metadata) + ? Properties\SchlageMetadata::from_json( + $json->schlage_metadata, + ) + : null, + seam_bridge_metadata: isset($json->seam_bridge_metadata) + ? Properties\SeamBridgeMetadata::from_json( + $json->seam_bridge_metadata, + ) + : null, + sensi_metadata: isset($json->sensi_metadata) + ? Properties\SensiMetadata::from_json($json->sensi_metadata) + : null, + serial_number: $json->serial_number ?? null, + smartthings_metadata: isset($json->smartthings_metadata) + ? Properties\SmartthingsMetadata::from_json( + $json->smartthings_metadata, + ) + : null, + supported_code_lengths: $json->supported_code_lengths ?? null, + supports_accessory_keypad: $json->supports_accessory_keypad ?? + null, + supports_backup_access_code_pool: $json->supports_backup_access_code_pool ?? + null, + supports_offline_access_codes: $json->supports_offline_access_codes ?? + null, + tado_metadata: isset($json->tado_metadata) + ? Properties\TadoMetadata::from_json($json->tado_metadata) + : null, + tedee_metadata: isset($json->tedee_metadata) + ? Properties\TedeeMetadata::from_json($json->tedee_metadata) + : null, + temperature_celsius: $json->temperature_celsius ?? null, + temperature_fahrenheit: $json->temperature_fahrenheit ?? null, + temperature_threshold: isset($json->temperature_threshold) + ? Properties\TemperatureThreshold::from_json( + $json->temperature_threshold, + ) + : null, + thermostat_daily_program_period_precision_minutes: $json->thermostat_daily_program_period_precision_minutes ?? + null, + thermostat_daily_programs: array_map( + fn($t) => Properties\ThermostatDailyPrograms::from_json($t), + $json->thermostat_daily_programs ?? [], + ), + thermostat_weekly_program: isset( + $json->thermostat_weekly_program, + ) + ? Properties\ThermostatWeeklyProgram::from_json( + $json->thermostat_weekly_program, + ) + : null, + ttlock_metadata: isset($json->ttlock_metadata) + ? Properties\TtlockMetadata::from_json( + $json->ttlock_metadata, + ) + : null, + two_n_metadata: isset($json->two_n_metadata) + ? Properties\TwoNMetadata::from_json($json->two_n_metadata) + : null, + ultraloq_metadata: isset($json->ultraloq_metadata) + ? Properties\UltraloqMetadata::from_json( + $json->ultraloq_metadata, + ) + : null, + visionline_metadata: isset($json->visionline_metadata) + ? Properties\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + wyze_metadata: isset($json->wyze_metadata) + ? Properties\WyzeMetadata::from_json($json->wyze_metadata) + : null, + ); + } -/** - * Metadata for an ASSA ABLOY Vostio system. - */ -class DeviceAssaAbloyVostioMetadata -{ - public static function from_json( - mixed $json, - ): DeviceAssaAbloyVostioMetadata|null { - if (!$json) { - return null; - } - return new self(encoder_name: $json->encoder_name ?? null); - } - - public function __construct( - /** - * Encoder name for an ASSA ABLOY Vostio system. - */ - public string|null $encoder_name, - ) {} -} + public function __construct( + /** + * Accessory keypad properties and state. + */ + public Properties\AccessoryKeypad|null $accessory_keypad, + /** + * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * + * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. + */ + public Properties\ActiveThermostatSchedule|null $active_thermostat_schedule, + /** + * ID of the active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $active_thermostat_schedule_id, + /** + * Metadata for an Akiles device. + */ + public Properties\AkilesMetadata|null $akiles_metadata, + /** + * Appearance-related properties, as reported by the device. + */ + public Properties\Appearance|null $appearance, + /** + * Metadata for an Aqara device. + */ + public Properties\AqaraMetadata|null $aqara_metadata, + /** + * ASSA ABLOY Credential Service metadata for the phone. + */ + public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, + /** + * Metadata for an ASSA ABLOY Vostio system. + */ + public Properties\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Metadata for an August device. + */ + public Properties\AugustMetadata|null $august_metadata, + /** + * The delay in seconds before the lock automatically locks after being unlocked. + */ + public float|null $auto_lock_delay_seconds, + /** + * Indicates whether automatic locking is enabled. + */ + public bool|null $auto_lock_enabled, + /** + * Climate preset modes that the thermostat supports, such as "home", "away", "wake", "sleep", "occupied", and "unoccupied". + */ + public array|null $available_climate_preset_modes, + /** + * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. + */ + public array $available_climate_presets, + /** + * Fan mode settings that the thermostat supports. + */ + public array|null $available_fan_mode_settings, + /** + * HVAC mode settings that the thermostat supports. + */ + public array|null $available_hvac_mode_settings, + /** + * Metadata for an Avigilon Alta system. + */ + public Properties\AvigilonAltaMetadata|null $avigilon_alta_metadata, + /** + * Indicates whether the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is currently enabled for the device. To disable it, set this to `false` using [/devices/update](https://docs.seam.co/api/devices/update). + */ + public bool|null $backup_access_code_pool_enabled, + /** + * Represents the current status of the battery charge level. + */ + public Properties\Battery|null $battery, + /** + * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. + */ + public float|null $battery_level, + /** + * Metadata for a Brivo device. + */ + public Properties\BrivoMetadata|null $brivo_metadata, + /** + * 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. + */ + public array $code_constraints, + /** + * Metadata for a ControlByWeb device. + */ + public Properties\ControlbywebMetadata|null $controlbyweb_metadata, + /** + * Current climate setting. + */ + public Properties\CurrentClimateSetting|null $current_climate_setting, + /** + * Array of noise threshold IDs that are currently triggering. + */ + public array|null $currently_triggering_noise_threshold_ids, + /** + * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. + */ + public Properties\DefaultClimateSetting|null $default_climate_setting, + /** + * Indicates whether the door is open. + */ + public bool|null $door_open, + /** + * Metadata for a dormakaba Oracode device. + */ + public Properties\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + /** + * Metadata for an ecobee device. + */ + public Properties\EcobeeMetadata|null $ecobee_metadata, + /** + * Key of the [fallback climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets/setting-the-fallback-climate-preset) for the thermostat. + */ + public string|null $fallback_climate_preset_key, + /** + * @deprecated Use `current_climate_setting.fan_mode_setting` instead. + */ + public string|null $fan_mode_setting, + /** + * Metadata for a 4SUITES device. + */ + public Properties\FourSuitesMetadata|null $four_suites_metadata, + /** + * Metadata for a Genie device. + */ + public Properties\GenieMetadata|null $genie_metadata, + /** + * Indicates whether the device has direct power. + */ + public bool|null $has_direct_power, + /** + * Indicates whether the device supports native entry events. + */ + public bool|null $has_native_entry_events, + /** + * Metadata for a Honeywell Resideo device. + */ + public Properties\HoneywellResideoMetadata|null $honeywell_resideo_metadata, + /** + * Metadata for an igloo device. + */ + public Properties\IglooMetadata|null $igloo_metadata, + /** + * Metadata for an igloohome device. + */ + public Properties\IgloohomeMetadata|null $igloohome_metadata, + /** + * Alt text for the device image. + */ + public string|null $image_alt_text, + /** + * Image URL for the device. + */ + public string|null $image_url, + /** + * Indicates whether the connected HVAC system is currently cooling, as reported by the thermostat. + */ + public bool|null $is_cooling, + /** + * Indicates whether the fan in the connected HVAC system is currently running, as reported by the thermostat. + */ + public bool|null $is_fan_running, + /** + * Indicates whether the connected HVAC system is currently heating, as reported by the thermostat. + */ + public bool|null $is_heating, + /** + * Indicates whether the current thermostat settings differ from the most recent active program or schedule that Seam activated. For this condition to occur, `current_climate_setting.manual_override_allowed` must also be `true`. + */ + public bool|null $is_temporary_manual_override_active, + /** + * Metadata for a KeyNest device. + */ + public Properties\KeynestMetadata|null $keynest_metadata, + /** + * Keypad battery status. + */ + public Properties\KeypadBattery|null $keypad_battery, + /** + * Metadata for a Kisi device. + */ + public Properties\KisiMetadata|null $kisi_metadata, + /** + * Metadata for a Korelock device. + */ + public Properties\KorelockMetadata|null $korelock_metadata, + /** + * Metadata for a Kwikset device. + */ + public Properties\KwiksetMetadata|null $kwikset_metadata, + /** + * Indicates whether the lock is locked. + */ + public bool|null $locked, + /** + * Metadata for a Lockly device. + */ + public Properties\LocklyMetadata|null $lockly_metadata, + /** + * 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. + */ + public string|null $manufacturer, + /** + * Maximum number of active access codes that the device supports. + */ + public float|null $max_active_codes_supported, + /** + * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. + */ + public float|null $max_cooling_set_point_celsius, + /** + * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. + */ + public float|null $max_cooling_set_point_fahrenheit, + /** + * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. + */ + public float|null $max_heating_set_point_celsius, + /** + * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. + */ + public float|null $max_heating_set_point_fahrenheit, + /** + * Maximum number of periods that the thermostat can support per day. For example, if the thermostat supports 4 periods per day, this value is 4. + */ + public float|null $max_thermostat_daily_program_periods_per_day, + /** + * Maximum number of climate presets that the thermostat can support for weekly programming. + */ + public float|null $max_unique_climate_presets_per_thermostat_weekly_program, + /** + * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. + */ + public float|null $min_cooling_set_point_celsius, + /** + * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. + */ + public float|null $min_cooling_set_point_fahrenheit, + /** + * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °C between the cooling and heating set points when in heat-cool (auto) mode. + */ + public float|null $min_heating_cooling_delta_celsius, + /** + * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °F between the cooling and heating set points when in heat-cool (auto) mode. + */ + public float|null $min_heating_cooling_delta_fahrenheit, + /** + * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. + */ + public float|null $min_heating_set_point_celsius, + /** + * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. + */ + public float|null $min_heating_set_point_fahrenheit, + /** + * Metadata for a Minut device. + */ + public Properties\MinutMetadata|null $minut_metadata, + /** + * Device model-related properties. + */ + public Properties\Model|null $model, + /** + * Name of the device. + * + * @deprecated use device.display_name instead + */ + public string|null $name, + /** + * Metadata for a Google Nest device. + */ + public Properties\NestMetadata|null $nest_metadata, + /** + * Indicates current noise level in decibels, if the device supports noise detection. + */ + public float|null $noise_level_decibels, + /** + * Metadata for a NoiseAware device. + */ + public Properties\NoiseawareMetadata|null $noiseaware_metadata, + /** + * Metadata for a Nuki device. + */ + public Properties\NukiMetadata|null $nuki_metadata, + /** + * Indicates whether it is currently possible to use offline access codes for the device. + * + * @deprecated use device.can_program_offline_access_codes + */ + public bool|null $offline_access_codes_enabled, + /** + * 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. + */ + public array $offline_time_frame_options, + /** + * Metadata for an Omnitec device. + */ + public Properties\OmnitecMetadata|null $omnitec_metadata, + /** + * Indicates whether the device is online. + */ + public bool|null $online, + /** + * Indicates whether it is currently possible to use online access codes for the device. + * + * @deprecated use device.can_program_online_access_codes + */ + public bool|null $online_access_codes_enabled, + /** + * 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. + */ + public array $online_time_frame_options, + /** + * Reported relative humidity, as a value between 0 and 1, inclusive. + */ + public float|null $relative_humidity, + /** + * Metadata for a Ring device. + */ + public Properties\RingMetadata|null $ring_metadata, + /** + * Metadata for a Salto KS device. + */ + public Properties\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Metada for a Salto device. + * + * @deprecated Use `salto_ks_metadata ` instead. + */ + public Properties\SaltoMetadata|null $salto_metadata, + /** + * Salto Space credential service metadata for the phone. + */ + public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, + /** + * Metadata for a Schlage device. + */ + public Properties\SchlageMetadata|null $schlage_metadata, + /** + * Metadata for Seam Bridge. + */ + public Properties\SeamBridgeMetadata|null $seam_bridge_metadata, + /** + * Metadata for a Sensi device. + */ + public Properties\SensiMetadata|null $sensi_metadata, + /** + * Serial number of the device. + */ + public string|null $serial_number, + /** + * Metadata for a SmartThings device. + */ + public Properties\SmartthingsMetadata|null $smartthings_metadata, + /** + * Supported code lengths for access codes. + */ + public array|null $supported_code_lengths, + /** + * @deprecated use device.properties.model.can_connect_accessory_keypad + */ + public bool|null $supports_accessory_keypad, + /** + * Indicates whether the device supports a [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes). + */ + public bool|null $supports_backup_access_code_pool, + /** + * @deprecated use offline_access_codes_enabled + */ + public bool|null $supports_offline_access_codes, + /** + * Metadata for a tado° device. + */ + public Properties\TadoMetadata|null $tado_metadata, + /** + * Metadata for a Tedee device. + */ + public Properties\TedeeMetadata|null $tedee_metadata, + /** + * Reported temperature in °C. + */ + public float|null $temperature_celsius, + /** + * Reported temperature in °F. + */ + public float|null $temperature_fahrenheit, + /** + * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public Properties\TemperatureThreshold|null $temperature_threshold, + /** + * Precision of the thermostat's period in minutes. For example, if the thermostat supports 15-minute periods, this value is 15. All values are relative to the top of the hour, so for 15 minutes, the periods would be 0, 15, 30, and 45 minutes past the hour. + */ + public float|null $thermostat_daily_program_period_precision_minutes, + /** + * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + public array $thermostat_daily_programs, + /** + * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + public Properties\ThermostatWeeklyProgram|null $thermostat_weekly_program, + /** + * Metadata for a TTLock device. + */ + public Properties\TtlockMetadata|null $ttlock_metadata, + /** + * Metadata for a 2N device. + */ + public Properties\TwoNMetadata|null $two_n_metadata, + /** + * Metadata for an Ultraloq device. + */ + public Properties\UltraloqMetadata|null $ultraloq_metadata, + /** + * Metadata for an ASSA ABLOY Visionline system. + */ + public Properties\VisionlineMetadata|null $visionline_metadata, + /** + * Metadata for a Wyze device. + */ + public Properties\WyzeMetadata|null $wyze_metadata, + ) {} + } -/** - * Metadata for an August device. - */ -class DeviceAugustMetadata -{ - public static function from_json(mixed $json): DeviceAugustMetadata|null - { - if (!$json) { - return null; - } - return new self( - has_keypad: $json->has_keypad ?? null, - house_id: $json->house_id ?? null, - house_name: $json->house_name ?? null, - keypad_battery_level: $json->keypad_battery_level ?? null, - lock_id: $json->lock_id ?? null, - lock_name: $json->lock_name ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Indicates whether an August device has a keypad. - */ - public bool|null $has_keypad, - /** - * House ID for an August device. - */ - public string|null $house_id, - /** - * House name for an August device. - */ - public string|null $house_name, - /** - * Keypad battery level for an August device. - */ - public string|null $keypad_battery_level, - /** - * Lock ID for an August device. - */ - public string|null $lock_id, - /** - * Lock name for an August device. - */ - public string|null $lock_name, - /** - * Model for an August device. - */ - public string|null $model, - ) {} -} + /** + * 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 Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + active_access_code_count: $json->active_access_code_count ?? + null, + created_at: $json->created_at ?? null, + max_active_access_code_count: $json->max_active_access_code_count ?? + null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } -/** - * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. - */ -class DeviceAvailableClimatePresets -{ - public static function from_json( - mixed $json, - ): DeviceAvailableClimatePresets|null { - if (!$json) { - return null; - } - return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DeviceEcobeeMetadata::from_json($json->ecobee_metadata) - : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. - */ - public bool|null $can_delete, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. - */ - public bool|null $can_edit, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. - */ - public bool|null $can_use_with_thermostat_daily_programs, - /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $climate_preset_key, - /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. - */ - public string|null $climate_preset_mode, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit, - /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $display_name, - /** - * Metadata specific to the Ecobee climate, if applicable. - */ - public DeviceEcobeeMetadata|null $ecobee_metadata, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_fahrenheit, - /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. - */ - public string|null $hvac_mode_setting, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' - */ - public bool|null $manual_override_allowed, - /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $name, - ) {} + public function __construct( + /** + * Number of active access codes on the device when the warning was set. + */ + public int|null $active_access_code_count, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Maximum number of active access codes supported by the device. + */ + public int|null $max_active_access_code_count, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Metadata for an Avigilon Alta system. - */ -class DeviceAvigilonAltaMetadata -{ - public static function from_json( - mixed $json, - ): DeviceAvigilonAltaMetadata|null { - if (!$json) { - return null; - } - return new self( - entry_name: $json->entry_name ?? null, - entry_relays_total_count: $json->entry_relays_total_count ?? null, - org_name: $json->org_name ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - zone_id: $json->zone_id ?? null, - zone_name: $json->zone_name ?? null, - ); - } - - public function __construct( - /** - * Entry name for an Avigilon Alta system. - */ - public string|null $entry_name, - /** - * Total count of entry relays for an Avigilon Alta system. - */ - public float|null $entry_relays_total_count, - /** - * Organization name for an Avigilon Alta system. - */ - public string|null $org_name, - /** - * Site ID for an Avigilon Alta system. - */ - public float|null $site_id, - /** - * Site name for an Avigilon Alta system. - */ - public string|null $site_name, - /** - * Zone ID for an Avigilon Alta system. - */ - public float|null $zone_id, - /** - * Zone name for an Avigilon Alta system. - */ - public string|null $zone_name, - ) {} -} +namespace Seam\Resources\Device\Properties { + /** + * Accessory keypad properties and state. + */ + class AccessoryKeypad + { + public static function from_json(mixed $json): AccessoryKeypad|null + { + if (!$json) { + return null; + } + return new self( + battery: isset($json->battery) + ? AccessoryKeypad\Battery::from_json($json->battery) + : null, + is_connected: $json->is_connected ?? null, + ); + } + + public function __construct( + /** + * Keypad battery properties. + */ + public AccessoryKeypad\Battery|null $battery, + /** + * Indicates if an accessory keypad is connected to the device. + */ + public bool|null $is_connected, + ) {} + } -/** - * Keypad battery properties. - */ -class DeviceBattery -{ - public static function from_json(mixed $json): DeviceBattery|null + /** + * Appearance-related properties, as reported by the device. + */ + class Appearance { - if (!$json) { - return null; + public static function from_json(mixed $json): Appearance|null + { + if (!$json) { + return null; + } + return new self(name: $json->name ?? null); } - return new self(level: $json->level ?? null); + + public function __construct( + /** + * Name of the device as seen from the provider API and application, not settable through Seam. + */ + public string|null $name, + ) {} } - public function __construct(public float|null $level) {} -} + /** + * Represents the current status of the battery charge level. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self( + level: $json->level ?? null, + status: $json->status ?? null, + ); + } -/** - * Metadata for a Brivo device. - */ -class DeviceBrivoMetadata -{ - public static function from_json(mixed $json): DeviceBrivoMetadata|null - { - if (!$json) { - return null; - } - return new self( - activation_enabled: $json->activation_enabled ?? null, - device_name: $json->device_name ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the Brivo access point has activation (remote unlock) enabled. - */ - public bool|null $activation_enabled, - /** - * Device name for a Brivo device. - */ - public string|null $device_name, - ) {} -} + public function __construct( + /** + * Battery charge level as a value between 0 and 1, inclusive. + */ + public float|null $level, + /** + * 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. + */ + public string|null $status, + ) {} + } -/** - * 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. - */ -class DeviceCodeConstraints -{ - public static function from_json(mixed $json): DeviceCodeConstraints|null - { - if (!$json) { - return null; - } - return new self( - constraint_type: $json->constraint_type ?? null, - max_length: $json->max_length ?? null, - min_length: $json->min_length ?? null, - ); - } - - public function __construct( - public string|null $constraint_type, - /** - * Maximum name length constraint for access codes. - */ - public float|null $max_length, - /** - * Minimum name length constraint for access codes. - */ - public float|null $min_length, - ) {} -} + /** + * Device model-related properties. + */ + class Model + { + public static function from_json(mixed $json): Model|null + { + if (!$json) { + return null; + } + return new self( + accessory_keypad_supported: $json->accessory_keypad_supported ?? + null, + can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? + null, + display_name: $json->display_name ?? null, + has_built_in_keypad: $json->has_built_in_keypad ?? null, + manufacturer_display_name: $json->manufacturer_display_name ?? + null, + offline_access_codes_supported: $json->offline_access_codes_supported ?? + null, + online_access_codes_supported: $json->online_access_codes_supported ?? + null, + ); + } -/** - * Metadata for a ControlByWeb device. - */ -class DeviceControlbywebMetadata -{ - public static function from_json( - mixed $json, - ): DeviceControlbywebMetadata|null { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - relay_name: $json->relay_name ?? null, - ); - } - - public function __construct( - /** - * Device ID for a ControlByWeb device. - */ - public string|null $device_id, - /** - * Device name for a ControlByWeb device. - */ - public string|null $device_name, - /** - * Relay name for a ControlByWeb device. - */ - public string|null $relay_name, - ) {} -} + public function __construct( + /** + * @deprecated use device.properties.model.can_connect_accessory_keypad + */ + public bool|null $accessory_keypad_supported, + /** + * Indicates whether the device can connect a accessory keypad. + */ + public bool|null $can_connect_accessory_keypad, + /** + * Display name of the device model. + */ + public string|null $display_name, + /** + * Indicates whether the device has a built in accessory keypad. + */ + public bool|null $has_built_in_keypad, + /** + * Display name that corresponds to the manufacturer-specific terminology for the device. + */ + public string|null $manufacturer_display_name, + /** + * @deprecated use device.can_program_offline_access_codes. + */ + public bool|null $offline_access_codes_supported, + /** + * @deprecated use device.can_program_online_access_codes. + */ + public bool|null $online_access_codes_supported, + ) {} + } -/** - * Current climate setting. - */ -class DeviceCurrentClimateSetting -{ - public static function from_json( - mixed $json, - ): DeviceCurrentClimateSetting|null { - if (!$json) { - return null; - } - return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DeviceEcobeeMetadata::from_json($json->ecobee_metadata) - : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. - */ - public bool|null $can_delete, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. - */ - public bool|null $can_edit, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. - */ - public bool|null $can_use_with_thermostat_daily_programs, - /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $climate_preset_key, - /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. - */ - public string|null $climate_preset_mode, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit, - /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $display_name, - /** - * Metadata specific to the Ecobee climate, if applicable. - */ - public DeviceEcobeeMetadata|null $ecobee_metadata, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_fahrenheit, - /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. - */ - public string|null $hvac_mode_setting, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' - */ - public bool|null $manual_override_allowed, - /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $name, - ) {} -} + /** + * ASSA ABLOY Credential Service metadata for the phone. + */ + class AssaAbloyCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self( + endpoints: array_map( + fn( + $e, + ) => AssaAbloyCredentialServiceMetadata\Endpoints::from_json( + $e, + ), + $json->endpoints ?? [], + ), + has_active_endpoint: $json->has_active_endpoint ?? null, + ); + } -class DeviceDefaultClimateSetting -{ - public static function from_json( - mixed $json, - ): DeviceDefaultClimateSetting|null { - if (!$json) { - return null; - } - return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DeviceEcobeeMetadata::from_json($json->ecobee_metadata) - : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. - */ - public bool|null $can_delete, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. - */ - public bool|null $can_edit, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. - */ - public bool|null $can_use_with_thermostat_daily_programs, - /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $climate_preset_key, - /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. - */ - public string|null $climate_preset_mode, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit, - /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $display_name, - /** - * Metadata specific to the Ecobee climate, if applicable. - */ - public DeviceEcobeeMetadata|null $ecobee_metadata, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_fahrenheit, - /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. - */ - public string|null $hvac_mode_setting, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' - */ - public bool|null $manual_override_allowed, - /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $name, - ) {} -} + public function __construct( + /** + * Endpoints associated with the phone. + */ + public array $endpoints, + /** + * Indicates whether the credential service has active endpoints associated with the phone. + */ + public bool|null $has_active_endpoint, + ) {} + } -/** - * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. - */ -class DeviceDeviceManufacturer -{ - public static function from_json(mixed $json): DeviceDeviceManufacturer|null - { - if (!$json) { - return null; - } - return new self( - display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, - manufacturer: $json->manufacturer ?? null, - ); - } - - public function __construct( - /** - * Display name for the manufacturer, such as `August`, `Yale`, `Salto`, and so on. - */ - public string|null $display_name, - /** - * Image URL for the manufacturer logo. - */ - public string|null $image_url, - /** - * Manufacturer identifier, such as `august`, `yale`, `salto`, and so on. - */ - public string|null $manufacturer, - ) {} -} + /** + * Salto Space credential service metadata for the phone. + */ + class SaltoSpaceCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): SaltoSpaceCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self(has_active_phone: $json->has_active_phone ?? null); + } -/** - * Provider of the device. Represents the third-party service through which the device is controlled. - */ -class DeviceDeviceProvider -{ - public static function from_json(mixed $json): DeviceDeviceProvider|null - { - if (!$json) { - return null; - } - return new self( - device_provider_name: $json->device_provider_name ?? null, - display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, - provider_category: $json->provider_category ?? null, - ); - } - - public function __construct( - /** - * Device provider name. Corresponds to the integration type, such as `august`, `schlage`, `yale_access`, and so on. - */ - public string|null $device_provider_name, - /** - * Display name for the device provider type. - */ - public string|null $display_name, - /** - * Image URL for the device provider. - */ - public string|null $image_url, - /** - * Provider category. Indicates the third-party provider type, such as `stable`, for stable integrations, or `internal`, for internal integrations. - */ - public string|null $provider_category, - ) {} -} + public function __construct( + /** + * Indicates whether the credential service has an active associated phone. + */ + public bool|null $has_active_phone, + ) {} + } -/** - * Metadata for a dormakaba Oracode device. - */ -class DeviceDormakabaOracodeMetadata -{ - public static function from_json( - mixed $json, - ): DeviceDormakabaOracodeMetadata|null { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - door_id: $json->door_id ?? null, - door_is_wireless: $json->door_is_wireless ?? null, - door_name: $json->door_name ?? null, - iana_timezone: $json->iana_timezone ?? null, - predefined_time_slots: array_map( - fn($p) => DevicePredefinedTimeSlots::from_json($p), - $json->predefined_time_slots ?? [], - ), - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - - public function __construct( - /** - * Device ID for a dormakaba Oracode device. - */ - public mixed $device_id, - /** - * Door ID for a dormakaba Oracode device. - */ - public float|null $door_id, - /** - * Indicates whether a door is wireless for a dormakaba Oracode device. - */ - public bool|null $door_is_wireless, - /** - * Door name for a dormakaba Oracode device. - */ - public string|null $door_name, - /** - * IANA time zone for a dormakaba Oracode device. - */ - public string|null $iana_timezone, - /** - * Predefined time slots for a dormakaba Oracode device. - */ - public array $predefined_time_slots, - /** - * Site ID for a dormakaba Oracode device. - * - * @deprecated Previously marked as "@DEPRECATED." - */ - public float|null $site_id, - /** - * Site name for a dormakaba Oracode device. - */ - public string|null $site_name, - ) {} -} + /** + * Metadata for an Akiles device. + */ + class AkilesMetadata + { + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self( + _member_group_id: $json->_member_group_id ?? null, + gadget_id: $json->gadget_id ?? null, + gadget_name: $json->gadget_name ?? null, + product_name: $json->product_name ?? null, + ); + } -/** - * Metadata for an ecobee device. - */ -class DeviceEcobeeMetadata -{ - public static function from_json(mixed $json): DeviceEcobeeMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_name: $json->device_name ?? null, - ecobee_device_id: $json->ecobee_device_id ?? null, - ); - } - - public function __construct( - /** - * Device name for an ecobee device. - */ - public string|null $device_name, - /** - * Device ID for an ecobee device. - */ - public string|null $ecobee_device_id, - ) {} -} + public function __construct( + /** + * Group ID to which to add users for an Akiles device. + */ + public string|null $_member_group_id, + /** + * Gadget ID for an Akiles device. + */ + public string|null $gadget_id, + /** + * Gadget name for an Akiles device. + */ + public string|null $gadget_name, + /** + * Product name for an Akiles device. + */ + public string|null $product_name, + ) {} + } -/** - * Endpoints associated with the phone. - */ -class DeviceEndpoints -{ - public static function from_json(mixed $json): DeviceEndpoints|null - { - if (!$json) { - return null; - } - return new self( - endpoint_id: $json->endpoint_id ?? null, - is_active: $json->is_active ?? null, - ); - } - - public function __construct( - /** - * ID of the associated endpoint. - */ - public string|null $endpoint_id, - /** - * Indicated whether the endpoint is active. - */ - public bool|null $is_active, - ) {} -} + /** + * Metadata for an Aqara device. + */ + class AqaraMetadata + { + public static function from_json(mixed $json): AqaraMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_name: $json->device_name ?? null, + did: $json->did ?? null, + firmware_version: $json->firmware_version ?? null, + model: $json->model ?? null, + model_type: $json->model_type ?? null, + parent_did: $json->parent_did ?? null, + position_id: $json->position_id ?? null, + time_zone: $json->time_zone ?? null, + ); + } -/** - * 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 DeviceErrors -{ - public static function from_json(mixed $json): DeviceErrors|null - { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - message: $json->message ?? null, - ); - } - - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. - */ - public bool|null $is_connected_account_error, - /** - * Indicates that the error is not a device error. - */ - public bool|null $is_device_error, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Device name for an Aqara device. + */ + public string|null $device_name, + /** + * Device ID (did) for an Aqara device. + */ + public string|null $did, + /** + * Firmware version for an Aqara device. + */ + public string|null $firmware_version, + /** + * Model identifier for an Aqara device. + */ + public string|null $model, + /** + * Model type for an Aqara device. + */ + public float|null $model_type, + /** + * Parent gateway device ID for an Aqara device. + */ + public string|null $parent_did, + /** + * Position (room) ID for an Aqara device. + */ + public string|null $position_id, + /** + * Time zone reported for an Aqara device (e.g. GMT-07:00). + */ + public string|null $time_zone, + ) {} + } -/** - * Features for a TTLock device. - */ -class DeviceFeatures -{ - public static function from_json(mixed $json): DeviceFeatures|null - { - if (!$json) { - return null; - } - return new self( - auto_lock_time_config: $json->auto_lock_time_config ?? null, - incomplete_keyboard_passcode: $json->incomplete_keyboard_passcode ?? - null, - lock_command: $json->lock_command ?? null, - passcode: $json->passcode ?? null, - passcode_management: $json->passcode_management ?? null, - unlock_via_gateway: $json->unlock_via_gateway ?? null, - wifi: $json->wifi ?? null, - ); - } - - public function __construct( - /** - * Indicates whether a TTLock device supports auto-lock time configuration. - */ - public bool|null $auto_lock_time_config, - /** - * Indicates whether a TTLock device supports an incomplete keyboard passcode. - */ - public bool|null $incomplete_keyboard_passcode, - /** - * Indicates whether a TTLock device supports the lock command. - */ - public bool|null $lock_command, - /** - * Indicates whether a TTLock device supports a passcode. - */ - public bool|null $passcode, - /** - * Indicates whether a TTLock device supports passcode management. - */ - public bool|null $passcode_management, - /** - * Indicates whether a TTLock device supports unlock via gateway. - */ - public bool|null $unlock_via_gateway, - /** - * Indicates whether a TTLock device supports Wi-Fi. - */ - public bool|null $wifi, - ) {} -} + /** + * Metadata for an ASSA ABLOY Vostio system. + */ + class AssaAbloyVostioMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self(encoder_name: $json->encoder_name ?? null); + } -/** - * Metadata for a 4SUITES device. - */ -class DeviceFourSuitesMetadata -{ - public static function from_json(mixed $json): DeviceFourSuitesMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - reclose_delay_in_seconds: $json->reclose_delay_in_seconds ?? null, - ); - } - - public function __construct( - /** - * Device ID for a 4SUITES device. - */ - public float|null $device_id, - /** - * Device name for a 4SUITES device. - */ - public string|null $device_name, - /** - * Reclose delay, in seconds, for a 4SUITES device. - */ - public float|null $reclose_delay_in_seconds, - ) {} -} + public function __construct( + /** + * Encoder name for an ASSA ABLOY Vostio system. + */ + public string|null $encoder_name, + ) {} + } -/** - * Metadata for a Genie device. - */ -class DeviceGenieMetadata -{ - public static function from_json(mixed $json): DeviceGenieMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_name: $json->device_name ?? null, - door_name: $json->door_name ?? null, - ); - } - - public function __construct( - /** - * Lock name for a Genie device. - */ - public string|null $device_name, - /** - * Door name for a Genie device. - */ - public string|null $door_name, - ) {} -} + /** + * Metadata for an August device. + */ + class AugustMetadata + { + public static function from_json(mixed $json): AugustMetadata|null + { + if (!$json) { + return null; + } + return new self( + has_keypad: $json->has_keypad ?? null, + house_id: $json->house_id ?? null, + house_name: $json->house_name ?? null, + keypad_battery_level: $json->keypad_battery_level ?? null, + lock_id: $json->lock_id ?? null, + lock_name: $json->lock_name ?? null, + model: $json->model ?? null, + ); + } -/** - * Metadata for a Honeywell Resideo device. - */ -class DeviceHoneywellResideoMetadata -{ - public static function from_json( - mixed $json, - ): DeviceHoneywellResideoMetadata|null { - if (!$json) { - return null; - } - return new self( - device_name: $json->device_name ?? null, - honeywell_resideo_device_id: $json->honeywell_resideo_device_id ?? - null, - ); - } - - public function __construct( - /** - * Device name for a Honeywell Resideo device. - */ - public string|null $device_name, - /** - * Device ID for a Honeywell Resideo device. - */ - public string|null $honeywell_resideo_device_id, - ) {} -} + public function __construct( + /** + * Indicates whether an August device has a keypad. + */ + public bool|null $has_keypad, + /** + * House ID for an August device. + */ + public string|null $house_id, + /** + * House name for an August device. + */ + public string|null $house_name, + /** + * Keypad battery level for an August device. + */ + public string|null $keypad_battery_level, + /** + * Lock ID for an August device. + */ + public string|null $lock_id, + /** + * Lock name for an August device. + */ + public string|null $lock_name, + /** + * Model for an August device. + */ + public string|null $model, + ) {} + } -/** - * Latest humidity reading for a Minut device. - */ -class DeviceHumidity -{ - public static function from_json(mixed $json): DeviceHumidity|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest humidity reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest humidity reading for a Minut device. - */ - public float|null $value, - ) {} -} + /** + * Metadata for an Avigilon Alta system. + */ + class AvigilonAltaMetadata + { + public static function from_json(mixed $json): AvigilonAltaMetadata|null + { + if (!$json) { + return null; + } + return new self( + entry_name: $json->entry_name ?? null, + entry_relays_total_count: $json->entry_relays_total_count ?? + null, + org_name: $json->org_name ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + zone_id: $json->zone_id ?? null, + zone_name: $json->zone_name ?? null, + ); + } -/** - * Metadata for an igloohome device. - */ -class DeviceIgloohomeMetadata -{ - public static function from_json(mixed $json): DeviceIgloohomeMetadata|null - { - if (!$json) { - return null; - } - return new self( - bridge_id: $json->bridge_id ?? null, - bridge_name: $json->bridge_name ?? null, - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - is_accessory_keypad_linked_to_bridge: $json->is_accessory_keypad_linked_to_bridge ?? - null, - keypad_id: $json->keypad_id ?? null, - ); - } - - public function __construct( - /** - * Bridge ID for an igloohome device. - */ - public string|null $bridge_id, - /** - * Bridge name for an igloohome device. - */ - public string|null $bridge_name, - /** - * Device ID for an igloohome device. - */ - public string|null $device_id, - /** - * Device name for an igloohome device. - */ - public string|null $device_name, - /** - * Indicates whether a keypad is linked to a bridge for an igloohome device. - */ - public bool|null $is_accessory_keypad_linked_to_bridge, - /** - * Keypad ID for an igloohome device. - */ - public string|null $keypad_id, - ) {} -} + public function __construct( + /** + * Entry name for an Avigilon Alta system. + */ + public string|null $entry_name, + /** + * Total count of entry relays for an Avigilon Alta system. + */ + public float|null $entry_relays_total_count, + /** + * Organization name for an Avigilon Alta system. + */ + public string|null $org_name, + /** + * Site ID for an Avigilon Alta system. + */ + public float|null $site_id, + /** + * Site name for an Avigilon Alta system. + */ + public string|null $site_name, + /** + * Zone ID for an Avigilon Alta system. + */ + public float|null $zone_id, + /** + * Zone name for an Avigilon Alta system. + */ + public string|null $zone_name, + ) {} + } -/** - * Metadata for an igloo device. - */ -class DeviceIglooMetadata -{ - public static function from_json(mixed $json): DeviceIglooMetadata|null - { - if (!$json) { - return null; - } - return new self( - bridge_id: $json->bridge_id ?? null, - device_id: $json->device_id ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Bridge ID for an igloo device. - */ - public string|null $bridge_id, - /** - * Device ID for an igloo device. - */ - public string|null $device_id, - /** - * Model for an igloo device. - */ - public string|null $model, - ) {} -} + /** + * Metadata for a Brivo device. + */ + class BrivoMetadata + { + public static function from_json(mixed $json): BrivoMetadata|null + { + if (!$json) { + return null; + } + return new self( + activation_enabled: $json->activation_enabled ?? null, + device_name: $json->device_name ?? null, + ); + } -/** - * Metadata for a KeyNest device. - */ -class DeviceKeynestMetadata -{ - public static function from_json(mixed $json): DeviceKeynestMetadata|null - { - if (!$json) { - return null; - } - return new self( - address: $json->address ?? null, - current_or_last_store_id: $json->current_or_last_store_id ?? null, - current_status: $json->current_status ?? null, - current_user_company: $json->current_user_company ?? null, - current_user_email: $json->current_user_email ?? null, - current_user_name: $json->current_user_name ?? null, - current_user_phone_number: $json->current_user_phone_number ?? null, - default_office_id: $json->default_office_id ?? null, - device_name: $json->device_name ?? null, - fob_id: $json->fob_id ?? null, - handover_method: $json->handover_method ?? null, - has_photo: $json->has_photo ?? null, - is_quadient_locker: $json->is_quadient_locker ?? null, - key_id: $json->key_id ?? null, - key_notes: $json->key_notes ?? null, - keynest_app_user: $json->keynest_app_user ?? null, - last_movement: $json->last_movement ?? null, - property_id: $json->property_id ?? null, - property_postcode: $json->property_postcode ?? null, - status_type: $json->status_type ?? null, - subscription_plan: $json->subscription_plan ?? null, - ); - } - - public function __construct( - /** - * Address for a KeyNest device. - */ - public string|null $address, - /** - * Current or last store ID for a KeyNest device. - */ - public float|null $current_or_last_store_id, - /** - * Current status for a KeyNest device. - */ - public string|null $current_status, - /** - * Current user company for a KeyNest device. - */ - public string|null $current_user_company, - /** - * Current user email for a KeyNest device. - */ - public string|null $current_user_email, - /** - * Current user name for a KeyNest device. - */ - public string|null $current_user_name, - /** - * Current user phone number for a KeyNest device. - */ - public string|null $current_user_phone_number, - /** - * Default office ID for a KeyNest device. - */ - public float|null $default_office_id, - /** - * Device name for a KeyNest device. - */ - public string|null $device_name, - /** - * Fob ID for a KeyNest device. - */ - public float|null $fob_id, - /** - * Handover method for a KeyNest device. - */ - public string|null $handover_method, - /** - * Whether the KeyNest device has a photo. - */ - public bool|null $has_photo, - /** - * Whether the key is in a locker that does not support the access codes API. - */ - public bool|null $is_quadient_locker, - /** - * Key ID for a KeyNest device. - */ - public string|null $key_id, - /** - * Key notes for a KeyNest device. - */ - public string|null $key_notes, - /** - * KeyNest app user for a KeyNest device. - */ - public string|null $keynest_app_user, - /** - * Last movement timestamp for a KeyNest device. - */ - public string|null $last_movement, - /** - * Property ID for a KeyNest device. - */ - public string|null $property_id, - /** - * Property postcode for a KeyNest device. - */ - public string|null $property_postcode, - /** - * Status type for a KeyNest device. - */ - public string|null $status_type, - /** - * Subscription plan for a KeyNest device. - */ - public string|null $subscription_plan, - ) {} -} + public function __construct( + /** + * Indicates whether the Brivo access point has activation (remote unlock) enabled. + */ + public bool|null $activation_enabled, + /** + * Device name for a Brivo device. + */ + public string|null $device_name, + ) {} + } -/** - * Keypad battery status. - */ -class DeviceKeypadBattery -{ - public static function from_json(mixed $json): DeviceKeypadBattery|null + /** + * Metadata for a ControlByWeb device. + */ + class ControlbywebMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): ControlbywebMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + relay_name: $json->relay_name ?? null, + ); } - return new self(level: $json->level ?? null); + + public function __construct( + /** + * Device ID for a ControlByWeb device. + */ + public string|null $device_id, + /** + * Device name for a ControlByWeb device. + */ + public string|null $device_name, + /** + * Relay name for a ControlByWeb device. + */ + public string|null $relay_name, + ) {} } - public function __construct( - /** - * Keypad battery charge level. - */ - public float|null $level, - ) {} -} + /** + * Metadata for a dormakaba Oracode device. + */ + class DormakabaOracodeMetadata + { + public static function from_json( + mixed $json, + ): DormakabaOracodeMetadata|null { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + door_id: $json->door_id ?? null, + door_is_wireless: $json->door_is_wireless ?? null, + door_name: $json->door_name ?? null, + iana_timezone: $json->iana_timezone ?? null, + predefined_time_slots: array_map( + fn( + $p, + ) => DormakabaOracodeMetadata\PredefinedTimeSlots::from_json( + $p, + ), + $json->predefined_time_slots ?? [], + ), + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); + } -/** - * Metadata for a Kisi device. - */ -class DeviceKisiMetadata -{ - public static function from_json(mixed $json): DeviceKisiMetadata|null - { - if (!$json) { - return null; - } - return new self( - description: $json->description ?? null, - lock_id: $json->lock_id ?? null, - lock_name: $json->lock_name ?? null, - place_name: $json->place_name ?? null, - ); - } - - public function __construct( - /** - * Description for a Kisi device. - */ - public string|null $description, - /** - * Lock ID for a Kisi device. - */ - public float|null $lock_id, - /** - * Lock name for a Kisi device. - */ - public string|null $lock_name, - /** - * Place name for a Kisi device. - */ - public string|null $place_name, - ) {} -} + public function __construct( + /** + * Device ID for a dormakaba Oracode device. + */ + public mixed $device_id, + /** + * Door ID for a dormakaba Oracode device. + */ + public float|null $door_id, + /** + * Indicates whether a door is wireless for a dormakaba Oracode device. + */ + public bool|null $door_is_wireless, + /** + * Door name for a dormakaba Oracode device. + */ + public string|null $door_name, + /** + * IANA time zone for a dormakaba Oracode device. + */ + public string|null $iana_timezone, + /** + * Predefined time slots for a dormakaba Oracode device. + */ + public array $predefined_time_slots, + /** + * Site ID for a dormakaba Oracode device. + * + * @deprecated Previously marked as "@DEPRECATED." + */ + public float|null $site_id, + /** + * Site name for a dormakaba Oracode device. + */ + public string|null $site_name, + ) {} + } -/** - * Metadata for a Korelock device. - */ -class DeviceKorelockMetadata -{ - public static function from_json(mixed $json): DeviceKorelockMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - firmware_version: $json->firmware_version ?? null, - location_id: $json->location_id ?? null, - model_code: $json->model_code ?? null, - serial_number: $json->serial_number ?? null, - wifi_signal_strength: $json->wifi_signal_strength ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Korelock device. - */ - public string|null $device_id, - /** - * Device name for a Korelock device. - */ - public string|null $device_name, - /** - * Firmware version for a Korelock device. - */ - public string|null $firmware_version, - /** - * Location ID for a Korelock device. Required for timebound access codes. - */ - public string|null $location_id, - /** - * Model code for a Korelock device. - */ - public string|null $model_code, - /** - * Serial number for a Korelock device. - */ - public string|null $serial_number, - /** - * WiFi signal strength (0-1) for a Korelock device. - */ - public float|null $wifi_signal_strength, - ) {} -} + /** + * Metadata for an ecobee device. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_name: $json->device_name ?? null, + ecobee_device_id: $json->ecobee_device_id ?? null, + ); + } -/** - * Metadata for a Kwikset device. - */ -class DeviceKwiksetMetadata -{ - public static function from_json(mixed $json): DeviceKwiksetMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - model_number: $json->model_number ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Kwikset device. - */ - public string|null $device_id, - /** - * Device name for a Kwikset device. - */ - public string|null $device_name, - /** - * Model number for a Kwikset device. - */ - public string|null $model_number, - ) {} -} + public function __construct( + /** + * Device name for an ecobee device. + */ + public string|null $device_name, + /** + * Device ID for an ecobee device. + */ + public string|null $ecobee_device_id, + ) {} + } -/** - * Latest sensor values for a Minut device. - */ -class DeviceLatestSensorValues -{ - public static function from_json(mixed $json): DeviceLatestSensorValues|null - { - if (!$json) { - return null; - } - return new self( - accelerometer_z: isset($json->accelerometer_z) - ? DeviceAccelerometerZ::from_json($json->accelerometer_z) - : null, - humidity: isset($json->humidity) - ? DeviceHumidity::from_json($json->humidity) - : null, - pressure: isset($json->pressure) - ? DevicePressure::from_json($json->pressure) - : null, - sound: isset($json->sound) - ? DeviceSound::from_json($json->sound) - : null, - temperature: isset($json->temperature) - ? DeviceTemperature::from_json($json->temperature) - : null, - ); - } - - public function __construct( - /** - * Latest accelerometer Z-axis reading for a Minut device. - */ - public DeviceAccelerometerZ|null $accelerometer_z, - /** - * Latest humidity reading for a Minut device. - */ - public DeviceHumidity|null $humidity, - /** - * Latest pressure reading for a Minut device. - */ - public DevicePressure|null $pressure, - /** - * Latest sound reading for a Minut device. - */ - public DeviceSound|null $sound, - /** - * Latest temperature reading for a Minut device. - */ - public DeviceTemperature|null $temperature, - ) {} -} + /** + * Metadata for a 4SUITES device. + */ + class FourSuitesMetadata + { + public static function from_json(mixed $json): FourSuitesMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + reclose_delay_in_seconds: $json->reclose_delay_in_seconds ?? + null, + ); + } -/** - * Location information for the device. - */ -class DeviceLocation -{ - public static function from_json(mixed $json): DeviceLocation|null - { - if (!$json) { - return null; - } - return new self( - location_name: $json->location_name ?? null, - time_zone: $json->time_zone ?? null, - timezone: $json->timezone ?? null, - ); - } - - public function __construct( - /** - * Name of the device location. - */ - public string|null $location_name, - /** - * Time zone of the device location. - */ - public string|null $time_zone, - /** - * Time zone of the device location. - * - * @deprecated Use `time_zone` instead. - */ - public string|null $timezone, - ) {} -} + public function __construct( + /** + * Device ID for a 4SUITES device. + */ + public float|null $device_id, + /** + * Device name for a 4SUITES device. + */ + public string|null $device_name, + /** + * Reclose delay, in seconds, for a 4SUITES device. + */ + public float|null $reclose_delay_in_seconds, + ) {} + } -/** - * Metadata for a Lockly device. - */ -class DeviceLocklyMetadata -{ - public static function from_json(mixed $json): DeviceLocklyMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Lockly device. - */ - public string|null $device_id, - /** - * Device name for a Lockly device. - */ - public string|null $device_name, - /** - * Model for a Lockly device. - */ - public string|null $model, - ) {} -} + /** + * Metadata for a Genie device. + */ + class GenieMetadata + { + public static function from_json(mixed $json): GenieMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_name: $json->device_name ?? null, + door_name: $json->door_name ?? null, + ); + } -/** - * Metadata for a Minut device. - */ -class DeviceMinutMetadata -{ - public static function from_json(mixed $json): DeviceMinutMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - latest_sensor_values: isset($json->latest_sensor_values) - ? DeviceLatestSensorValues::from_json( - $json->latest_sensor_values, - ) - : null, - ); - } - - public function __construct( - /** - * Device ID for a Minut device. - */ - public string|null $device_id, - /** - * Device name for a Minut device. - */ - public string|null $device_name, - /** - * Latest sensor values for a Minut device. - */ - public DeviceLatestSensorValues|null $latest_sensor_values, - ) {} -} + public function __construct( + /** + * Lock name for a Genie device. + */ + public string|null $device_name, + /** + * Door name for a Genie device. + */ + public string|null $door_name, + ) {} + } -/** - * Device model-related properties. - */ -class DeviceModel -{ - public static function from_json(mixed $json): DeviceModel|null - { - if (!$json) { - return null; - } - return new self( - accessory_keypad_supported: $json->accessory_keypad_supported ?? - null, - can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? - null, - display_name: $json->display_name ?? null, - has_built_in_keypad: $json->has_built_in_keypad ?? null, - manufacturer_display_name: $json->manufacturer_display_name ?? null, - offline_access_codes_supported: $json->offline_access_codes_supported ?? - null, - online_access_codes_supported: $json->online_access_codes_supported ?? - null, - ); - } - - public function __construct( - /** - * @deprecated use device.properties.model.can_connect_accessory_keypad - */ - public bool|null $accessory_keypad_supported, - /** - * Indicates whether the device can connect a accessory keypad. - */ - public bool|null $can_connect_accessory_keypad, - /** - * Display name of the device model. - */ - public string|null $display_name, - /** - * Indicates whether the device has a built in accessory keypad. - */ - public bool|null $has_built_in_keypad, - /** - * Display name that corresponds to the manufacturer-specific terminology for the device. - */ - public string|null $manufacturer_display_name, - /** - * @deprecated use device.can_program_offline_access_codes. - */ - public bool|null $offline_access_codes_supported, - /** - * @deprecated use device.can_program_online_access_codes. - */ - public bool|null $online_access_codes_supported, - ) {} -} + /** + * Metadata for a Honeywell Resideo device. + */ + class HoneywellResideoMetadata + { + public static function from_json( + mixed $json, + ): HoneywellResideoMetadata|null { + if (!$json) { + return null; + } + return new self( + device_name: $json->device_name ?? null, + honeywell_resideo_device_id: $json->honeywell_resideo_device_id ?? + null, + ); + } -/** - * Metadata for a Google Nest device. - */ -class DeviceNestMetadata -{ - public static function from_json(mixed $json): DeviceNestMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_custom_name: $json->device_custom_name ?? null, - device_name: $json->device_name ?? null, - display_name: $json->display_name ?? null, - nest_device_id: $json->nest_device_id ?? null, - ); - } - - public function __construct( - /** - * Custom device name for a Google Nest device. The device owner sets this value. - */ - public string|null $device_custom_name, - /** - * Device name for a Google Nest device. Google sets this value. - */ - public string|null $device_name, - /** - * Display name for a Google Nest device. - */ - public string|null $display_name, - /** - * Device ID for a Google Nest device. - */ - public string|null $nest_device_id, - ) {} -} + public function __construct( + /** + * Device name for a Honeywell Resideo device. + */ + public string|null $device_name, + /** + * Device ID for a Honeywell Resideo device. + */ + public string|null $honeywell_resideo_device_id, + ) {} + } -/** - * Metadata for a NoiseAware device. - */ -class DeviceNoiseawareMetadata -{ - public static function from_json(mixed $json): DeviceNoiseawareMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_model: $json->device_model ?? null, - device_name: $json->device_name ?? null, - noise_level_decibel: $json->noise_level_decibel ?? null, - noise_level_nrs: $json->noise_level_nrs ?? null, - ); - } - - public function __construct( - /** - * Device ID for a NoiseAware device. - */ - public string|null $device_id, - /** - * Device model for a NoiseAware device. - */ - public string|null $device_model, - /** - * Device name for a NoiseAware device. - */ - public string|null $device_name, - /** - * Noise level, in decibels, for a NoiseAware device. - */ - public float|null $noise_level_decibel, - /** - * Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. - */ - public float|null $noise_level_nrs, - ) {} -} + /** + * Metadata for an igloo device. + */ + class IglooMetadata + { + public static function from_json(mixed $json): IglooMetadata|null + { + if (!$json) { + return null; + } + return new self( + bridge_id: $json->bridge_id ?? null, + device_id: $json->device_id ?? null, + model: $json->model ?? null, + ); + } -/** - * Metadata for a Nuki device. - */ -class DeviceNukiMetadata -{ - public static function from_json(mixed $json): DeviceNukiMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - keypad_2_paired: $json->keypad_2_paired ?? null, - keypad_battery_critical: $json->keypad_battery_critical ?? null, - keypad_paired: $json->keypad_paired ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Nuki device. - */ - public string|null $device_id, - /** - * Device name for a Nuki device. - */ - public string|null $device_name, - /** - * Indicates whether keypad 2 is paired for a Nuki device. - */ - public bool|null $keypad_2_paired, - /** - * Indicates whether the keypad battery is in a critical state for a Nuki device. - */ - public bool|null $keypad_battery_critical, - /** - * Indicates whether the keypad is paired for a Nuki device. - */ - public bool|null $keypad_paired, - ) {} -} + public function __construct( + /** + * Bridge ID for an igloo device. + */ + public string|null $bridge_id, + /** + * Device ID for an igloo device. + */ + public string|null $device_id, + /** + * Model for an igloo device. + */ + public string|null $model, + ) {} + } -/** - * 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. - */ -class DeviceOfflineTimeFrameOptions -{ - public static function from_json( - mixed $json, - ): DeviceOfflineTimeFrameOptions|null { - if (!$json) { - return null; - } - return new self( - display_name: $json->display_name ?? null, - end_date_recurrence_rule: $json->end_date_recurrence_rule ?? null, - matching_start_end_time: $json->matching_start_end_time ?? null, - max_duration: $json->max_duration ?? null, - min_duration: $json->min_duration ?? null, - start_date_recurrence_rule: $json->start_date_recurrence_rule ?? - null, - time_pairs: array_map( - fn($t) => DeviceTimePairs::from_json($t), - $json->time_pairs ?? [], - ), - time_zone: $json->time_zone ?? null, - ); - } - - public function __construct( - /** - * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). - */ - public string|null $display_name, - /** - * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. - */ - public string|null $end_date_recurrence_rule, - /** - * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. - */ - public bool|null $matching_start_end_time, - /** - * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. - */ - public string|null $max_duration, - /** - * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. - */ - public string|null $min_duration, - /** - * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. - */ - public string|null $start_date_recurrence_rule, - /** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. - */ - public array $time_pairs, - /** - * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. - */ - public string|null $time_zone, - ) {} -} + /** + * Metadata for an igloohome device. + */ + class IgloohomeMetadata + { + public static function from_json(mixed $json): IgloohomeMetadata|null + { + if (!$json) { + return null; + } + return new self( + bridge_id: $json->bridge_id ?? null, + bridge_name: $json->bridge_name ?? null, + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + is_accessory_keypad_linked_to_bridge: $json->is_accessory_keypad_linked_to_bridge ?? + null, + keypad_id: $json->keypad_id ?? null, + ); + } -/** - * Metadata for an Omnitec device. - */ -class DeviceOmnitecMetadata -{ - public static function from_json(mixed $json): DeviceOmnitecMetadata|null - { - if (!$json) { - return null; - } - return new self( - has_gateway: $json->has_gateway ?? null, - lock_alias: $json->lock_alias ?? null, - lock_id: $json->lock_id ?? null, - lock_mac: $json->lock_mac ?? null, - lock_name: $json->lock_name ?? null, - time_zone: $json->time_zone ?? null, - timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, - ); - } - - public function __construct( - /** - * Whether the Omnitec lock has a connected gateway for remote operations. - */ - public bool|null $has_gateway, - /** - * Operator-assigned alias for an Omnitec device. - */ - public string|null $lock_alias, - /** - * Lock ID for an Omnitec device. - */ - public float|null $lock_id, - /** - * Bluetooth MAC address for an Omnitec device. - */ - public string|null $lock_mac, - /** - * Lock name for an Omnitec device. - */ - public string|null $lock_name, - /** - * IANA time zone for the Omnitec device, used to schedule time-bound access codes at the correct local time (accounting for DST). - */ - public string|null $time_zone, - /** - * Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. - */ - public float|null $timezone_raw_offset_ms, - ) {} -} + public function __construct( + /** + * Bridge ID for an igloohome device. + */ + public string|null $bridge_id, + /** + * Bridge name for an igloohome device. + */ + public string|null $bridge_name, + /** + * Device ID for an igloohome device. + */ + public string|null $device_id, + /** + * Device name for an igloohome device. + */ + public string|null $device_name, + /** + * Indicates whether a keypad is linked to a bridge for an igloohome device. + */ + public bool|null $is_accessory_keypad_linked_to_bridge, + /** + * Keypad ID for an igloohome device. + */ + public string|null $keypad_id, + ) {} + } -/** - * 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. - */ -class DeviceOnlineTimeFrameOptions -{ - public static function from_json( - mixed $json, - ): DeviceOnlineTimeFrameOptions|null { - if (!$json) { - return null; - } - return new self( - display_name: $json->display_name ?? null, - end_date_recurrence_rule: $json->end_date_recurrence_rule ?? null, - matching_start_end_time: $json->matching_start_end_time ?? null, - max_duration: $json->max_duration ?? null, - min_duration: $json->min_duration ?? null, - start_date_recurrence_rule: $json->start_date_recurrence_rule ?? - null, - time_pairs: array_map( - fn($t) => DeviceTimePairs::from_json($t), - $json->time_pairs ?? [], - ), - time_zone: $json->time_zone ?? null, - ); - } - - public function __construct( - /** - * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). - */ - public string|null $display_name, - /** - * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. - */ - public string|null $end_date_recurrence_rule, - /** - * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. - */ - public bool|null $matching_start_end_time, - /** - * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. - */ - public string|null $max_duration, - /** - * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. - */ - public string|null $min_duration, - /** - * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. - */ - public string|null $start_date_recurrence_rule, - /** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. - */ - public array $time_pairs, - /** - * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. - */ - public string|null $time_zone, - ) {} -} + /** + * Metadata for a KeyNest device. + */ + class KeynestMetadata + { + public static function from_json(mixed $json): KeynestMetadata|null + { + if (!$json) { + return null; + } + return new self( + address: $json->address ?? null, + current_or_last_store_id: $json->current_or_last_store_id ?? + null, + current_status: $json->current_status ?? null, + current_user_company: $json->current_user_company ?? null, + current_user_email: $json->current_user_email ?? null, + current_user_name: $json->current_user_name ?? null, + current_user_phone_number: $json->current_user_phone_number ?? + null, + default_office_id: $json->default_office_id ?? null, + device_name: $json->device_name ?? null, + fob_id: $json->fob_id ?? null, + handover_method: $json->handover_method ?? null, + has_photo: $json->has_photo ?? null, + is_quadient_locker: $json->is_quadient_locker ?? null, + key_id: $json->key_id ?? null, + key_notes: $json->key_notes ?? null, + keynest_app_user: $json->keynest_app_user ?? null, + last_movement: $json->last_movement ?? null, + property_id: $json->property_id ?? null, + property_postcode: $json->property_postcode ?? null, + status_type: $json->status_type ?? null, + subscription_plan: $json->subscription_plan ?? null, + ); + } -/** - * Array of thermostat daily program periods. - */ -class DevicePeriods -{ - public static function from_json(mixed $json): DevicePeriods|null - { - if (!$json) { - return null; - } - return new self( - climate_preset_key: $json->climate_preset_key ?? null, - starts_at_time: $json->starts_at_time ?? null, - ); - } - - public function __construct( - /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. - */ - public string|null $climate_preset_key, - /** - * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at_time, - ) {} -} + public function __construct( + /** + * Address for a KeyNest device. + */ + public string|null $address, + /** + * Current or last store ID for a KeyNest device. + */ + public float|null $current_or_last_store_id, + /** + * Current status for a KeyNest device. + */ + public string|null $current_status, + /** + * Current user company for a KeyNest device. + */ + public string|null $current_user_company, + /** + * Current user email for a KeyNest device. + */ + public string|null $current_user_email, + /** + * Current user name for a KeyNest device. + */ + public string|null $current_user_name, + /** + * Current user phone number for a KeyNest device. + */ + public string|null $current_user_phone_number, + /** + * Default office ID for a KeyNest device. + */ + public float|null $default_office_id, + /** + * Device name for a KeyNest device. + */ + public string|null $device_name, + /** + * Fob ID for a KeyNest device. + */ + public float|null $fob_id, + /** + * Handover method for a KeyNest device. + */ + public string|null $handover_method, + /** + * Whether the KeyNest device has a photo. + */ + public bool|null $has_photo, + /** + * Whether the key is in a locker that does not support the access codes API. + */ + public bool|null $is_quadient_locker, + /** + * Key ID for a KeyNest device. + */ + public string|null $key_id, + /** + * Key notes for a KeyNest device. + */ + public string|null $key_notes, + /** + * KeyNest app user for a KeyNest device. + */ + public string|null $keynest_app_user, + /** + * Last movement timestamp for a KeyNest device. + */ + public string|null $last_movement, + /** + * Property ID for a KeyNest device. + */ + public string|null $property_id, + /** + * Property postcode for a KeyNest device. + */ + public string|null $property_postcode, + /** + * Status type for a KeyNest device. + */ + public string|null $status_type, + /** + * Subscription plan for a KeyNest device. + */ + public string|null $subscription_plan, + ) {} + } -/** - * Predefined time slots for a dormakaba Oracode device. - */ -class DevicePredefinedTimeSlots -{ - public static function from_json( - mixed $json, - ): DevicePredefinedTimeSlots|null { - if (!$json) { - return null; - } - return new self( - check_in_time: $json->check_in_time ?? null, - check_out_time: $json->check_out_time ?? null, - dormakaba_oracode_user_level_id: $json->dormakaba_oracode_user_level_id ?? - null, - dormakaba_oracode_user_level_prefix: $json->dormakaba_oracode_user_level_prefix ?? - null, - is_24_hour: $json->is_24_hour ?? null, - is_biweekly_mode: $json->is_biweekly_mode ?? null, - is_master: $json->is_master ?? null, - is_one_shot: $json->is_one_shot ?? null, - name: $json->name ?? null, - prefix: $json->prefix ?? null, - ); - } - - public function __construct( - /** - * Check in time for a time slot for a dormakaba Oracode device. - */ - public string|null $check_in_time, - /** - * Checkout time for a time slot for a dormakaba Oracode device. - */ - public string|null $check_out_time, - /** - * ID of a user level for a dormakaba Oracode device. - */ - public string|null $dormakaba_oracode_user_level_id, - /** - * Prefix for a user level for a dormakaba Oracode device. - */ - public float|null $dormakaba_oracode_user_level_prefix, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a 24-hour time slot. - */ - public bool|null $is_24_hour, - /** - * Indicates whether a time slot for a dormakaba Oracode device is in biweekly mode. - */ - public bool|null $is_biweekly_mode, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a master time slot. - */ - public bool|null $is_master, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a one-shot time slot. - */ - public bool|null $is_one_shot, - /** - * Name of a time slot for a dormakaba Oracode device. - */ - public string|null $name, - /** - * Prefix for a time slot for a dormakaba Oracode device. - */ - public float|null $prefix, - ) {} -} + /** + * Metadata for a Kisi device. + */ + class KisiMetadata + { + public static function from_json(mixed $json): KisiMetadata|null + { + if (!$json) { + return null; + } + return new self( + description: $json->description ?? null, + lock_id: $json->lock_id ?? null, + lock_name: $json->lock_name ?? null, + place_name: $json->place_name ?? null, + ); + } -/** - * Latest pressure reading for a Minut device. - */ -class DevicePressure -{ - public static function from_json(mixed $json): DevicePressure|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest pressure reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest pressure reading for a Minut device. - */ - public float|null $value, - ) {} -} + public function __construct( + /** + * Description for a Kisi device. + */ + public string|null $description, + /** + * Lock ID for a Kisi device. + */ + public float|null $lock_id, + /** + * Lock name for a Kisi device. + */ + public string|null $lock_name, + /** + * Place name for a Kisi device. + */ + public string|null $place_name, + ) {} + } -/** - * Properties of the device. - */ -class DeviceProperties -{ - public static function from_json(mixed $json): DeviceProperties|null - { - if (!$json) { - return null; - } - return new self( - accessory_keypad: isset($json->accessory_keypad) - ? DeviceAccessoryKeypad::from_json($json->accessory_keypad) - : null, - active_thermostat_schedule: isset($json->active_thermostat_schedule) - ? DeviceActiveThermostatSchedule::from_json( - $json->active_thermostat_schedule, - ) - : null, - active_thermostat_schedule_id: $json->active_thermostat_schedule_id ?? - null, - akiles_metadata: isset($json->akiles_metadata) - ? DeviceAkilesMetadata::from_json($json->akiles_metadata) - : null, - appearance: isset($json->appearance) - ? DeviceAppearance::from_json($json->appearance) - : null, - aqara_metadata: isset($json->aqara_metadata) - ? DeviceAqaraMetadata::from_json($json->aqara_metadata) - : null, - assa_abloy_credential_service_metadata: isset( - $json->assa_abloy_credential_service_metadata, - ) - ? DeviceAssaAbloyCredentialServiceMetadata::from_json( - $json->assa_abloy_credential_service_metadata, - ) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? DeviceAssaAbloyVostioMetadata::from_json( - $json->assa_abloy_vostio_metadata, - ) - : null, - august_metadata: isset($json->august_metadata) - ? DeviceAugustMetadata::from_json($json->august_metadata) - : null, - auto_lock_delay_seconds: $json->auto_lock_delay_seconds ?? null, - auto_lock_enabled: $json->auto_lock_enabled ?? null, - available_climate_preset_modes: $json->available_climate_preset_modes ?? - null, - available_climate_presets: array_map( - fn($a) => DeviceAvailableClimatePresets::from_json($a), - $json->available_climate_presets ?? [], - ), - available_fan_mode_settings: $json->available_fan_mode_settings ?? - null, - available_hvac_mode_settings: $json->available_hvac_mode_settings ?? - null, - avigilon_alta_metadata: isset($json->avigilon_alta_metadata) - ? DeviceAvigilonAltaMetadata::from_json( - $json->avigilon_alta_metadata, - ) - : null, - backup_access_code_pool_enabled: $json->backup_access_code_pool_enabled ?? - null, - battery: isset($json->battery) - ? DeviceBattery::from_json($json->battery) - : null, - battery_level: $json->battery_level ?? null, - brivo_metadata: isset($json->brivo_metadata) - ? DeviceBrivoMetadata::from_json($json->brivo_metadata) - : null, - code_constraints: array_map( - fn($c) => DeviceCodeConstraints::from_json($c), - $json->code_constraints ?? [], - ), - controlbyweb_metadata: isset($json->controlbyweb_metadata) - ? DeviceControlbywebMetadata::from_json( - $json->controlbyweb_metadata, - ) - : null, - current_climate_setting: isset($json->current_climate_setting) - ? DeviceCurrentClimateSetting::from_json( - $json->current_climate_setting, - ) - : null, - currently_triggering_noise_threshold_ids: $json->currently_triggering_noise_threshold_ids ?? - null, - default_climate_setting: isset($json->default_climate_setting) - ? DeviceDefaultClimateSetting::from_json( - $json->default_climate_setting, - ) - : null, - door_open: $json->door_open ?? null, - dormakaba_oracode_metadata: isset($json->dormakaba_oracode_metadata) - ? DeviceDormakabaOracodeMetadata::from_json( - $json->dormakaba_oracode_metadata, - ) - : null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DeviceEcobeeMetadata::from_json($json->ecobee_metadata) - : null, - fallback_climate_preset_key: $json->fallback_climate_preset_key ?? - null, - fan_mode_setting: $json->fan_mode_setting ?? null, - four_suites_metadata: isset($json->four_suites_metadata) - ? DeviceFourSuitesMetadata::from_json( - $json->four_suites_metadata, - ) - : null, - genie_metadata: isset($json->genie_metadata) - ? DeviceGenieMetadata::from_json($json->genie_metadata) - : null, - has_direct_power: $json->has_direct_power ?? null, - has_native_entry_events: $json->has_native_entry_events ?? null, - honeywell_resideo_metadata: isset($json->honeywell_resideo_metadata) - ? DeviceHoneywellResideoMetadata::from_json( - $json->honeywell_resideo_metadata, - ) - : null, - igloo_metadata: isset($json->igloo_metadata) - ? DeviceIglooMetadata::from_json($json->igloo_metadata) - : null, - igloohome_metadata: isset($json->igloohome_metadata) - ? DeviceIgloohomeMetadata::from_json($json->igloohome_metadata) - : null, - image_alt_text: $json->image_alt_text ?? null, - image_url: $json->image_url ?? null, - is_cooling: $json->is_cooling ?? null, - is_fan_running: $json->is_fan_running ?? null, - is_heating: $json->is_heating ?? null, - is_temporary_manual_override_active: $json->is_temporary_manual_override_active ?? - null, - keynest_metadata: isset($json->keynest_metadata) - ? DeviceKeynestMetadata::from_json($json->keynest_metadata) - : null, - keypad_battery: isset($json->keypad_battery) - ? DeviceKeypadBattery::from_json($json->keypad_battery) - : null, - kisi_metadata: isset($json->kisi_metadata) - ? DeviceKisiMetadata::from_json($json->kisi_metadata) - : null, - korelock_metadata: isset($json->korelock_metadata) - ? DeviceKorelockMetadata::from_json($json->korelock_metadata) - : null, - kwikset_metadata: isset($json->kwikset_metadata) - ? DeviceKwiksetMetadata::from_json($json->kwikset_metadata) - : null, - locked: $json->locked ?? null, - lockly_metadata: isset($json->lockly_metadata) - ? DeviceLocklyMetadata::from_json($json->lockly_metadata) - : null, - manufacturer: $json->manufacturer ?? null, - max_active_codes_supported: $json->max_active_codes_supported ?? - null, - max_cooling_set_point_celsius: $json->max_cooling_set_point_celsius ?? - null, - max_cooling_set_point_fahrenheit: $json->max_cooling_set_point_fahrenheit ?? - null, - max_heating_set_point_celsius: $json->max_heating_set_point_celsius ?? - null, - max_heating_set_point_fahrenheit: $json->max_heating_set_point_fahrenheit ?? - null, - max_thermostat_daily_program_periods_per_day: $json->max_thermostat_daily_program_periods_per_day ?? - null, - max_unique_climate_presets_per_thermostat_weekly_program: $json->max_unique_climate_presets_per_thermostat_weekly_program ?? - null, - min_cooling_set_point_celsius: $json->min_cooling_set_point_celsius ?? - null, - min_cooling_set_point_fahrenheit: $json->min_cooling_set_point_fahrenheit ?? - null, - min_heating_cooling_delta_celsius: $json->min_heating_cooling_delta_celsius ?? - null, - min_heating_cooling_delta_fahrenheit: $json->min_heating_cooling_delta_fahrenheit ?? - null, - min_heating_set_point_celsius: $json->min_heating_set_point_celsius ?? - null, - min_heating_set_point_fahrenheit: $json->min_heating_set_point_fahrenheit ?? - null, - minut_metadata: isset($json->minut_metadata) - ? DeviceMinutMetadata::from_json($json->minut_metadata) - : null, - model: isset($json->model) - ? DeviceModel::from_json($json->model) - : null, - name: $json->name ?? null, - nest_metadata: isset($json->nest_metadata) - ? DeviceNestMetadata::from_json($json->nest_metadata) - : null, - noise_level_decibels: $json->noise_level_decibels ?? null, - noiseaware_metadata: isset($json->noiseaware_metadata) - ? DeviceNoiseawareMetadata::from_json( - $json->noiseaware_metadata, - ) - : null, - nuki_metadata: isset($json->nuki_metadata) - ? DeviceNukiMetadata::from_json($json->nuki_metadata) - : null, - offline_access_codes_enabled: $json->offline_access_codes_enabled ?? - null, - offline_time_frame_options: array_map( - fn($o) => DeviceOfflineTimeFrameOptions::from_json($o), - $json->offline_time_frame_options ?? [], - ), - omnitec_metadata: isset($json->omnitec_metadata) - ? DeviceOmnitecMetadata::from_json($json->omnitec_metadata) - : null, - online: $json->online ?? null, - online_access_codes_enabled: $json->online_access_codes_enabled ?? - null, - online_time_frame_options: array_map( - fn($o) => DeviceOnlineTimeFrameOptions::from_json($o), - $json->online_time_frame_options ?? [], - ), - relative_humidity: $json->relative_humidity ?? null, - ring_metadata: isset($json->ring_metadata) - ? DeviceRingMetadata::from_json($json->ring_metadata) - : null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? DeviceSaltoKsMetadata::from_json($json->salto_ks_metadata) - : null, - salto_metadata: isset($json->salto_metadata) - ? DeviceSaltoMetadata::from_json($json->salto_metadata) - : null, - salto_space_credential_service_metadata: isset( - $json->salto_space_credential_service_metadata, - ) - ? DeviceSaltoSpaceCredentialServiceMetadata::from_json( - $json->salto_space_credential_service_metadata, - ) - : null, - schlage_metadata: isset($json->schlage_metadata) - ? DeviceSchlageMetadata::from_json($json->schlage_metadata) - : null, - seam_bridge_metadata: isset($json->seam_bridge_metadata) - ? DeviceSeamBridgeMetadata::from_json( - $json->seam_bridge_metadata, - ) - : null, - sensi_metadata: isset($json->sensi_metadata) - ? DeviceSensiMetadata::from_json($json->sensi_metadata) - : null, - serial_number: $json->serial_number ?? null, - smartthings_metadata: isset($json->smartthings_metadata) - ? DeviceSmartthingsMetadata::from_json( - $json->smartthings_metadata, - ) - : null, - supported_code_lengths: $json->supported_code_lengths ?? null, - supports_accessory_keypad: $json->supports_accessory_keypad ?? null, - supports_backup_access_code_pool: $json->supports_backup_access_code_pool ?? - null, - supports_offline_access_codes: $json->supports_offline_access_codes ?? - null, - tado_metadata: isset($json->tado_metadata) - ? DeviceTadoMetadata::from_json($json->tado_metadata) - : null, - tedee_metadata: isset($json->tedee_metadata) - ? DeviceTedeeMetadata::from_json($json->tedee_metadata) - : null, - temperature_celsius: $json->temperature_celsius ?? null, - temperature_fahrenheit: $json->temperature_fahrenheit ?? null, - temperature_threshold: isset($json->temperature_threshold) - ? DeviceTemperatureThreshold::from_json( - $json->temperature_threshold, - ) - : null, - thermostat_daily_program_period_precision_minutes: $json->thermostat_daily_program_period_precision_minutes ?? - null, - thermostat_daily_programs: array_map( - fn($t) => DeviceThermostatDailyPrograms::from_json($t), - $json->thermostat_daily_programs ?? [], - ), - thermostat_weekly_program: isset($json->thermostat_weekly_program) - ? DeviceThermostatWeeklyProgram::from_json( - $json->thermostat_weekly_program, - ) - : null, - ttlock_metadata: isset($json->ttlock_metadata) - ? DeviceTtlockMetadata::from_json($json->ttlock_metadata) - : null, - two_n_metadata: isset($json->two_n_metadata) - ? DeviceTwoNMetadata::from_json($json->two_n_metadata) - : null, - ultraloq_metadata: isset($json->ultraloq_metadata) - ? DeviceUltraloqMetadata::from_json($json->ultraloq_metadata) - : null, - visionline_metadata: isset($json->visionline_metadata) - ? DeviceVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - wyze_metadata: isset($json->wyze_metadata) - ? DeviceWyzeMetadata::from_json($json->wyze_metadata) - : null, - ); - } - - public function __construct( - /** - * Accessory keypad properties and state. - */ - public DeviceAccessoryKeypad|null $accessory_keypad, - /** - * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - * - * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. - */ - public DeviceActiveThermostatSchedule|null $active_thermostat_schedule, - /** - * ID of the active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $active_thermostat_schedule_id, - /** - * Metadata for an Akiles device. - */ - public DeviceAkilesMetadata|null $akiles_metadata, - /** - * Appearance-related properties, as reported by the device. - */ - public DeviceAppearance|null $appearance, - /** - * Metadata for an Aqara device. - */ - public DeviceAqaraMetadata|null $aqara_metadata, - /** - * ASSA ABLOY Credential Service metadata for the phone. - */ - public DeviceAssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, - /** - * Metadata for an ASSA ABLOY Vostio system. - */ - public DeviceAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Metadata for an August device. - */ - public DeviceAugustMetadata|null $august_metadata, - /** - * The delay in seconds before the lock automatically locks after being unlocked. - */ - public float|null $auto_lock_delay_seconds, - /** - * Indicates whether automatic locking is enabled. - */ - public bool|null $auto_lock_enabled, - /** - * Climate preset modes that the thermostat supports, such as "home", "away", "wake", "sleep", "occupied", and "unoccupied". - */ - public array|null $available_climate_preset_modes, - /** - * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. - */ - public array $available_climate_presets, - /** - * Fan mode settings that the thermostat supports. - */ - public array|null $available_fan_mode_settings, - /** - * HVAC mode settings that the thermostat supports. - */ - public array|null $available_hvac_mode_settings, - /** - * Metadata for an Avigilon Alta system. - */ - public DeviceAvigilonAltaMetadata|null $avigilon_alta_metadata, - /** - * Indicates whether the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is currently enabled for the device. To disable it, set this to `false` using [/devices/update](https://docs.seam.co/api/devices/update). - */ - public bool|null $backup_access_code_pool_enabled, - /** - * Represents the current status of the battery charge level. - */ - public DeviceBattery|null $battery, - /** - * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. - */ - public float|null $battery_level, - /** - * Metadata for a Brivo device. - */ - public DeviceBrivoMetadata|null $brivo_metadata, - /** - * 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. - */ - public array $code_constraints, - /** - * Metadata for a ControlByWeb device. - */ - public DeviceControlbywebMetadata|null $controlbyweb_metadata, - /** - * Current climate setting. - */ - public DeviceCurrentClimateSetting|null $current_climate_setting, - /** - * Array of noise threshold IDs that are currently triggering. - */ - public array|null $currently_triggering_noise_threshold_ids, - /** - * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. - */ - public DeviceDefaultClimateSetting|null $default_climate_setting, - /** - * Indicates whether the door is open. - */ - public bool|null $door_open, - /** - * Metadata for a dormakaba Oracode device. - */ - public DeviceDormakabaOracodeMetadata|null $dormakaba_oracode_metadata, - /** - * Metadata for an ecobee device. - */ - public DeviceEcobeeMetadata|null $ecobee_metadata, - /** - * Key of the [fallback climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets/setting-the-fallback-climate-preset) for the thermostat. - */ - public string|null $fallback_climate_preset_key, - /** - * @deprecated Use `current_climate_setting.fan_mode_setting` instead. - */ - public string|null $fan_mode_setting, - /** - * Metadata for a 4SUITES device. - */ - public DeviceFourSuitesMetadata|null $four_suites_metadata, - /** - * Metadata for a Genie device. - */ - public DeviceGenieMetadata|null $genie_metadata, - /** - * Indicates whether the device has direct power. - */ - public bool|null $has_direct_power, - /** - * Indicates whether the device supports native entry events. - */ - public bool|null $has_native_entry_events, - /** - * Metadata for a Honeywell Resideo device. - */ - public DeviceHoneywellResideoMetadata|null $honeywell_resideo_metadata, - /** - * Metadata for an igloo device. - */ - public DeviceIglooMetadata|null $igloo_metadata, - /** - * Metadata for an igloohome device. - */ - public DeviceIgloohomeMetadata|null $igloohome_metadata, - /** - * Alt text for the device image. - */ - public string|null $image_alt_text, - /** - * Image URL for the device. - */ - public string|null $image_url, - /** - * Indicates whether the connected HVAC system is currently cooling, as reported by the thermostat. - */ - public bool|null $is_cooling, - /** - * Indicates whether the fan in the connected HVAC system is currently running, as reported by the thermostat. - */ - public bool|null $is_fan_running, - /** - * Indicates whether the connected HVAC system is currently heating, as reported by the thermostat. - */ - public bool|null $is_heating, - /** - * Indicates whether the current thermostat settings differ from the most recent active program or schedule that Seam activated. For this condition to occur, `current_climate_setting.manual_override_allowed` must also be `true`. - */ - public bool|null $is_temporary_manual_override_active, - /** - * Metadata for a KeyNest device. - */ - public DeviceKeynestMetadata|null $keynest_metadata, - /** - * Keypad battery status. - */ - public DeviceKeypadBattery|null $keypad_battery, - /** - * Metadata for a Kisi device. - */ - public DeviceKisiMetadata|null $kisi_metadata, - /** - * Metadata for a Korelock device. - */ - public DeviceKorelockMetadata|null $korelock_metadata, - /** - * Metadata for a Kwikset device. - */ - public DeviceKwiksetMetadata|null $kwikset_metadata, - /** - * Indicates whether the lock is locked. - */ - public bool|null $locked, - /** - * Metadata for a Lockly device. - */ - public DeviceLocklyMetadata|null $lockly_metadata, - /** - * 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. - */ - public string|null $manufacturer, - /** - * Maximum number of active access codes that the device supports. - */ - public float|null $max_active_codes_supported, - /** - * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. - */ - public float|null $max_cooling_set_point_celsius, - /** - * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. - */ - public float|null $max_cooling_set_point_fahrenheit, - /** - * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. - */ - public float|null $max_heating_set_point_celsius, - /** - * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. - */ - public float|null $max_heating_set_point_fahrenheit, - /** - * Maximum number of periods that the thermostat can support per day. For example, if the thermostat supports 4 periods per day, this value is 4. - */ - public float|null $max_thermostat_daily_program_periods_per_day, - /** - * Maximum number of climate presets that the thermostat can support for weekly programming. - */ - public float|null $max_unique_climate_presets_per_thermostat_weekly_program, - /** - * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. - */ - public float|null $min_cooling_set_point_celsius, - /** - * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. - */ - public float|null $min_cooling_set_point_fahrenheit, - /** - * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °C between the cooling and heating set points when in heat-cool (auto) mode. - */ - public float|null $min_heating_cooling_delta_celsius, - /** - * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °F between the cooling and heating set points when in heat-cool (auto) mode. - */ - public float|null $min_heating_cooling_delta_fahrenheit, - /** - * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. - */ - public float|null $min_heating_set_point_celsius, - /** - * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. - */ - public float|null $min_heating_set_point_fahrenheit, - /** - * Metadata for a Minut device. - */ - public DeviceMinutMetadata|null $minut_metadata, - /** - * Device model-related properties. - */ - public DeviceModel|null $model, - /** - * Name of the device. - * - * @deprecated use device.display_name instead - */ - public string|null $name, - /** - * Metadata for a Google Nest device. - */ - public DeviceNestMetadata|null $nest_metadata, - /** - * Indicates current noise level in decibels, if the device supports noise detection. - */ - public float|null $noise_level_decibels, - /** - * Metadata for a NoiseAware device. - */ - public DeviceNoiseawareMetadata|null $noiseaware_metadata, - /** - * Metadata for a Nuki device. - */ - public DeviceNukiMetadata|null $nuki_metadata, - /** - * Indicates whether it is currently possible to use offline access codes for the device. - * - * @deprecated use device.can_program_offline_access_codes - */ - public bool|null $offline_access_codes_enabled, - /** - * 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. - */ - public array $offline_time_frame_options, - /** - * Metadata for an Omnitec device. - */ - public DeviceOmnitecMetadata|null $omnitec_metadata, - /** - * Indicates whether the device is online. - */ - public bool|null $online, - /** - * Indicates whether it is currently possible to use online access codes for the device. - * - * @deprecated use device.can_program_online_access_codes - */ - public bool|null $online_access_codes_enabled, - /** - * 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. - */ - public array $online_time_frame_options, - /** - * Reported relative humidity, as a value between 0 and 1, inclusive. - */ - public float|null $relative_humidity, - /** - * Metadata for a Ring device. - */ - public DeviceRingMetadata|null $ring_metadata, - /** - * Metadata for a Salto KS device. - */ - public DeviceSaltoKsMetadata|null $salto_ks_metadata, - /** - * Metada for a Salto device. - * - * @deprecated Use `salto_ks_metadata ` instead. - */ - public DeviceSaltoMetadata|null $salto_metadata, - /** - * Salto Space credential service metadata for the phone. - */ - public DeviceSaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, - /** - * Metadata for a Schlage device. - */ - public DeviceSchlageMetadata|null $schlage_metadata, - /** - * Metadata for Seam Bridge. - */ - public DeviceSeamBridgeMetadata|null $seam_bridge_metadata, - /** - * Metadata for a Sensi device. - */ - public DeviceSensiMetadata|null $sensi_metadata, - /** - * Serial number of the device. - */ - public string|null $serial_number, - /** - * Metadata for a SmartThings device. - */ - public DeviceSmartthingsMetadata|null $smartthings_metadata, - /** - * Supported code lengths for access codes. - */ - public array|null $supported_code_lengths, - /** - * @deprecated use device.properties.model.can_connect_accessory_keypad - */ - public bool|null $supports_accessory_keypad, - /** - * Indicates whether the device supports a [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes). - */ - public bool|null $supports_backup_access_code_pool, - /** - * @deprecated use offline_access_codes_enabled - */ - public bool|null $supports_offline_access_codes, - /** - * Metadata for a tado° device. - */ - public DeviceTadoMetadata|null $tado_metadata, - /** - * Metadata for a Tedee device. - */ - public DeviceTedeeMetadata|null $tedee_metadata, - /** - * Reported temperature in °C. - */ - public float|null $temperature_celsius, - /** - * Reported temperature in °F. - */ - public float|null $temperature_fahrenheit, - /** - * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public DeviceTemperatureThreshold|null $temperature_threshold, - /** - * Precision of the thermostat's period in minutes. For example, if the thermostat supports 15-minute periods, this value is 15. All values are relative to the top of the hour, so for 15 minutes, the periods would be 0, 15, 30, and 45 minutes past the hour. - */ - public float|null $thermostat_daily_program_period_precision_minutes, - /** - * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. - */ - public array $thermostat_daily_programs, - /** - * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. - */ - public DeviceThermostatWeeklyProgram|null $thermostat_weekly_program, - /** - * Metadata for a TTLock device. - */ - public DeviceTtlockMetadata|null $ttlock_metadata, - /** - * Metadata for a 2N device. - */ - public DeviceTwoNMetadata|null $two_n_metadata, - /** - * Metadata for an Ultraloq device. - */ - public DeviceUltraloqMetadata|null $ultraloq_metadata, - /** - * Metadata for an ASSA ABLOY Visionline system. - */ - public DeviceVisionlineMetadata|null $visionline_metadata, - /** - * Metadata for a Wyze device. - */ - public DeviceWyzeMetadata|null $wyze_metadata, - ) {} -} + /** + * Metadata for a Korelock device. + */ + class KorelockMetadata + { + public static function from_json(mixed $json): KorelockMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + firmware_version: $json->firmware_version ?? null, + location_id: $json->location_id ?? null, + model_code: $json->model_code ?? null, + serial_number: $json->serial_number ?? null, + wifi_signal_strength: $json->wifi_signal_strength ?? null, + ); + } -/** - * Metadata for a Ring device. - */ -class DeviceRingMetadata -{ - public static function from_json(mixed $json): DeviceRingMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Ring device. - */ - public string|null $device_id, - /** - * Device name for a Ring device. - */ - public string|null $device_name, - ) {} -} + public function __construct( + /** + * Device ID for a Korelock device. + */ + public string|null $device_id, + /** + * Device name for a Korelock device. + */ + public string|null $device_name, + /** + * Firmware version for a Korelock device. + */ + public string|null $firmware_version, + /** + * Location ID for a Korelock device. Required for timebound access codes. + */ + public string|null $location_id, + /** + * Model code for a Korelock device. + */ + public string|null $model_code, + /** + * Serial number for a Korelock device. + */ + public string|null $serial_number, + /** + * WiFi signal strength (0-1) for a Korelock device. + */ + public float|null $wifi_signal_strength, + ) {} + } -/** - * Metadata for a Salto KS device. - */ -class DeviceSaltoKsMetadata -{ - public static function from_json(mixed $json): DeviceSaltoKsMetadata|null - { - if (!$json) { - return null; - } - return new self( - battery_level: $json->battery_level ?? null, - customer_reference: $json->customer_reference ?? null, - has_custom_pin_subscription: $json->has_custom_pin_subscription ?? - null, - lock_id: $json->lock_id ?? null, - lock_type: $json->lock_type ?? null, - locked_state: $json->locked_state ?? null, - model: $json->model ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - - public function __construct( - /** - * Battery level for a Salto KS device. - */ - public string|null $battery_level, - /** - * Customer reference for a Salto KS device. - */ - public string|null $customer_reference, - /** - * Indicates whether the site has a Salto KS subscription that supports custom PINs. - */ - public bool|null $has_custom_pin_subscription, - /** - * Lock ID for a Salto KS device. - */ - public string|null $lock_id, - /** - * Lock type for a Salto KS device. - */ - public string|null $lock_type, - /** - * Locked state for a Salto KS device. - */ - public string|null $locked_state, - /** - * Model for a Salto KS device. - */ - public string|null $model, - /** - * Site ID for the Salto KS site to which the device belongs. - */ - public string|null $site_id, - /** - * Site name for the Salto KS site to which the device belongs. - */ - public string|null $site_name, - ) {} -} + /** + * Metadata for a Kwikset device. + */ + class KwiksetMetadata + { + public static function from_json(mixed $json): KwiksetMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + model_number: $json->model_number ?? null, + ); + } -/** - * Metada for a Salto device. - */ -class DeviceSaltoMetadata -{ - public static function from_json(mixed $json): DeviceSaltoMetadata|null - { - if (!$json) { - return null; - } - return new self( - battery_level: $json->battery_level ?? null, - customer_reference: $json->customer_reference ?? null, - lock_id: $json->lock_id ?? null, - lock_type: $json->lock_type ?? null, - locked_state: $json->locked_state ?? null, - model: $json->model ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - - public function __construct( - /** - * Battery level for a Salto device. - */ - public string|null $battery_level, - /** - * Customer reference for a Salto device. - */ - public string|null $customer_reference, - /** - * Lock ID for a Salto device. - */ - public string|null $lock_id, - /** - * Lock type for a Salto device. - */ - public string|null $lock_type, - /** - * Locked state for a Salto device. - */ - public string|null $locked_state, - /** - * Model for a Salto device. - */ - public string|null $model, - /** - * Site ID for the Salto KS site to which the device belongs. - */ - public string|null $site_id, - /** - * Site name for the Salto KS site to which the device belongs. - */ - public string|null $site_name, - ) {} -} + public function __construct( + /** + * Device ID for a Kwikset device. + */ + public string|null $device_id, + /** + * Device name for a Kwikset device. + */ + public string|null $device_name, + /** + * Model number for a Kwikset device. + */ + public string|null $model_number, + ) {} + } -/** - * Salto Space credential service metadata for the phone. - */ -class DeviceSaltoSpaceCredentialServiceMetadata -{ - public static function from_json( - mixed $json, - ): DeviceSaltoSpaceCredentialServiceMetadata|null { - if (!$json) { - return null; - } - return new self(has_active_phone: $json->has_active_phone ?? null); - } - - public function __construct( - /** - * Indicates whether the credential service has an active associated phone. - */ - public bool|null $has_active_phone, - ) {} -} + /** + * Metadata for a Lockly device. + */ + class LocklyMetadata + { + public static function from_json(mixed $json): LocklyMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + model: $json->model ?? null, + ); + } -/** - * Metadata for a Schlage device. - */ -class DeviceSchlageMetadata -{ - public static function from_json(mixed $json): DeviceSchlageMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Schlage device. - */ - public string|null $device_id, - /** - * Device name for a Schlage device. - */ - public string|null $device_name, - /** - * Model for a Schlage device. - */ - public string|null $model, - ) {} -} + public function __construct( + /** + * Device ID for a Lockly device. + */ + public string|null $device_id, + /** + * Device name for a Lockly device. + */ + public string|null $device_name, + /** + * Model for a Lockly device. + */ + public string|null $model, + ) {} + } -/** - * Metadata for Seam Bridge. - */ -class DeviceSeamBridgeMetadata -{ - public static function from_json(mixed $json): DeviceSeamBridgeMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_num: $json->device_num ?? null, - name: $json->name ?? null, - unlock_method: $json->unlock_method ?? null, - ); - } - - public function __construct( - /** - * Device number for Seam Bridge. - */ - public float|null $device_num, - /** - * Name for Seam Bridge. - */ - public string|null $name, - /** - * Unlock method for Seam Bridge. - */ - public string|null $unlock_method, - ) {} -} + /** + * Metadata for a Minut device. + */ + class MinutMetadata + { + public static function from_json(mixed $json): MinutMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + latest_sensor_values: isset($json->latest_sensor_values) + ? MinutMetadata\LatestSensorValues::from_json( + $json->latest_sensor_values, + ) + : null, + ); + } -/** - * Metadata for a Sensi device. - */ -class DeviceSensiMetadata -{ - public static function from_json(mixed $json): DeviceSensiMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - dual_setpoints_not_supported: $json->dual_setpoints_not_supported ?? - null, - enforced_setpoint_range_celsius: $json->enforced_setpoint_range_celsius ?? - null, - product_type: $json->product_type ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Sensi device. - */ - public string|null $device_id, - /** - * Device name for a Sensi device. - */ - public string|null $device_name, - /** - * Set to true when the device does not support the /dual-setpoints API endpoint. - */ - public bool|null $dual_setpoints_not_supported, - /** - * Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. - */ - public array|null $enforced_setpoint_range_celsius, - /** - * Product type for a Sensi device. - */ - public string|null $product_type, - ) {} -} + public function __construct( + /** + * Device ID for a Minut device. + */ + public string|null $device_id, + /** + * Device name for a Minut device. + */ + public string|null $device_name, + /** + * Latest sensor values for a Minut device. + */ + public MinutMetadata\LatestSensorValues|null $latest_sensor_values, + ) {} + } -/** - * Metadata for a SmartThings device. - */ -class DeviceSmartthingsMetadata -{ - public static function from_json( - mixed $json, - ): DeviceSmartthingsMetadata|null { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - location_id: $json->location_id ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Device ID for a SmartThings device. - */ - public string|null $device_id, - /** - * Device name for a SmartThings device. - */ - public string|null $device_name, - /** - * Location ID for a SmartThings device. - */ - public string|null $location_id, - /** - * Model for a SmartThings device. - */ - public string|null $model, - ) {} -} + /** + * Metadata for a Google Nest device. + */ + class NestMetadata + { + public static function from_json(mixed $json): NestMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_custom_name: $json->device_custom_name ?? null, + device_name: $json->device_name ?? null, + display_name: $json->display_name ?? null, + nest_device_id: $json->nest_device_id ?? null, + ); + } -/** - * Latest sound reading for a Minut device. - */ -class DeviceSound -{ - public static function from_json(mixed $json): DeviceSound|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest sound reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest sound reading for a Minut device. - */ - public float|null $value, - ) {} -} + public function __construct( + /** + * Custom device name for a Google Nest device. The device owner sets this value. + */ + public string|null $device_custom_name, + /** + * Device name for a Google Nest device. Google sets this value. + */ + public string|null $device_name, + /** + * Display name for a Google Nest device. + */ + public string|null $display_name, + /** + * Device ID for a Google Nest device. + */ + public string|null $nest_device_id, + ) {} + } -/** - * Metadata for a tado° device. - */ -class DeviceTadoMetadata -{ - public static function from_json(mixed $json): DeviceTadoMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_type: $json->device_type ?? null, - serial_no: $json->serial_no ?? null, - ); - } - - public function __construct( - /** - * Device type for a tado° device. - */ - public string|null $device_type, - /** - * Serial number for a tado° device. - */ - public string|null $serial_no, - ) {} -} + /** + * Metadata for a NoiseAware device. + */ + class NoiseawareMetadata + { + public static function from_json(mixed $json): NoiseawareMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_model: $json->device_model ?? null, + device_name: $json->device_name ?? null, + noise_level_decibel: $json->noise_level_decibel ?? null, + noise_level_nrs: $json->noise_level_nrs ?? null, + ); + } + + public function __construct( + /** + * Device ID for a NoiseAware device. + */ + public string|null $device_id, + /** + * Device model for a NoiseAware device. + */ + public string|null $device_model, + /** + * Device name for a NoiseAware device. + */ + public string|null $device_name, + /** + * Noise level, in decibels, for a NoiseAware device. + */ + public float|null $noise_level_decibel, + /** + * Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. + */ + public float|null $noise_level_nrs, + ) {} + } + + /** + * Metadata for a Nuki device. + */ + class NukiMetadata + { + public static function from_json(mixed $json): NukiMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + keypad_2_paired: $json->keypad_2_paired ?? null, + keypad_battery_critical: $json->keypad_battery_critical ?? null, + keypad_paired: $json->keypad_paired ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Nuki device. + */ + public string|null $device_id, + /** + * Device name for a Nuki device. + */ + public string|null $device_name, + /** + * Indicates whether keypad 2 is paired for a Nuki device. + */ + public bool|null $keypad_2_paired, + /** + * Indicates whether the keypad battery is in a critical state for a Nuki device. + */ + public bool|null $keypad_battery_critical, + /** + * Indicates whether the keypad is paired for a Nuki device. + */ + public bool|null $keypad_paired, + ) {} + } + + /** + * Metadata for an Omnitec device. + */ + class OmnitecMetadata + { + public static function from_json(mixed $json): OmnitecMetadata|null + { + if (!$json) { + return null; + } + return new self( + has_gateway: $json->has_gateway ?? null, + lock_alias: $json->lock_alias ?? null, + lock_id: $json->lock_id ?? null, + lock_mac: $json->lock_mac ?? null, + lock_name: $json->lock_name ?? null, + time_zone: $json->time_zone ?? null, + timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, + ); + } + + public function __construct( + /** + * Whether the Omnitec lock has a connected gateway for remote operations. + */ + public bool|null $has_gateway, + /** + * Operator-assigned alias for an Omnitec device. + */ + public string|null $lock_alias, + /** + * Lock ID for an Omnitec device. + */ + public float|null $lock_id, + /** + * Bluetooth MAC address for an Omnitec device. + */ + public string|null $lock_mac, + /** + * Lock name for an Omnitec device. + */ + public string|null $lock_name, + /** + * IANA time zone for the Omnitec device, used to schedule time-bound access codes at the correct local time (accounting for DST). + */ + public string|null $time_zone, + /** + * Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. + */ + public float|null $timezone_raw_offset_ms, + ) {} + } + + /** + * Metadata for a Ring device. + */ + class RingMetadata + { + public static function from_json(mixed $json): RingMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Ring device. + */ + public string|null $device_id, + /** + * Device name for a Ring device. + */ + public string|null $device_name, + ) {} + } + + /** + * Metadata for a Salto KS device. + */ + class SaltoKsMetadata + { + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self( + battery_level: $json->battery_level ?? null, + customer_reference: $json->customer_reference ?? null, + has_custom_pin_subscription: $json->has_custom_pin_subscription ?? + null, + lock_id: $json->lock_id ?? null, + lock_type: $json->lock_type ?? null, + locked_state: $json->locked_state ?? null, + model: $json->model ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); + } + + public function __construct( + /** + * Battery level for a Salto KS device. + */ + public string|null $battery_level, + /** + * Customer reference for a Salto KS device. + */ + public string|null $customer_reference, + /** + * Indicates whether the site has a Salto KS subscription that supports custom PINs. + */ + public bool|null $has_custom_pin_subscription, + /** + * Lock ID for a Salto KS device. + */ + public string|null $lock_id, + /** + * Lock type for a Salto KS device. + */ + public string|null $lock_type, + /** + * Locked state for a Salto KS device. + */ + public string|null $locked_state, + /** + * Model for a Salto KS device. + */ + public string|null $model, + /** + * Site ID for the Salto KS site to which the device belongs. + */ + public string|null $site_id, + /** + * Site name for the Salto KS site to which the device belongs. + */ + public string|null $site_name, + ) {} + } + + /** + * Metada for a Salto device. + * + * @deprecated Use `salto_ks_metadata ` instead. + */ + class SaltoMetadata + { + public static function from_json(mixed $json): SaltoMetadata|null + { + if (!$json) { + return null; + } + return new self( + battery_level: $json->battery_level ?? null, + customer_reference: $json->customer_reference ?? null, + lock_id: $json->lock_id ?? null, + lock_type: $json->lock_type ?? null, + locked_state: $json->locked_state ?? null, + model: $json->model ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); + } + + public function __construct( + /** + * Battery level for a Salto device. + */ + public string|null $battery_level, + /** + * Customer reference for a Salto device. + */ + public string|null $customer_reference, + /** + * Lock ID for a Salto device. + */ + public string|null $lock_id, + /** + * Lock type for a Salto device. + */ + public string|null $lock_type, + /** + * Locked state for a Salto device. + */ + public string|null $locked_state, + /** + * Model for a Salto device. + */ + public string|null $model, + /** + * Site ID for the Salto KS site to which the device belongs. + */ + public string|null $site_id, + /** + * Site name for the Salto KS site to which the device belongs. + */ + public string|null $site_name, + ) {} + } + + /** + * Metadata for a Schlage device. + */ + class SchlageMetadata + { + public static function from_json(mixed $json): SchlageMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + model: $json->model ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Schlage device. + */ + public string|null $device_id, + /** + * Device name for a Schlage device. + */ + public string|null $device_name, + /** + * Model for a Schlage device. + */ + public string|null $model, + ) {} + } -/** - * Metadata for a Tedee device. - */ -class DeviceTedeeMetadata -{ - public static function from_json(mixed $json): DeviceTedeeMetadata|null - { - if (!$json) { - return null; - } - return new self( - bridge_id: $json->bridge_id ?? null, - bridge_name: $json->bridge_name ?? null, - device_id: $json->device_id ?? null, - device_model: $json->device_model ?? null, - device_name: $json->device_name ?? null, - keypad_id: $json->keypad_id ?? null, - serial_number: $json->serial_number ?? null, - ); - } - - public function __construct( - /** - * Bridge ID for a Tedee device. - */ - public float|null $bridge_id, - /** - * Bridge name for a Tedee device. - */ - public string|null $bridge_name, - /** - * Device ID for a Tedee device. - */ - public float|null $device_id, - /** - * Device model for a Tedee device. - */ - public string|null $device_model, - /** - * Device name for a Tedee device. - */ - public string|null $device_name, - /** - * Keypad ID for a Tedee device. - */ - public float|null $keypad_id, - /** - * Serial number for a Tedee device. - */ - public string|null $serial_number, - ) {} + /** + * Metadata for Seam Bridge. + */ + class SeamBridgeMetadata + { + public static function from_json(mixed $json): SeamBridgeMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_num: $json->device_num ?? null, + name: $json->name ?? null, + unlock_method: $json->unlock_method ?? null, + ); + } + + public function __construct( + /** + * Device number for Seam Bridge. + */ + public float|null $device_num, + /** + * Name for Seam Bridge. + */ + public string|null $name, + /** + * Unlock method for Seam Bridge. + */ + public string|null $unlock_method, + ) {} + } + + /** + * Metadata for a Sensi device. + */ + class SensiMetadata + { + public static function from_json(mixed $json): SensiMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + dual_setpoints_not_supported: $json->dual_setpoints_not_supported ?? + null, + enforced_setpoint_range_celsius: $json->enforced_setpoint_range_celsius ?? + null, + product_type: $json->product_type ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Sensi device. + */ + public string|null $device_id, + /** + * Device name for a Sensi device. + */ + public string|null $device_name, + /** + * Set to true when the device does not support the /dual-setpoints API endpoint. + */ + public bool|null $dual_setpoints_not_supported, + /** + * Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. + */ + public array|null $enforced_setpoint_range_celsius, + /** + * Product type for a Sensi device. + */ + public string|null $product_type, + ) {} + } + + /** + * Metadata for a SmartThings device. + */ + class SmartthingsMetadata + { + public static function from_json(mixed $json): SmartthingsMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + location_id: $json->location_id ?? null, + model: $json->model ?? null, + ); + } + + public function __construct( + /** + * Device ID for a SmartThings device. + */ + public string|null $device_id, + /** + * Device name for a SmartThings device. + */ + public string|null $device_name, + /** + * Location ID for a SmartThings device. + */ + public string|null $location_id, + /** + * Model for a SmartThings device. + */ + public string|null $model, + ) {} + } + + /** + * Metadata for a tado° device. + */ + class TadoMetadata + { + public static function from_json(mixed $json): TadoMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_type: $json->device_type ?? null, + serial_no: $json->serial_no ?? null, + ); + } + + public function __construct( + /** + * Device type for a tado° device. + */ + public string|null $device_type, + /** + * Serial number for a tado° device. + */ + public string|null $serial_no, + ) {} + } + + /** + * Metadata for a Tedee device. + */ + class TedeeMetadata + { + public static function from_json(mixed $json): TedeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + bridge_id: $json->bridge_id ?? null, + bridge_name: $json->bridge_name ?? null, + device_id: $json->device_id ?? null, + device_model: $json->device_model ?? null, + device_name: $json->device_name ?? null, + keypad_id: $json->keypad_id ?? null, + serial_number: $json->serial_number ?? null, + ); + } + + public function __construct( + /** + * Bridge ID for a Tedee device. + */ + public float|null $bridge_id, + /** + * Bridge name for a Tedee device. + */ + public string|null $bridge_name, + /** + * Device ID for a Tedee device. + */ + public float|null $device_id, + /** + * Device model for a Tedee device. + */ + public string|null $device_model, + /** + * Device name for a Tedee device. + */ + public string|null $device_name, + /** + * Keypad ID for a Tedee device. + */ + public float|null $keypad_id, + /** + * Serial number for a Tedee device. + */ + public string|null $serial_number, + ) {} + } + + /** + * Metadata for a TTLock device. + */ + class TtlockMetadata + { + public static function from_json(mixed $json): TtlockMetadata|null + { + if (!$json) { + return null; + } + return new self( + feature_value: $json->feature_value ?? null, + features: isset($json->features) + ? TtlockMetadata\Features::from_json($json->features) + : null, + has_gateway: $json->has_gateway ?? null, + lock_alias: $json->lock_alias ?? null, + lock_id: $json->lock_id ?? null, + timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, + wireless_keypads: array_map( + fn($w) => TtlockMetadata\WirelessKeypads::from_json($w), + $json->wireless_keypads ?? [], + ), + ); + } + + public function __construct( + /** + * Feature value for a TTLock device. + */ + public string|null $feature_value, + /** + * Features for a TTLock device. + */ + public TtlockMetadata\Features|null $features, + /** + * Indicates whether a TTLock device has a gateway. + */ + public bool|null $has_gateway, + /** + * Lock alias for a TTLock device. + */ + public string|null $lock_alias, + /** + * Lock ID for a TTLock device. + */ + public float|null $lock_id, + /** + * Lock-side timezone offset in milliseconds east of UTC, as configured in the TTLock app. Source of truth for the lock's wall-clock interpretation of access code start/end times — a misconfigured value here is the typical cause of customer "codes offset by N hours" reports. Diagnostic only; Seam does not convert times based on this value. + */ + public float|null $timezone_raw_offset_ms, + /** + * Wireless keypads for a TTLock device. + */ + public array $wireless_keypads, + ) {} + } + + /** + * Metadata for a 2N device. + */ + class TwoNMetadata + { + public static function from_json(mixed $json): TwoNMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + ); + } + + public function __construct( + /** + * Device ID for a 2N device. + */ + public float|null $device_id, + /** + * Device name for a 2N device. + */ + public string|null $device_name, + ) {} + } + + /** + * Metadata for an Ultraloq device. + */ + class UltraloqMetadata + { + public static function from_json(mixed $json): UltraloqMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + device_type: $json->device_type ?? null, + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( + /** + * Device ID for an Ultraloq device. + */ + public string|null $device_id, + /** + * Device name for an Ultraloq device. + */ + public string|null $device_name, + /** + * Device type for an Ultraloq device. + */ + public string|null $device_type, + /** + * IANA timezone for the Ultraloq device. + */ + public string|null $time_zone, + ) {} + } + + /** + * Metadata for an ASSA ABLOY Visionline system. + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self(encoder_id: $json->encoder_id ?? null); + } + + public function __construct( + /** + * Encoder ID for an ASSA ABLOY Visionline system. + */ + public string|null $encoder_id, + ) {} + } + + /** + * Metadata for a Wyze device. + */ + class WyzeMetadata + { + public static function from_json(mixed $json): WyzeMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_info_model: $json->device_info_model ?? null, + device_name: $json->device_name ?? null, + keypad_uuid: $json->keypad_uuid ?? null, + locker_status_hardlock: $json->locker_status_hardlock ?? null, + product_model: $json->product_model ?? null, + product_name: $json->product_name ?? null, + product_type: $json->product_type ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Wyze device. + */ + public string|null $device_id, + /** + * Device information model for a Wyze device. + */ + public string|null $device_info_model, + /** + * Device name for a Wyze device. + */ + public string|null $device_name, + /** + * Keypad UUID for a Wyze device. + */ + public string|null $keypad_uuid, + /** + * Locker status (hardlock) for a Wyze device. + */ + public float|null $locker_status_hardlock, + /** + * Product model for a Wyze device. + */ + public string|null $product_model, + /** + * Product name for a Wyze device. + */ + public string|null $product_name, + /** + * Product type for a Wyze device. + */ + public string|null $product_type, + ) {} + } + + /** + * 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. + */ + class CodeConstraints + { + public static function from_json(mixed $json): CodeConstraints|null + { + if (!$json) { + return null; + } + return new self( + constraint_type: $json->constraint_type ?? null, + max_length: $json->max_length ?? null, + min_length: $json->min_length ?? null, + ); + } + + public function __construct( + public string|null $constraint_type, + /** + * Maximum name length constraint for access codes. + */ + public float|null $max_length, + /** + * Minimum name length constraint for access codes. + */ + public float|null $min_length, + ) {} + } + + /** + * Keypad battery status. + */ + class KeypadBattery + { + public static function from_json(mixed $json): KeypadBattery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); + } + + public function __construct( + /** + * Keypad battery charge level. + */ + public float|null $level, + ) {} + } + + /** + * 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. + */ + class OfflineTimeFrameOptions + { + public static function from_json( + mixed $json, + ): OfflineTimeFrameOptions|null { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_date_recurrence_rule: $json->end_date_recurrence_rule ?? + null, + matching_start_end_time: $json->matching_start_end_time ?? null, + max_duration: $json->max_duration ?? null, + min_duration: $json->min_duration ?? null, + start_date_recurrence_rule: $json->start_date_recurrence_rule ?? + null, + time_pairs: array_map( + fn($t) => OfflineTimeFrameOptions\TimePairs::from_json($t), + $json->time_pairs ?? [], + ), + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( + /** + * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). + */ + public string|null $display_name, + /** + * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $end_date_recurrence_rule, + /** + * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. + */ + public bool|null $matching_start_end_time, + /** + * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. + */ + public string|null $max_duration, + /** + * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. + */ + public string|null $min_duration, + /** + * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $start_date_recurrence_rule, + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + public array $time_pairs, + /** + * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. + */ + public string|null $time_zone, + ) {} + } + + /** + * 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. + */ + class OnlineTimeFrameOptions + { + public static function from_json( + mixed $json, + ): OnlineTimeFrameOptions|null { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_date_recurrence_rule: $json->end_date_recurrence_rule ?? + null, + matching_start_end_time: $json->matching_start_end_time ?? null, + max_duration: $json->max_duration ?? null, + min_duration: $json->min_duration ?? null, + start_date_recurrence_rule: $json->start_date_recurrence_rule ?? + null, + time_pairs: array_map( + fn($t) => OnlineTimeFrameOptions\TimePairs::from_json($t), + $json->time_pairs ?? [], + ), + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( + /** + * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). + */ + public string|null $display_name, + /** + * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $end_date_recurrence_rule, + /** + * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. + */ + public bool|null $matching_start_end_time, + /** + * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. + */ + public string|null $max_duration, + /** + * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. + */ + public string|null $min_duration, + /** + * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $start_date_recurrence_rule, + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + public array $time_pairs, + /** + * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. + */ + public string|null $time_zone, + ) {} + } + + /** + * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * + * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. + */ + class ActiveThermostatSchedule + { + public static function from_json( + mixed $json, + ): ActiveThermostatSchedule|null { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => ActiveThermostatSchedule\Errors::from_json($e), + $json->errors ?? [], + ), + is_override_allowed: $json->is_override_allowed ?? null, + max_override_period_minutes: $json->max_override_period_minutes ?? + null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + thermostat_schedule_id: $json->thermostat_schedule_id ?? null, + workspace_id: $json->workspace_id ?? null, + ); + } + + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $climate_preset_key, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. + */ + public string|null $created_at, + /** + * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. + */ + public string|null $device_id, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public array $errors, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. + */ + public bool|null $is_override_allowed, + /** + * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + */ + public int|null $max_override_period_minutes, + /** + * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $name, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $thermostat_schedule_id, + /** + * ID of the workspace that contains the thermostat schedule. + */ + public string|null $workspace_id, + ) {} + } + + /** + * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. + */ + class AvailableClimatePresets + { + public static function from_json( + mixed $json, + ): AvailableClimatePresets|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + display_name: $json->display_name ?? null, + ecobee_metadata: isset($json->ecobee_metadata) + ? AvailableClimatePresets\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + */ + public string|null $climate_preset_mode, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public AvailableClimatePresets\EcobeeMetadata|null $ecobee_metadata, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + */ + public string|null $fan_mode_setting, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + */ + public string|null $hvac_mode_setting, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name, + ) {} + } + + /** + * Current climate setting. + */ + class CurrentClimateSetting + { + public static function from_json( + mixed $json, + ): CurrentClimateSetting|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + display_name: $json->display_name ?? null, + ecobee_metadata: isset($json->ecobee_metadata) + ? CurrentClimateSetting\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + */ + public string|null $climate_preset_mode, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public CurrentClimateSetting\EcobeeMetadata|null $ecobee_metadata, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + */ + public string|null $fan_mode_setting, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + */ + public string|null $hvac_mode_setting, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name, + ) {} + } + + /** + * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. + */ + class DefaultClimateSetting + { + public static function from_json( + mixed $json, + ): DefaultClimateSetting|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + display_name: $json->display_name ?? null, + ecobee_metadata: isset($json->ecobee_metadata) + ? DefaultClimateSetting\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + */ + public string|null $climate_preset_mode, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public DefaultClimateSetting\EcobeeMetadata|null $ecobee_metadata, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + */ + public string|null $fan_mode_setting, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + */ + public string|null $hvac_mode_setting, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name, + ) {} + } + + /** + * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + class TemperatureThreshold + { + public static function from_json(mixed $json): TemperatureThreshold|null + { + if (!$json) { + return null; + } + return new self( + lower_limit_celsius: $json->lower_limit_celsius ?? null, + lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, + upper_limit_celsius: $json->upper_limit_celsius ?? null, + upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, + ); + } + + public function __construct( + /** + * Lower limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $lower_limit_celsius, + /** + * Lower limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $lower_limit_fahrenheit, + /** + * Upper limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $upper_limit_celsius, + /** + * Upper limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $upper_limit_fahrenheit, + ) {} + } + + /** + * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + class ThermostatDailyPrograms + { + public static function from_json( + mixed $json, + ): ThermostatDailyPrograms|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + name: $json->name ?? null, + periods: array_map( + fn($p) => ThermostatDailyPrograms\Periods::from_json($p), + $json->periods ?? [], + ), + thermostat_daily_program_id: $json->thermostat_daily_program_id ?? + null, + workspace_id: $json->workspace_id ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the thermostat daily program was created. + */ + public string|null $created_at, + /** + * ID of the thermostat device on which the thermostat daily program is configured. + */ + public string|null $device_id, + /** + * User-friendly name to identify the thermostat daily program. + */ + public string|null $name, + /** + * Array of thermostat daily program periods. + */ + public array $periods, + /** + * ID of the thermostat daily program. + */ + public string|null $thermostat_daily_program_id, + /** + * ID of the workspace that contains the thermostat daily program. + */ + public string|null $workspace_id, + ) {} + } + + /** + * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + class ThermostatWeeklyProgram + { + public static function from_json( + mixed $json, + ): ThermostatWeeklyProgram|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + friday_program_id: $json->friday_program_id ?? null, + monday_program_id: $json->monday_program_id ?? null, + saturday_program_id: $json->saturday_program_id ?? null, + sunday_program_id: $json->sunday_program_id ?? null, + thursday_program_id: $json->thursday_program_id ?? null, + tuesday_program_id: $json->tuesday_program_id ?? null, + wednesday_program_id: $json->wednesday_program_id ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the thermostat weekly program was created. + */ + public string|null $created_at, + /** + * ID of the thermostat daily program to run on Fridays. + */ + public string|null $friday_program_id, + /** + * ID of the thermostat daily program to run on Mondays. + */ + public string|null $monday_program_id, + /** + * ID of the thermostat daily program to run on Saturdays. + */ + public string|null $saturday_program_id, + /** + * ID of the thermostat daily program to run on Sundays. + */ + public string|null $sunday_program_id, + /** + * ID of the thermostat daily program to run on Thursdays. + */ + public string|null $thursday_program_id, + /** + * ID of the thermostat daily program to run on Tuesdays. + */ + public string|null $tuesday_program_id, + /** + * ID of the thermostat daily program to run on Wednesdays. + */ + public string|null $wednesday_program_id, + ) {} + } } -/** - * Latest temperature reading for a Minut device. - */ -class DeviceTemperature -{ - public static function from_json(mixed $json): DeviceTemperature|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest temperature reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest temperature reading for a Minut device. - */ - public float|null $value, - ) {} +namespace Seam\Resources\Device\Properties\AccessoryKeypad { + /** + * Keypad battery properties. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); + } + + public function __construct(public float|null $level) {} + } } -/** - * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ -class DeviceTemperatureThreshold -{ - public static function from_json( - mixed $json, - ): DeviceTemperatureThreshold|null { - if (!$json) { - return null; - } - return new self( - lower_limit_celsius: $json->lower_limit_celsius ?? null, - lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, - upper_limit_celsius: $json->upper_limit_celsius ?? null, - upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, - ); - } - - public function __construct( - /** - * Lower limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $lower_limit_celsius, - /** - * Lower limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $lower_limit_fahrenheit, - /** - * Upper limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $upper_limit_celsius, - /** - * Upper limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $upper_limit_fahrenheit, - ) {} +namespace Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata { + /** + * Endpoints associated with the phone. + */ + class Endpoints + { + public static function from_json(mixed $json): Endpoints|null + { + if (!$json) { + return null; + } + return new self( + endpoint_id: $json->endpoint_id ?? null, + is_active: $json->is_active ?? null, + ); + } + + public function __construct( + /** + * ID of the associated endpoint. + */ + public string|null $endpoint_id, + /** + * Indicated whether the endpoint is active. + */ + public bool|null $is_active, + ) {} + } } -/** - * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. - */ -class DeviceThermostatDailyPrograms -{ - public static function from_json( - mixed $json, - ): DeviceThermostatDailyPrograms|null { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - name: $json->name ?? null, - periods: array_map( - fn($p) => DevicePeriods::from_json($p), - $json->periods ?? [], - ), - thermostat_daily_program_id: $json->thermostat_daily_program_id ?? - null, - workspace_id: $json->workspace_id ?? null, - ); - } - - public function __construct( - /** - * Date and time at which the thermostat daily program was created. - */ - public string|null $created_at, - /** - * ID of the thermostat device on which the thermostat daily program is configured. - */ - public string|null $device_id, - /** - * User-friendly name to identify the thermostat daily program. - */ - public string|null $name, - /** - * Array of thermostat daily program periods. - */ - public array $periods, - /** - * ID of the thermostat daily program. - */ - public string|null $thermostat_daily_program_id, - /** - * ID of the workspace that contains the thermostat daily program. - */ - public string|null $workspace_id, - ) {} +namespace Seam\Resources\Device\Properties\DormakabaOracodeMetadata { + /** + * Predefined time slots for a dormakaba Oracode device. + */ + class PredefinedTimeSlots + { + public static function from_json(mixed $json): PredefinedTimeSlots|null + { + if (!$json) { + return null; + } + return new self( + check_in_time: $json->check_in_time ?? null, + check_out_time: $json->check_out_time ?? null, + dormakaba_oracode_user_level_id: $json->dormakaba_oracode_user_level_id ?? + null, + dormakaba_oracode_user_level_prefix: $json->dormakaba_oracode_user_level_prefix ?? + null, + is_24_hour: $json->is_24_hour ?? null, + is_biweekly_mode: $json->is_biweekly_mode ?? null, + is_master: $json->is_master ?? null, + is_one_shot: $json->is_one_shot ?? null, + name: $json->name ?? null, + prefix: $json->prefix ?? null, + ); + } + + public function __construct( + /** + * Check in time for a time slot for a dormakaba Oracode device. + */ + public string|null $check_in_time, + /** + * Checkout time for a time slot for a dormakaba Oracode device. + */ + public string|null $check_out_time, + /** + * ID of a user level for a dormakaba Oracode device. + */ + public string|null $dormakaba_oracode_user_level_id, + /** + * Prefix for a user level for a dormakaba Oracode device. + */ + public float|null $dormakaba_oracode_user_level_prefix, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a 24-hour time slot. + */ + public bool|null $is_24_hour, + /** + * Indicates whether a time slot for a dormakaba Oracode device is in biweekly mode. + */ + public bool|null $is_biweekly_mode, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a master time slot. + */ + public bool|null $is_master, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a one-shot time slot. + */ + public bool|null $is_one_shot, + /** + * Name of a time slot for a dormakaba Oracode device. + */ + public string|null $name, + /** + * Prefix for a time slot for a dormakaba Oracode device. + */ + public float|null $prefix, + ) {} + } } -/** - * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. - */ -class DeviceThermostatWeeklyProgram -{ - public static function from_json( - mixed $json, - ): DeviceThermostatWeeklyProgram|null { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - friday_program_id: $json->friday_program_id ?? null, - monday_program_id: $json->monday_program_id ?? null, - saturday_program_id: $json->saturday_program_id ?? null, - sunday_program_id: $json->sunday_program_id ?? null, - thursday_program_id: $json->thursday_program_id ?? null, - tuesday_program_id: $json->tuesday_program_id ?? null, - wednesday_program_id: $json->wednesday_program_id ?? null, - ); - } - - public function __construct( - /** - * Date and time at which the thermostat weekly program was created. - */ - public string|null $created_at, - /** - * ID of the thermostat daily program to run on Fridays. - */ - public string|null $friday_program_id, - /** - * ID of the thermostat daily program to run on Mondays. - */ - public string|null $monday_program_id, - /** - * ID of the thermostat daily program to run on Saturdays. - */ - public string|null $saturday_program_id, - /** - * ID of the thermostat daily program to run on Sundays. - */ - public string|null $sunday_program_id, - /** - * ID of the thermostat daily program to run on Thursdays. - */ - public string|null $thursday_program_id, - /** - * ID of the thermostat daily program to run on Tuesdays. - */ - public string|null $tuesday_program_id, - /** - * ID of the thermostat daily program to run on Wednesdays. - */ - public string|null $wednesday_program_id, - ) {} +namespace Seam\Resources\Device\Properties\MinutMetadata { + /** + * Latest sensor values for a Minut device. + */ + class LatestSensorValues + { + public static function from_json(mixed $json): LatestSensorValues|null + { + if (!$json) { + return null; + } + return new self( + accelerometer_z: isset($json->accelerometer_z) + ? LatestSensorValues\AccelerometerZ::from_json( + $json->accelerometer_z, + ) + : null, + humidity: isset($json->humidity) + ? LatestSensorValues\Humidity::from_json($json->humidity) + : null, + pressure: isset($json->pressure) + ? LatestSensorValues\Pressure::from_json($json->pressure) + : null, + sound: isset($json->sound) + ? LatestSensorValues\Sound::from_json($json->sound) + : null, + temperature: isset($json->temperature) + ? LatestSensorValues\Temperature::from_json( + $json->temperature, + ) + : null, + ); + } + + public function __construct( + /** + * Latest accelerometer Z-axis reading for a Minut device. + */ + public LatestSensorValues\AccelerometerZ|null $accelerometer_z, + /** + * Latest humidity reading for a Minut device. + */ + public LatestSensorValues\Humidity|null $humidity, + /** + * Latest pressure reading for a Minut device. + */ + public LatestSensorValues\Pressure|null $pressure, + /** + * Latest sound reading for a Minut device. + */ + public LatestSensorValues\Sound|null $sound, + /** + * Latest temperature reading for a Minut device. + */ + public LatestSensorValues\Temperature|null $temperature, + ) {} + } } -/** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. - */ -class DeviceTimePairs -{ - public static function from_json(mixed $json): DeviceTimePairs|null - { - if (!$json) { - return null; - } - return new self( - display_name: $json->display_name ?? null, - end_time: $json->end_time ?? null, - start_time: $json->start_time ?? null, - ); - } - - public function __construct( - /** - * Label for the start/end time pairing. - */ - public string|null $display_name, - /** - * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. - */ - public string|null $end_time, - /** - * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. - */ - public string|null $start_time, - ) {} +namespace Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues { + /** + * Latest accelerometer Z-axis reading for a Minut device. + */ + class AccelerometerZ + { + public static function from_json(mixed $json): AccelerometerZ|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest accelerometer Z-axis reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest accelerometer Z-axis reading for a Minut device. + */ + public float|null $value, + ) {} + } + + /** + * Latest humidity reading for a Minut device. + */ + class Humidity + { + public static function from_json(mixed $json): Humidity|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest humidity reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest humidity reading for a Minut device. + */ + public float|null $value, + ) {} + } + + /** + * Latest pressure reading for a Minut device. + */ + class Pressure + { + public static function from_json(mixed $json): Pressure|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest pressure reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest pressure reading for a Minut device. + */ + public float|null $value, + ) {} + } + + /** + * Latest sound reading for a Minut device. + */ + class Sound + { + public static function from_json(mixed $json): Sound|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest sound reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest sound reading for a Minut device. + */ + public float|null $value, + ) {} + } + + /** + * Latest temperature reading for a Minut device. + */ + class Temperature + { + public static function from_json(mixed $json): Temperature|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest temperature reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest temperature reading for a Minut device. + */ + public float|null $value, + ) {} + } } -/** - * Metadata for a TTLock device. - */ -class DeviceTtlockMetadata -{ - public static function from_json(mixed $json): DeviceTtlockMetadata|null - { - if (!$json) { - return null; - } - return new self( - feature_value: $json->feature_value ?? null, - features: isset($json->features) - ? DeviceFeatures::from_json($json->features) - : null, - has_gateway: $json->has_gateway ?? null, - lock_alias: $json->lock_alias ?? null, - lock_id: $json->lock_id ?? null, - timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, - wireless_keypads: array_map( - fn($w) => DeviceWirelessKeypads::from_json($w), - $json->wireless_keypads ?? [], - ), - ); - } - - public function __construct( - /** - * Feature value for a TTLock device. - */ - public string|null $feature_value, - /** - * Features for a TTLock device. - */ - public DeviceFeatures|null $features, - /** - * Indicates whether a TTLock device has a gateway. - */ - public bool|null $has_gateway, - /** - * Lock alias for a TTLock device. - */ - public string|null $lock_alias, - /** - * Lock ID for a TTLock device. - */ - public float|null $lock_id, - /** - * Lock-side timezone offset in milliseconds east of UTC, as configured in the TTLock app. Source of truth for the lock's wall-clock interpretation of access code start/end times — a misconfigured value here is the typical cause of customer "codes offset by N hours" reports. Diagnostic only; Seam does not convert times based on this value. - */ - public float|null $timezone_raw_offset_ms, - /** - * Wireless keypads for a TTLock device. - */ - public array $wireless_keypads, - ) {} +namespace Seam\Resources\Device\Properties\TtlockMetadata { + /** + * Features for a TTLock device. + */ + class Features + { + public static function from_json(mixed $json): Features|null + { + if (!$json) { + return null; + } + return new self( + auto_lock_time_config: $json->auto_lock_time_config ?? null, + incomplete_keyboard_passcode: $json->incomplete_keyboard_passcode ?? + null, + lock_command: $json->lock_command ?? null, + passcode: $json->passcode ?? null, + passcode_management: $json->passcode_management ?? null, + unlock_via_gateway: $json->unlock_via_gateway ?? null, + wifi: $json->wifi ?? null, + ); + } + + public function __construct( + /** + * Indicates whether a TTLock device supports auto-lock time configuration. + */ + public bool|null $auto_lock_time_config, + /** + * Indicates whether a TTLock device supports an incomplete keyboard passcode. + */ + public bool|null $incomplete_keyboard_passcode, + /** + * Indicates whether a TTLock device supports the lock command. + */ + public bool|null $lock_command, + /** + * Indicates whether a TTLock device supports a passcode. + */ + public bool|null $passcode, + /** + * Indicates whether a TTLock device supports passcode management. + */ + public bool|null $passcode_management, + /** + * Indicates whether a TTLock device supports unlock via gateway. + */ + public bool|null $unlock_via_gateway, + /** + * Indicates whether a TTLock device supports Wi-Fi. + */ + public bool|null $wifi, + ) {} + } + + /** + * Wireless keypads for a TTLock device. + */ + class WirelessKeypads + { + public static function from_json(mixed $json): WirelessKeypads|null + { + if (!$json) { + return null; + } + return new self( + wireless_keypad_id: $json->wireless_keypad_id ?? null, + wireless_keypad_name: $json->wireless_keypad_name ?? null, + ); + } + + public function __construct( + /** + * ID for a wireless keypad for a TTLock device. + */ + public float|null $wireless_keypad_id, + /** + * Name for a wireless keypad for a TTLock device. + */ + public string|null $wireless_keypad_name, + ) {} + } } -/** - * Metadata for a 2N device. - */ -class DeviceTwoNMetadata -{ - public static function from_json(mixed $json): DeviceTwoNMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - ); - } - - public function __construct( - /** - * Device ID for a 2N device. - */ - public float|null $device_id, - /** - * Device name for a 2N device. - */ - public string|null $device_name, - ) {} +namespace Seam\Resources\Device\Properties\OfflineTimeFrameOptions { + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + class TimePairs + { + public static function from_json(mixed $json): TimePairs|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_time: $json->end_time ?? null, + start_time: $json->start_time ?? null, + ); + } + + public function __construct( + /** + * Label for the start/end time pairing. + */ + public string|null $display_name, + /** + * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. + */ + public string|null $end_time, + /** + * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. + */ + public string|null $start_time, + ) {} + } } -/** - * Metadata for an Ultraloq device. - */ -class DeviceUltraloqMetadata -{ - public static function from_json(mixed $json): DeviceUltraloqMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - device_type: $json->device_type ?? null, - time_zone: $json->time_zone ?? null, - ); - } - - public function __construct( - /** - * Device ID for an Ultraloq device. - */ - public string|null $device_id, - /** - * Device name for an Ultraloq device. - */ - public string|null $device_name, - /** - * Device type for an Ultraloq device. - */ - public string|null $device_type, - /** - * IANA timezone for the Ultraloq device. - */ - public string|null $time_zone, - ) {} +namespace Seam\Resources\Device\Properties\OnlineTimeFrameOptions { + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + class TimePairs + { + public static function from_json(mixed $json): TimePairs|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_time: $json->end_time ?? null, + start_time: $json->start_time ?? null, + ); + } + + public function __construct( + /** + * Label for the start/end time pairing. + */ + public string|null $display_name, + /** + * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. + */ + public string|null $end_time, + /** + * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. + */ + public string|null $start_time, + ) {} + } } -/** - * Metadata for an ASSA ABLOY Visionline system. - */ -class DeviceVisionlineMetadata -{ - public static function from_json(mixed $json): DeviceVisionlineMetadata|null +namespace Seam\Resources\Device\Properties\ActiveThermostatSchedule { + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self(encoder_id: $json->encoder_id ?? null); + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} } +} + +namespace Seam\Resources\Device\Properties\AvailableClimatePresets { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } - public function __construct( - /** - * Encoder ID for an ASSA ABLOY Visionline system. - */ - public string|null $encoder_id, - ) {} + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized, + /** + * Indicates whether the climate preset is owned by the user or the system. + */ + public string|null $owner, + ) {} + } } -/** - * 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 DeviceWarnings -{ - public static function from_json(mixed $json): DeviceWarnings|null - { - if (!$json) { - return null; - } - return new self( - active_access_code_count: $json->active_access_code_count ?? null, - created_at: $json->created_at ?? null, - max_active_access_code_count: $json->max_active_access_code_count ?? - null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - - public function __construct( - /** - * Number of active access codes on the device when the warning was set. - */ - public int|null $active_access_code_count, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Maximum number of active access codes supported by the device. - */ - public int|null $max_active_access_code_count, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} +namespace Seam\Resources\Device\Properties\CurrentClimateSetting { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } + + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized, + /** + * Indicates whether the climate preset is owned by the user or the system. + */ + public string|null $owner, + ) {} + } } -/** - * Wireless keypads for a TTLock device. - */ -class DeviceWirelessKeypads -{ - public static function from_json(mixed $json): DeviceWirelessKeypads|null - { - if (!$json) { - return null; - } - return new self( - wireless_keypad_id: $json->wireless_keypad_id ?? null, - wireless_keypad_name: $json->wireless_keypad_name ?? null, - ); - } - - public function __construct( - /** - * ID for a wireless keypad for a TTLock device. - */ - public float|null $wireless_keypad_id, - /** - * Name for a wireless keypad for a TTLock device. - */ - public string|null $wireless_keypad_name, - ) {} +namespace Seam\Resources\Device\Properties\DefaultClimateSetting { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } + + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized, + /** + * Indicates whether the climate preset is owned by the user or the system. + */ + public string|null $owner, + ) {} + } } -/** - * Metadata for a Wyze device. - */ -class DeviceWyzeMetadata -{ - public static function from_json(mixed $json): DeviceWyzeMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_info_model: $json->device_info_model ?? null, - device_name: $json->device_name ?? null, - keypad_uuid: $json->keypad_uuid ?? null, - locker_status_hardlock: $json->locker_status_hardlock ?? null, - product_model: $json->product_model ?? null, - product_name: $json->product_name ?? null, - product_type: $json->product_type ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Wyze device. - */ - public string|null $device_id, - /** - * Device information model for a Wyze device. - */ - public string|null $device_info_model, - /** - * Device name for a Wyze device. - */ - public string|null $device_name, - /** - * Keypad UUID for a Wyze device. - */ - public string|null $keypad_uuid, - /** - * Locker status (hardlock) for a Wyze device. - */ - public float|null $locker_status_hardlock, - /** - * Product model for a Wyze device. - */ - public string|null $product_model, - /** - * Product name for a Wyze device. - */ - public string|null $product_name, - /** - * Product type for a Wyze device. - */ - public string|null $product_type, - ) {} +namespace Seam\Resources\Device\Properties\ThermostatDailyPrograms { + /** + * Array of thermostat daily program periods. + */ + class Periods + { + public static function from_json(mixed $json): Periods|null + { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + starts_at_time: $json->starts_at_time ?? null, + ); + } + + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. + */ + public string|null $climate_preset_key, + /** + * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at_time, + ) {} + } } diff --git a/src/Resources/DeviceProvider.php b/src/Resources/DeviceProvider.php index 2f1539f6..a3276967 100644 --- a/src/Resources/DeviceProvider.php +++ b/src/Resources/DeviceProvider.php @@ -1,148 +1,148 @@ can_configure_auto_lock ?? null, + can_hvac_cool: $json->can_hvac_cool ?? null, + can_hvac_heat: $json->can_hvac_heat ?? null, + can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, + can_program_offline_access_codes: $json->can_program_offline_access_codes ?? + null, + can_program_online_access_codes: $json->can_program_online_access_codes ?? + null, + can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? + null, + can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? + null, + can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? + null, + can_remotely_lock: $json->can_remotely_lock ?? null, + can_remotely_unlock: $json->can_remotely_unlock ?? null, + can_run_thermostat_programs: $json->can_run_thermostat_programs ?? + null, + can_simulate_connection: $json->can_simulate_connection ?? null, + can_simulate_disconnection: $json->can_simulate_disconnection ?? + null, + can_simulate_hub_connection: $json->can_simulate_hub_connection ?? + null, + can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? + null, + can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? + null, + can_simulate_removal: $json->can_simulate_removal ?? null, + can_turn_off_hvac: $json->can_turn_off_hvac ?? null, + can_unlock_with_code: $json->can_unlock_with_code ?? null, + device_provider_name: $json->device_provider_name ?? null, + display_name: $json->display_name ?? null, + image_url: $json->image_url ?? null, + provider_categories: $json->provider_categories ?? null, + ); } - return new self( - can_configure_auto_lock: $json->can_configure_auto_lock ?? null, - can_hvac_cool: $json->can_hvac_cool ?? null, - can_hvac_heat: $json->can_hvac_heat ?? null, - can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, - can_program_offline_access_codes: $json->can_program_offline_access_codes ?? - null, - can_program_online_access_codes: $json->can_program_online_access_codes ?? - null, - can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? - null, - can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? - null, - can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? - null, - can_remotely_lock: $json->can_remotely_lock ?? null, - can_remotely_unlock: $json->can_remotely_unlock ?? null, - can_run_thermostat_programs: $json->can_run_thermostat_programs ?? - null, - can_simulate_connection: $json->can_simulate_connection ?? null, - can_simulate_disconnection: $json->can_simulate_disconnection ?? - null, - can_simulate_hub_connection: $json->can_simulate_hub_connection ?? - null, - can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? - null, - can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? - null, - can_simulate_removal: $json->can_simulate_removal ?? null, - can_turn_off_hvac: $json->can_turn_off_hvac ?? null, - can_unlock_with_code: $json->can_unlock_with_code ?? null, - device_provider_name: $json->device_provider_name ?? null, - display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, - provider_categories: $json->provider_categories ?? null, - ); - } - public function __construct( - /** - * Indicates whether the lock supports configuring automatic locking. - */ - public bool|null $can_configure_auto_lock, - /** - * Indicates whether the thermostat supports cooling. - */ - public bool|null $can_hvac_cool, - /** - * Indicates whether the thermostat supports heating. - */ - public bool|null $can_hvac_heat, - /** - * Indicates whether the thermostat supports simultaneous heating and cooling. - */ - public bool|null $can_hvac_heat_cool, - /** - * Indicates whether the device supports programming offline access codes. - */ - public bool|null $can_program_offline_access_codes, - /** - * Indicates whether the device supports programming online access codes. - */ - public bool|null $can_program_online_access_codes, - /** - * Indicates whether the thermostat supports different climate programs for each day of the week. - */ - public bool|null $can_program_thermostat_programs_as_different_each_day, - /** - * Indicates whether the thermostat supports a single climate program applied to every day. - */ - public bool|null $can_program_thermostat_programs_as_same_each_day, - /** - * Indicates whether the thermostat supports weekday/weekend climate programs. - */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, - /** - * Indicates whether the device supports remote locking. - */ - public bool|null $can_remotely_lock, - /** - * Indicates whether the device supports remote unlocking. - */ - public bool|null $can_remotely_unlock, - /** - * Indicates whether the thermostat supports running climate programs. - */ - public bool|null $can_run_thermostat_programs, - /** - * Indicates whether the device supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_connection, - /** - * Indicates whether the device supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_disconnection, - /** - * Indicates whether the hub supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_hub_connection, - /** - * Indicates whether the hub supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_hub_disconnection, - /** - * Indicates whether the device supports simulating a paid subscription in a sandbox. - */ - public bool|null $can_simulate_paid_subscription, - /** - * Indicates whether the device supports simulating removal in a sandbox. - */ - public bool|null $can_simulate_removal, - /** - * Indicates whether the thermostat can be turned off. - */ - public bool|null $can_turn_off_hvac, - /** - * Indicates whether the lock supports unlocking with an access code. - */ - public bool|null $can_unlock_with_code, - /** - * Name of the device provider. - */ - public string|null $device_provider_name, - /** - * Display name for the device provider. - */ - public string|null $display_name, - /** - * Image URL for the device provider. - */ - public string|null $image_url, - /** - * List of provider categories to which the device provider belongs, such as `stable`, `consumer_smartlocks`, `thermostats`, and so on. - */ - public array|null $provider_categories, - ) {} + public function __construct( + /** + * Indicates whether the lock supports configuring automatic locking. + */ + public bool|null $can_configure_auto_lock, + /** + * Indicates whether the thermostat supports cooling. + */ + public bool|null $can_hvac_cool, + /** + * Indicates whether the thermostat supports heating. + */ + public bool|null $can_hvac_heat, + /** + * Indicates whether the thermostat supports simultaneous heating and cooling. + */ + public bool|null $can_hvac_heat_cool, + /** + * Indicates whether the device supports programming offline access codes. + */ + public bool|null $can_program_offline_access_codes, + /** + * Indicates whether the device supports programming online access codes. + */ + public bool|null $can_program_online_access_codes, + /** + * Indicates whether the thermostat supports different climate programs for each day of the week. + */ + public bool|null $can_program_thermostat_programs_as_different_each_day, + /** + * Indicates whether the thermostat supports a single climate program applied to every day. + */ + public bool|null $can_program_thermostat_programs_as_same_each_day, + /** + * Indicates whether the thermostat supports weekday/weekend climate programs. + */ + public bool|null $can_program_thermostat_programs_as_weekday_weekend, + /** + * Indicates whether the device supports remote locking. + */ + public bool|null $can_remotely_lock, + /** + * Indicates whether the device supports remote unlocking. + */ + public bool|null $can_remotely_unlock, + /** + * Indicates whether the thermostat supports running climate programs. + */ + public bool|null $can_run_thermostat_programs, + /** + * Indicates whether the device supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_connection, + /** + * Indicates whether the device supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_disconnection, + /** + * Indicates whether the hub supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_hub_connection, + /** + * Indicates whether the hub supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_hub_disconnection, + /** + * Indicates whether the device supports simulating a paid subscription in a sandbox. + */ + public bool|null $can_simulate_paid_subscription, + /** + * Indicates whether the device supports simulating removal in a sandbox. + */ + public bool|null $can_simulate_removal, + /** + * Indicates whether the thermostat can be turned off. + */ + public bool|null $can_turn_off_hvac, + /** + * Indicates whether the lock supports unlocking with an access code. + */ + public bool|null $can_unlock_with_code, + /** + * Name of the device provider. + */ + public string|null $device_provider_name, + /** + * Display name for the device provider. + */ + public string|null $display_name, + /** + * Image URL for the device provider. + */ + public string|null $image_url, + /** + * List of provider categories to which the device provider belongs, such as `stable`, `consumer_smartlocks`, `thermostats`, and so on. + */ + public array|null $provider_categories, + ) {} + } } diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 9277ed2f..4ce80506 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -1,946 +1,878 @@ Event\AccessCodeErrors::from_json($a), + $json->access_code_errors ?? [], + ), + access_code_id: $json->access_code_id ?? null, + access_code_is_managed: $json->access_code_is_managed ?? null, + access_code_warnings: array_map( + fn($a) => Event\AccessCodeWarnings::from_json($a), + $json->access_code_warnings ?? [], + ), + access_grant_id: $json->access_grant_id ?? null, + access_grant_ids: $json->access_grant_ids ?? null, + access_grant_key: $json->access_grant_key ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + access_method_id: $json->access_method_id ?? null, + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_encoder_id: $json->acs_encoder_id ?? null, + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_entrance_ids: $json->acs_entrance_ids ?? null, + acs_system_errors: array_map( + fn($a) => Event\AcsSystemErrors::from_json($a), + $json->acs_system_errors ?? [], + ), + acs_system_id: $json->acs_system_id ?? null, + acs_system_warnings: array_map( + fn($a) => Event\AcsSystemWarnings::from_json($a), + $json->acs_system_warnings ?? [], + ), + acs_user_id: $json->acs_user_id ?? null, + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + activation_reason: $json->activation_reason ?? null, + backup_access_code_id: $json->backup_access_code_id ?? null, + battery_level: $json->battery_level ?? null, + battery_status: $json->battery_status ?? null, + change_reason: $json->change_reason ?? null, + changed_properties: array_map( + fn($c) => Event\ChangedProperties::from_json($c), + $json->changed_properties ?? [], + ), + client_session_id: $json->client_session_id ?? null, + climate_preset_key: $json->climate_preset_key ?? null, + code: $json->code ?? null, + connect_webview_id: $json->connect_webview_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + connected_account_errors: array_map( + fn($c) => Event\ConnectedAccountErrors::from_json($c), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_type: $json->connected_account_type ?? null, + connected_account_warnings: array_map( + fn($c) => Event\ConnectedAccountWarnings::from_json($c), + $json->connected_account_warnings ?? [], + ), + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + created_at: $json->created_at ?? null, + customer_key: $json->customer_key ?? null, + description: $json->description ?? null, + desired_temperature_celsius: $json->desired_temperature_celsius ?? + null, + desired_temperature_fahrenheit: $json->desired_temperature_fahrenheit ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + device_errors: array_map( + fn($d) => Event\DeviceErrors::from_json($d), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_ids: $json->device_ids ?? null, + device_name: $json->device_name ?? null, + device_warnings: array_map( + fn($d) => Event\DeviceWarnings::from_json($d), + $json->device_warnings ?? [], + ), + ends_at: $json->ends_at ?? null, + error_code: $json->error_code ?? null, + error_message: $json->error_message ?? null, + event_description: $json->event_description ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + fan_mode_setting: $json->fan_mode_setting ?? null, + from: isset($json->from) + ? Event\From::from_json($json->from) + : null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + image_url: $json->image_url ?? null, + is_backup_code: $json->is_backup_code ?? null, + is_fallback_climate_preset: $json->is_fallback_climate_preset ?? + null, + is_via_bluetooth: $json->is_via_bluetooth ?? null, + is_via_nfc: $json->is_via_nfc ?? null, + lower_limit_celsius: $json->lower_limit_celsius ?? null, + lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, + method: $json->method ?? null, + minut_metadata: $json->minut_metadata ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + motion_sub_type: $json->motion_sub_type ?? null, + noise_level_decibels: $json->noise_level_decibels ?? null, + noise_level_nrs: $json->noise_level_nrs ?? null, + noise_threshold_id: $json->noise_threshold_id ?? null, + noise_threshold_name: $json->noise_threshold_name ?? null, + noiseaware_metadata: $json->noiseaware_metadata ?? null, + occurred_at: $json->occurred_at ?? null, + reason: isset($json->reason) + ? Event\Reason::from_json($json->reason) + : null, + requested_mutations: array_map( + fn($r) => Event\RequestedMutations::from_json($r), + $json->requested_mutations ?? [], + ), + space_id: $json->space_id ?? null, + space_key: $json->space_key ?? null, + starts_at: $json->starts_at ?? null, + status: $json->status ?? null, + temperature_celsius: $json->temperature_celsius ?? null, + temperature_fahrenheit: $json->temperature_fahrenheit ?? null, + thermostat_schedule_id: $json->thermostat_schedule_id ?? null, + to: isset($json->to) ? Event\To::from_json($json->to) : null, + upper_limit_celsius: $json->upper_limit_celsius ?? null, + upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, + user_identity_id: $json->user_identity_id ?? null, + video_url: $json->video_url ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_code_errors: array_map( - fn($a) => EventAccessCodeErrors::from_json($a), - $json->access_code_errors ?? [], - ), - access_code_id: $json->access_code_id ?? null, - access_code_is_managed: $json->access_code_is_managed ?? null, - access_code_warnings: array_map( - fn($a) => EventAccessCodeWarnings::from_json($a), - $json->access_code_warnings ?? [], - ), - access_grant_id: $json->access_grant_id ?? null, - access_grant_ids: $json->access_grant_ids ?? null, - access_grant_key: $json->access_grant_key ?? null, - access_grant_keys: $json->access_grant_keys ?? null, - access_method_id: $json->access_method_id ?? null, - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_encoder_id: $json->acs_encoder_id ?? null, - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_entrance_ids: $json->acs_entrance_ids ?? null, - acs_system_errors: array_map( - fn($a) => EventAcsSystemErrors::from_json($a), - $json->acs_system_errors ?? [], - ), - acs_system_id: $json->acs_system_id ?? null, - acs_system_warnings: array_map( - fn($a) => EventAcsSystemWarnings::from_json($a), - $json->acs_system_warnings ?? [], - ), - acs_user_id: $json->acs_user_id ?? null, - action_attempt_id: $json->action_attempt_id ?? null, - action_type: $json->action_type ?? null, - activation_reason: $json->activation_reason ?? null, - backup_access_code_id: $json->backup_access_code_id ?? null, - battery_level: $json->battery_level ?? null, - battery_status: $json->battery_status ?? null, - change_reason: $json->change_reason ?? null, - changed_properties: array_map( - fn($c) => EventChangedProperties::from_json($c), - $json->changed_properties ?? [], - ), - client_session_id: $json->client_session_id ?? null, - climate_preset_key: $json->climate_preset_key ?? null, - code: $json->code ?? null, - connect_webview_id: $json->connect_webview_id ?? null, - connected_account_custom_metadata: $json->connected_account_custom_metadata ?? - null, - connected_account_errors: array_map( - fn($c) => EventConnectedAccountErrors::from_json($c), - $json->connected_account_errors ?? [], - ), - connected_account_id: $json->connected_account_id ?? null, - connected_account_type: $json->connected_account_type ?? null, - connected_account_warnings: array_map( - fn($c) => EventConnectedAccountWarnings::from_json($c), - $json->connected_account_warnings ?? [], - ), - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - created_at: $json->created_at ?? null, - customer_key: $json->customer_key ?? null, - description: $json->description ?? null, - desired_temperature_celsius: $json->desired_temperature_celsius ?? - null, - desired_temperature_fahrenheit: $json->desired_temperature_fahrenheit ?? - null, - device_custom_metadata: $json->device_custom_metadata ?? null, - device_errors: array_map( - fn($d) => EventDeviceErrors::from_json($d), - $json->device_errors ?? [], - ), - device_id: $json->device_id ?? null, - device_ids: $json->device_ids ?? null, - device_name: $json->device_name ?? null, - device_warnings: array_map( - fn($d) => EventDeviceWarnings::from_json($d), - $json->device_warnings ?? [], - ), - ends_at: $json->ends_at ?? null, - error_code: $json->error_code ?? null, - error_message: $json->error_message ?? null, - event_description: $json->event_description ?? null, - event_id: $json->event_id ?? null, - event_type: $json->event_type ?? null, - fan_mode_setting: $json->fan_mode_setting ?? null, - from: isset($json->from) ? EventFrom::from_json($json->from) : null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - image_url: $json->image_url ?? null, - is_backup_code: $json->is_backup_code ?? null, - is_fallback_climate_preset: $json->is_fallback_climate_preset ?? - null, - is_via_bluetooth: $json->is_via_bluetooth ?? null, - is_via_nfc: $json->is_via_nfc ?? null, - lower_limit_celsius: $json->lower_limit_celsius ?? null, - lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, - method: $json->method ?? null, - minut_metadata: $json->minut_metadata ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - motion_sub_type: $json->motion_sub_type ?? null, - noise_level_decibels: $json->noise_level_decibels ?? null, - noise_level_nrs: $json->noise_level_nrs ?? null, - noise_threshold_id: $json->noise_threshold_id ?? null, - noise_threshold_name: $json->noise_threshold_name ?? null, - noiseaware_metadata: $json->noiseaware_metadata ?? null, - occurred_at: $json->occurred_at ?? null, - reason: isset($json->reason) - ? EventReason::from_json($json->reason) - : null, - requested_mutations: array_map( - fn($r) => EventRequestedMutations::from_json($r), - $json->requested_mutations ?? [], - ), - space_id: $json->space_id ?? null, - space_key: $json->space_key ?? null, - starts_at: $json->starts_at ?? null, - status: $json->status ?? null, - temperature_celsius: $json->temperature_celsius ?? null, - temperature_fahrenheit: $json->temperature_fahrenheit ?? null, - thermostat_schedule_id: $json->thermostat_schedule_id ?? null, - to: isset($json->to) ? EventTo::from_json($json->to) : null, - upper_limit_celsius: $json->upper_limit_celsius ?? null, - upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, - user_identity_id: $json->user_identity_id ?? null, - video_url: $json->video_url ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Errors associated with the access code. - */ - public array $access_code_errors, - /** - * ID of the affected access code. - */ - public string|null $access_code_id, - /** - * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. - */ - public bool|null $access_code_is_managed, - /** - * Warnings associated with the access code. - */ - public array $access_code_warnings, - /** - * ID of the affected Access Grant. - */ - public string|null $access_grant_id, - /** - * IDs of the access grants associated with this access method. - */ - public array|null $access_grant_ids, - /** - * Key of the affected Access Grant (if present). - */ - public string|null $access_grant_key, - /** - * Keys of the access grants associated with this access method (if present). - */ - public array|null $access_grant_keys, - /** - * ID of the affected access method. - */ - public string|null $access_method_id, - /** - * ID of the affected access group. - */ - public string|null $acs_access_group_id, - /** - * ID of the affected credential. - */ - public string|null $acs_credential_id, - /** - * ID of the affected encoder. - */ - public string|null $acs_encoder_id, - /** - * ID of the affected [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $acs_entrance_id, - /** - * IDs of all ACS entrances currently attached to the space. - */ - public array|null $acs_entrance_ids, - /** - * Errors associated with the access control system. - */ - public array $acs_system_errors, - /** - * ID of the access system. - */ - public string|null $acs_system_id, - /** - * Warnings associated with the access control system. - */ - public array $acs_system_warnings, - /** - * ID of the affected access system user. - */ - public string|null $acs_user_id, - /** - * ID of the affected action attempt. - */ - public string|null $action_attempt_id, - /** - * Type of the action. - */ - public string|null $action_type, - /** - * The reason the camera was activated. - */ - public string|null $activation_reason, - /** - * ID of the backup access code that was pulled from the pool. - */ - public string|null $backup_access_code_id, - /** - * Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. - */ - public float|null $battery_level, - /** - * Battery status of the affected device, calculated from the numeric `battery_level` value. - */ - public string|null $battery_status, - /** - * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). - */ - public string|null $change_reason, - /** - * List of properties that changed on the access code. - */ - public array $changed_properties, - /** - * ID of the affected client session. - */ - public string|null $client_session_id, - /** - * Key of the climate preset that was activated. - */ - public string|null $climate_preset_key, - /** - * Code for the affected access code. - */ - public string|null $code, - /** - * ID of the Connect Webview associated with the event. - */ - public string|null $connect_webview_id, - /** - * Custom metadata of the connected account, present when connected_account_id is provided. - */ - public mixed $connected_account_custom_metadata, - /** - * Errors associated with the connected account. - */ - public array $connected_account_errors, - /** - * ID of the connected account associated with the affected access code. - */ - public string|null $connected_account_id, - /** - * undocumented: Unreleased. - */ - public string|null $connected_account_type, - /** - * Warnings associated with the connected account. - */ - public array $connected_account_warnings, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit, - /** - * Date and time at which the event was created. - */ - public string|null $created_at, - /** - * The customer key associated with this connected account, if any. - */ - public string|null $customer_key, - /** - * Human-readable description of the change and its source. - */ - public string|null $description, - /** - * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. - */ - public float|null $desired_temperature_celsius, - /** - * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. - */ - public float|null $desired_temperature_fahrenheit, - /** - * Custom metadata of the device, present when device_id is provided. - */ - public mixed $device_custom_metadata, - /** - * Errors associated with the device. - */ - public array $device_errors, - /** - * ID of the device associated with the affected access code. - */ - public string|null $device_id, - /** - * IDs of all devices currently attached to the space. - */ - public array|null $device_ids, - /** - * 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. - */ - public string|null $device_name, - /** - * Warnings associated with the device. - */ - public array $device_warnings, - /** - * The new end time for the access grant. - */ - public string|null $ends_at, - /** - * Error code associated with the disconnection event, if any. - */ - public string|null $error_code, - /** - * Description of why the access methods could not be created. - */ - public string|null $error_message, - /** - * 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. - */ - public string|null $event_description, - /** - * ID of the event. - */ - public string|null $event_id, - public string|null $event_type, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting, - /** - * Previous access code name configuration. - */ - public EventFrom|null $from, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_fahrenheit, - /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. - */ - public string|null $hvac_mode_setting, - /** - * URL to a thumbnail image captured at the time of activation. - */ - public string|null $image_url, - /** - * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). - */ - public bool|null $is_backup_code, - /** - * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. - */ - public bool|null $is_fallback_climate_preset, - /** - * 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. - */ - public bool|null $is_via_bluetooth, - /** - * 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. - */ - public bool|null $is_via_nfc, - /** - * Lower temperature limit, in °C, defined by the set threshold. - */ - public float|null $lower_limit_celsius, - /** - * Lower temperature limit, in °F, defined by the set threshold. - */ - public float|null $lower_limit_fahrenheit, - /** - * 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. - */ - public string|null $method, - /** - * Metadata from Minut. - */ - public mixed $minut_metadata, - /** - * 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. - */ - public array|null $missing_device_ids, - /** - * Sub-type of motion detected, if available. - */ - public string|null $motion_sub_type, - /** - * Detected noise level in decibels. - */ - public float|null $noise_level_decibels, - /** - * Detected noise level in Noiseaware Noise Risk Score (NRS). - */ - public float|null $noise_level_nrs, - /** - * ID of the noise threshold that was triggered. - */ - public string|null $noise_threshold_id, - /** - * Name of the noise threshold that was triggered. - */ - public string|null $noise_threshold_name, - /** - * Metadata from Noiseaware. - */ - public mixed $noiseaware_metadata, - /** - * Date and time at which the event occurred. - */ - public string|null $occurred_at, - /** - * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. - */ - public EventReason|null $reason, - /** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. - */ - public array $requested_mutations, - /** - * ID of the affected space. - */ - public string|null $space_id, - /** - * Unique key for the space within the workspace. - */ - public string|null $space_key, - /** - * The new start time for the access grant. - */ - public string|null $starts_at, - /** - * Status of the action. - */ - public string|null $status, - /** - * Temperature, in °C, reported by the affected thermostat. - */ - public float|null $temperature_celsius, - /** - * Temperature, in °F, reported by the affected thermostat. - */ - public float|null $temperature_fahrenheit, - /** - * ID of the thermostat schedule that prompted the affected climate preset to be activated. - */ - public string|null $thermostat_schedule_id, - /** - * New access code name configuration. - */ - public EventTo|null $to, - /** - * Upper temperature limit, in °C, defined by the set threshold. - */ - public float|null $upper_limit_celsius, - /** - * Upper temperature limit, in °F, defined by the set threshold. - */ - public float|null $upper_limit_fahrenheit, - /** - * undocumented: Unreleased. - * --- - * ID of the user identity associated with the lock event. - */ - public string|null $user_identity_id, - /** - * URL to a short video clip captured at the time of activation. - */ - public string|null $video_url, - /** - * ID of the workspace associated with the event. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Errors associated with the access code. + */ + public array $access_code_errors, + public string|null $access_code_id, + /** + * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + */ + public bool|null $access_code_is_managed, + /** + * Warnings associated with the access code. + */ + public array $access_code_warnings, + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * IDs of the access grants associated with this access method. + */ + public array|null $access_grant_ids, + /** + * Key of the affected Access Grant (if present). + */ + public string|null $access_grant_key, + /** + * Keys of the access grants associated with this access method (if present). + */ + public array|null $access_grant_keys, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * ID of the affected access group. + */ + public string|null $acs_access_group_id, + /** + * ID of the affected credential. + */ + public string|null $acs_credential_id, + /** + * ID of the affected encoder. + */ + public string|null $acs_encoder_id, + public string|null $acs_entrance_id, + public array|null $acs_entrance_ids, + /** + * Errors associated with the access control system. + */ + public array $acs_system_errors, + public string|null $acs_system_id, + /** + * Warnings associated with the access control system. + */ + public array $acs_system_warnings, + public string|null $acs_user_id, + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * The reason the camera was activated. + */ + public string|null $activation_reason, + /** + * ID of the backup access code that was pulled from the pool. + */ + public string|null $backup_access_code_id, + /** + * Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. + */ + public float|null $battery_level, + /** + * Battery status of the affected device, calculated from the numeric `battery_level` value. + */ + public string|null $battery_status, + /** + * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). + */ + public string|null $change_reason, + /** + * List of properties that changed on the access code. + */ + public array $changed_properties, + /** + * ID of the affected client session. + */ + public string|null $client_session_id, + /** + * Key of the climate preset that was activated. + */ + public string|null $climate_preset_key, + public string|null $code, + public string|null $connect_webview_id, + public mixed $connected_account_custom_metadata, + /** + * Errors associated with the connected account. + */ + public array $connected_account_errors, + public string|null $connected_account_id, + /** + * undocumented: Unreleased. + */ + public string|null $connected_account_type, + /** + * Warnings associated with the connected account. + */ + public array $connected_account_warnings, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit, + /** + * Date and time at which the event was created. + */ + public string|null $created_at, + public string|null $customer_key, + /** + * Human-readable description of the change and its source. + */ + public string|null $description, + /** + * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. + */ + public float|null $desired_temperature_celsius, + /** + * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. + */ + public float|null $desired_temperature_fahrenheit, + public mixed $device_custom_metadata, + /** + * Errors associated with the device. + */ + public array $device_errors, + public string|null $device_id, + public array|null $device_ids, + public string|null $device_name, + /** + * Warnings associated with the device. + */ + public array $device_warnings, + /** + * The new end time for the access grant. + */ + public string|null $ends_at, + /** + * Error code associated with the disconnection event, if any. + */ + public string|null $error_code, + /** + * Description of why the access methods could not be created. + */ + public string|null $error_message, + /** + * 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. + */ + public string|null $event_description, + /** + * ID of the event. + */ + public string|null $event_id, + /** + * Type of the event. + */ + public string|null $event_type, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + */ + public string|null $fan_mode_setting, + public Event\From|null $from, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + */ + public string|null $hvac_mode_setting, + public string|null $image_url, + /** + * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). + */ + public bool|null $is_backup_code, + /** + * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. + */ + public bool|null $is_fallback_climate_preset, + public bool|null $is_via_bluetooth, + public bool|null $is_via_nfc, + /** + * Lower temperature limit, in °C, defined by the set threshold. + */ + public float|null $lower_limit_celsius, + /** + * Lower temperature limit, in °F, defined by the set threshold. + */ + public float|null $lower_limit_fahrenheit, + public string|null $method, + /** + * Metadata from Minut. + */ + public mixed $minut_metadata, + /** + * 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. + */ + public array|null $missing_device_ids, + /** + * Sub-type of motion detected, if available. + */ + public string|null $motion_sub_type, + /** + * Detected noise level in decibels. + */ + public float|null $noise_level_decibels, + /** + * Detected noise level in Noiseaware Noise Risk Score (NRS). + */ + public float|null $noise_level_nrs, + /** + * ID of the noise threshold that was triggered. + */ + public string|null $noise_threshold_id, + /** + * Name of the noise threshold that was triggered. + */ + public string|null $noise_threshold_name, + /** + * Metadata from Noiseaware. + */ + public mixed $noiseaware_metadata, + /** + * Date and time at which the event occurred. + */ + public string|null $occurred_at, + /** + * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + */ + public Event\Reason|null $reason, + /** + * Array of mutations requested on the access code, each containing the mutation type and from/to values. + */ + public array $requested_mutations, + /** + * ID of the affected space. + */ + public string|null $space_id, + /** + * Unique key for the space within the workspace. + */ + public string|null $space_key, + /** + * The new start time for the access grant. + */ + public string|null $starts_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * Temperature, in °C, reported by the affected thermostat. + */ + public float|null $temperature_celsius, + /** + * Temperature, in °F, reported by the affected thermostat. + */ + public float|null $temperature_fahrenheit, + /** + * ID of the thermostat schedule that prompted the affected climate preset to be activated. + */ + public string|null $thermostat_schedule_id, + public Event\To|null $to, + /** + * Upper temperature limit, in °C, defined by the set threshold. + */ + public float|null $upper_limit_celsius, + /** + * Upper temperature limit, in °F, defined by the set threshold. + */ + public float|null $upper_limit_fahrenheit, + public string|null $user_identity_id, + public string|null $video_url, + /** + * ID of the workspace associated with the event. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the access code. - */ -class EventAccessCodeErrors -{ - public static function from_json(mixed $json): EventAccessCodeErrors|null +namespace Seam\Resources\Event { + /** + * List of properties that changed on the access code. + */ + class ChangedProperties { - if (!$json) { - return null; + public static function from_json(mixed $json): ChangedProperties|null + { + if (!$json) { + return null; + } + return new self( + from: $json->from ?? null, + property: $json->property ?? null, + to: $json->to ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Previous value of the property, or null if not set. + */ + public string|null $from, + /** + * Name of the property that changed (e.g. `code`). + */ + public string|null $property, + /** + * New value of the property, or null if cleared. + */ + public string|null $to, + ) {} + } -/** - * Warnings associated with the access code. - */ -class EventAccessCodeWarnings -{ - public static function from_json(mixed $json): EventAccessCodeWarnings|null + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} -} + public function __construct( + /** + * Previous pin code. + */ + public string|null $code, + /** + * Previous end time. + */ + public string|null $ends_at, + /** + * Previous name of the access code. + */ + public string|null $name, + /** + * Previous start time. + */ + public string|null $starts_at, + ) {} + } -/** - * Errors associated with the access control system. - */ -class EventAcsSystemErrors -{ - public static function from_json(mixed $json): EventAcsSystemErrors|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * New pin code. + */ + public string|null $code, + /** + * New end time. + */ + public string|null $ends_at, + /** + * New name of the access code. + */ + public string|null $name, + /** + * New start time. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the access control system. - */ -class EventAcsSystemWarnings -{ - public static function from_json(mixed $json): EventAcsSystemWarnings|null + /** + * Array of mutations requested on the access code, each containing the mutation type and from/to values. + */ + class RequestedMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): RequestedMutations|null + { + if (!$json) { + return null; + } + return new self( + from: $json->from ?? null, + mutation_code: $json->mutation_code ?? null, + to: $json->to ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} -} + public function __construct( + /** + * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + */ + public mixed $from, + /** + * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. + */ + public string|null $mutation_code, + /** + * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + */ + public mixed $to, + ) {} + } -/** - * List of properties that changed on the access code. - */ -class EventChangedProperties -{ - public static function from_json(mixed $json): EventChangedProperties|null + /** + * Errors associated with the access code. + */ + class AccessCodeErrors { - if (!$json) { - return null; + public static function from_json(mixed $json): AccessCodeErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - from: $json->from ?? null, - property: $json->property ?? null, - to: $json->to ?? null, - ); - } - public function __construct( - /** - * Previous value of the property, or null if not set. - */ - public string|null $from, - /** - * Name of the property that changed (e.g. `code`). - */ - public string|null $property, - /** - * New value of the property, or null if cleared. - */ - public string|null $to, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Errors associated with the connected account. - */ -class EventConnectedAccountErrors -{ - public static function from_json( - mixed $json, - ): EventConnectedAccountErrors|null { - if (!$json) { - return null; + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings + { + public static function from_json(mixed $json): AccessCodeWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } -/** - * Warnings associated with the connected account. - */ -class EventConnectedAccountWarnings -{ - public static function from_json( - mixed $json, - ): EventConnectedAccountWarnings|null { - if (!$json) { - return null; + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Errors associated with the device. - */ -class EventDeviceErrors -{ - public static function from_json(mixed $json): EventDeviceErrors|null + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } -/** - * Warnings associated with the device. - */ -class EventDeviceWarnings -{ - public static function from_json(mixed $json): EventDeviceWarnings|null + /** + * Errors associated with the device. + */ + class DeviceErrors { - if (!$json) { - return null; + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Previous access code name configuration. - */ -class EventFrom -{ - public static function from_json(mixed $json): EventFrom|null + /** + * Warnings associated with the device. + */ + class DeviceWarnings { - if (!$json) { - return null; + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous pin code. - */ - public string|null $code, - /** - * Previous end time. - */ - public string|null $ends_at, - /** - * Previous name of the access code. - */ - public string|null $name, - /** - * Previous start time. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } -/** - * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. - */ -class EventReason -{ - public static function from_json(mixed $json): EventReason|null + /** + * Errors associated with the access control system. + */ + class AcsSystemErrors { - if (!$json) { - return null; + public static function from_json(mixed $json): AcsSystemErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - message: $json->message ?? null, - reason_code: $json->reason_code ?? null, - ); - } - public function __construct( - /** - * Human-readable explanation of why access was denied. - */ - public string|null $message, - /** - * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. - */ - public string|null $reason_code, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. - */ -class EventRequestedMutations -{ - public static function from_json(mixed $json): EventRequestedMutations|null + /** + * Warnings associated with the access control system. + */ + class AcsSystemWarnings { - if (!$json) { - return null; + public static function from_json(mixed $json): AcsSystemWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - from: $json->from ?? null, - mutation_code: $json->mutation_code ?? null, - to: $json->to ?? null, - ); - } - public function __construct( - /** - * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. - */ - public mixed $from, - /** - * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. - */ - public string|null $mutation_code, - /** - * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. - */ - public mixed $to, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } -/** - * New access code name configuration. - */ -class EventTo -{ - public static function from_json(mixed $json): EventTo|null + /** + * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + */ + class Reason { - if (!$json) { - return null; + public static function from_json(mixed $json): Reason|null + { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + reason_code: $json->reason_code ?? null, + ); } - return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New pin code. - */ - public string|null $code, - /** - * New end time. - */ - public string|null $ends_at, - /** - * New name of the access code. - */ - public string|null $name, - /** - * New start time. - */ - public string|null $starts_at, - ) {} + public function __construct( + /** + * Human-readable explanation of why access was denied. + */ + public string|null $message, + /** + * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. + */ + public string|null $reason_code, + ) {} + } } diff --git a/src/Resources/InstantKey.php b/src/Resources/InstantKey.php index b14e3cd5..cbeb2807 100644 --- a/src/Resources/InstantKey.php +++ b/src/Resources/InstantKey.php @@ -1,103 +1,106 @@ client_session_id ?? null, + created_at: $json->created_at ?? null, + customization: isset($json->customization) + ? InstantKey\Customization::from_json($json->customization) + : null, + customization_profile_id: $json->customization_profile_id ?? + null, + expires_at: $json->expires_at ?? null, + instant_key_id: $json->instant_key_id ?? null, + instant_key_url: $json->instant_key_url ?? null, + user_identity_id: $json->user_identity_id ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - client_session_id: $json->client_session_id ?? null, - created_at: $json->created_at ?? null, - customization: isset($json->customization) - ? InstantKeyCustomization::from_json($json->customization) - : null, - customization_profile_id: $json->customization_profile_id ?? null, - expires_at: $json->expires_at ?? null, - instant_key_id: $json->instant_key_id ?? null, - instant_key_url: $json->instant_key_url ?? null, - user_identity_id: $json->user_identity_id ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the client session associated with the Instant Key. - */ - public string|null $client_session_id, - /** - * Date and time at which the Instant Key was created. - */ - public string|null $created_at, - /** - * Customization applied to the Instant Key UI. - */ - public InstantKeyCustomization|null $customization, - /** - * ID of the customization profile associated with the Instant Key. - */ - public string|null $customization_profile_id, - /** - * Date and time at which the Instant Key expires. - */ - public string|null $expires_at, - /** - * ID of the Instant Key. - */ - public string|null $instant_key_id, - /** - * Shareable URL for the Instant Key. Use the URL to deliver the Instant Key to your user through a link in a text message or email or by embedding it in your web app. - */ - public string|null $instant_key_url, - /** - * ID of the user identity associated with the Instant Key. - */ - public string|null $user_identity_id, - /** - * ID of the workspace that contains the Instant Key. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the client session associated with the Instant Key. + */ + public string|null $client_session_id, + /** + * Date and time at which the Instant Key was created. + */ + public string|null $created_at, + /** + * Customization applied to the Instant Key UI. + */ + public InstantKey\Customization|null $customization, + /** + * ID of the customization profile associated with the Instant Key. + */ + public string|null $customization_profile_id, + /** + * Date and time at which the Instant Key expires. + */ + public string|null $expires_at, + /** + * ID of the Instant Key. + */ + public string|null $instant_key_id, + /** + * Shareable URL for the Instant Key. Use the URL to deliver the Instant Key to your user through a link in a text message or email or by embedding it in your web app. + */ + public string|null $instant_key_url, + /** + * ID of the user identity associated with the Instant Key. + */ + public string|null $user_identity_id, + /** + * ID of the workspace that contains the Instant Key. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Customization applied to the Instant Key UI. - */ -class InstantKeyCustomization -{ - public static function from_json(mixed $json): InstantKeyCustomization|null +namespace Seam\Resources\InstantKey { + /** + * Customization applied to the Instant Key UI. + */ + class Customization { - if (!$json) { - return null; + public static function from_json(mixed $json): Customization|null + { + if (!$json) { + return null; + } + return new self( + logo_url: $json->logo_url ?? null, + primary_color: $json->primary_color ?? null, + secondary_color: $json->secondary_color ?? null, + ); } - return new self( - logo_url: $json->logo_url ?? null, - primary_color: $json->primary_color ?? null, - secondary_color: $json->secondary_color ?? null, - ); - } - public function __construct( - /** - * URL of the logo displayed on the Instant Key. - */ - public string|null $logo_url, - /** - * Primary color used in the Instant Key UI. - */ - public string|null $primary_color, - /** - * Secondary color used in the Instant Key UI. - */ - public string|null $secondary_color, - ) {} + public function __construct( + /** + * URL of the logo displayed on the Instant Key. + */ + public string|null $logo_url, + /** + * Primary color used in the Instant Key UI. + */ + public string|null $primary_color, + /** + * Secondary color used in the Instant Key UI. + */ + public string|null $secondary_color, + ) {} + } } diff --git a/src/Resources/NoiseThreshold.php b/src/Resources/NoiseThreshold.php index 31a7b30d..765329d2 100644 --- a/src/Resources/NoiseThreshold.php +++ b/src/Resources/NoiseThreshold.php @@ -1,56 +1,57 @@ device_id ?? null, + ends_daily_at: $json->ends_daily_at ?? null, + name: $json->name ?? null, + noise_threshold_decibels: $json->noise_threshold_decibels ?? + null, + noise_threshold_id: $json->noise_threshold_id ?? null, + noise_threshold_nrs: $json->noise_threshold_nrs ?? null, + starts_daily_at: $json->starts_daily_at ?? null, + ); } - return new self( - device_id: $json->device_id ?? null, - ends_daily_at: $json->ends_daily_at ?? null, - name: $json->name ?? null, - noise_threshold_decibels: $json->noise_threshold_decibels ?? null, - noise_threshold_id: $json->noise_threshold_id ?? null, - noise_threshold_nrs: $json->noise_threshold_nrs ?? null, - starts_daily_at: $json->starts_daily_at ?? null, - ); - } - public function __construct( - /** - * Unique identifier for the device that contains the noise threshold. - */ - public string|null $device_id, - /** - * Time at which the noise threshold should become inactive daily. - */ - public string|null $ends_daily_at, - /** - * Name of the noise threshold. - */ - public string|null $name, - /** - * Noise level in decibels for the noise threshold. - */ - public float|null $noise_threshold_decibels, - /** - * Unique identifier for the noise threshold. - */ - public string|null $noise_threshold_id, - /** - * Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for [Noiseaware sensors](https://docs.seam.co/device-and-system-integration-guides/noiseaware-sensors). - */ - public float|null $noise_threshold_nrs, - /** - * Time at which the noise threshold should become active daily. - */ - public string|null $starts_daily_at, - ) {} + public function __construct( + /** + * Unique identifier for the device that contains the noise threshold. + */ + public string|null $device_id, + /** + * Time at which the noise threshold should become inactive daily. + */ + public string|null $ends_daily_at, + /** + * Name of the noise threshold. + */ + public string|null $name, + /** + * Noise level in decibels for the noise threshold. + */ + public float|null $noise_threshold_decibels, + /** + * Unique identifier for the noise threshold. + */ + public string|null $noise_threshold_id, + /** + * Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for [Noiseaware sensors](https://docs.seam.co/device-and-system-integration-guides/noiseaware-sensors). + */ + public float|null $noise_threshold_nrs, + /** + * Time at which the noise threshold should become active daily. + */ + public string|null $starts_daily_at, + ) {} + } } diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index 866153d1..28f7d0d8 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -1,267 +1,277 @@ created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + device_id: $json->device_id ?? null, + device_type: $json->device_type ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => Phone\Errors::from_json($e), + $json->errors ?? [], + ), + nickname: $json->nickname ?? null, + properties: isset($json->properties) + ? Phone\Properties::from_json($json->properties) + : null, + warnings: array_map( + fn($w) => Phone\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - device_id: $json->device_id ?? null, - device_type: $json->device_type ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => PhoneErrors::from_json($e), - $json->errors ?? [], - ), - nickname: $json->nickname ?? null, - properties: isset($json->properties) - ? PhoneProperties::from_json($json->properties) - : null, - warnings: array_map( - fn($w) => PhoneWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - - public function __construct( - /** - * Date and time at which the phone was created. - */ - public string|null $created_at, - /** - * Optional [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) for the phone. - */ - public mixed $custom_metadata, - /** - * ID of the phone. - */ - public string|null $device_id, - /** - * Type of the phone device, such as `ios_phone` or `android_phone`. - */ - public string|null $device_type, - /** - * Display name of the phone. Defaults to `nickname` (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the phone easily, especially when there are numerous phones. - */ - public string|null $display_name, - /** - * Errors associated with the phone. - */ - public array $errors, - /** - * Optional nickname to describe the phone, settable through Seam. - */ - public string|null $nickname, - /** - * Properties of the phone. - */ - public PhoneProperties|null $properties, - /** - * Warnings associated with the phone. - */ - public array $warnings, - /** - * ID of the workspace that contains the phone. - */ - public string|null $workspace_id, - ) {} -} -/** - * ASSA ABLOY Credential Service metadata for the phone. - */ -class PhoneAssaAbloyCredentialServiceMetadata -{ - public static function from_json( - mixed $json, - ): PhoneAssaAbloyCredentialServiceMetadata|null { - if (!$json) { - return null; - } - return new self( - endpoints: array_map( - fn($e) => PhoneEndpoints::from_json($e), - $json->endpoints ?? [], - ), - has_active_endpoint: $json->has_active_endpoint ?? null, - ); + public function __construct( + /** + * Date and time at which the phone was created. + */ + public string|null $created_at, + /** + * Optional [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) for the phone. + */ + public mixed $custom_metadata, + /** + * ID of the phone. + */ + public string|null $device_id, + /** + * Type of the phone device, such as `ios_phone` or `android_phone`. + */ + public string|null $device_type, + /** + * Display name of the phone. Defaults to `nickname` (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the phone easily, especially when there are numerous phones. + */ + public string|null $display_name, + /** + * Errors associated with the phone. + */ + public array $errors, + /** + * Optional nickname to describe the phone, settable through Seam. + */ + public string|null $nickname, + /** + * Properties of the phone. + */ + public Phone\Properties|null $properties, + /** + * Warnings associated with the phone. + */ + public array $warnings, + /** + * ID of the workspace that contains the phone. + */ + public string|null $workspace_id, + ) {} } - - public function __construct( - /** - * Endpoints associated with the phone. - */ - public array $endpoints, - /** - * Indicates whether the credential service has active endpoints associated with the phone. - */ - public bool|null $has_active_endpoint, - ) {} } -/** - * Endpoints associated with the phone. - */ -class PhoneEndpoints -{ - public static function from_json(mixed $json): PhoneEndpoints|null +namespace Seam\Resources\Phone { + /** + * Errors associated with the phone. + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - endpoint_id: $json->endpoint_id ?? null, - is_active: $json->is_active ?? null, - ); - } - public function __construct( - /** - * ID of the associated endpoint. - */ - public string|null $endpoint_id, - /** - * Indicated whether the endpoint is active. - */ - public bool|null $is_active, - ) {} -} - -/** - * Errors associated with the phone. - */ -class PhoneErrors -{ - public static function from_json(mixed $json): PhoneErrors|null - { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. + */ + public string|null $error_code, + /** + * Detailed description of the error. + */ + public string|null $message, + ) {} } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. - */ - public string|null $error_code, - /** - * Detailed description of the error. - */ - public string|null $message, - ) {} -} - -/** - * Properties of the phone. - */ -class PhoneProperties -{ - public static function from_json(mixed $json): PhoneProperties|null + /** + * Properties of the phone. + */ + class Properties { - if (!$json) { - return null; - } - return new self( - assa_abloy_credential_service_metadata: isset( - $json->assa_abloy_credential_service_metadata, - ) - ? PhoneAssaAbloyCredentialServiceMetadata::from_json( + public static function from_json(mixed $json): Properties|null + { + if (!$json) { + return null; + } + return new self( + assa_abloy_credential_service_metadata: isset( $json->assa_abloy_credential_service_metadata, ) - : null, - salto_space_credential_service_metadata: isset( - $json->salto_space_credential_service_metadata, - ) - ? PhoneSaltoSpaceCredentialServiceMetadata::from_json( + ? Properties\AssaAbloyCredentialServiceMetadata::from_json( + $json->assa_abloy_credential_service_metadata, + ) + : null, + salto_space_credential_service_metadata: isset( $json->salto_space_credential_service_metadata, ) - : null, - ); + ? Properties\SaltoSpaceCredentialServiceMetadata::from_json( + $json->salto_space_credential_service_metadata, + ) + : null, + ); + } + + public function __construct( + /** + * ASSA ABLOY Credential Service metadata for the phone. + */ + public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, + /** + * Salto Space credential service metadata for the phone. + */ + public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, + ) {} } - public function __construct( - /** - * ASSA ABLOY Credential Service metadata for the phone. - */ - public PhoneAssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, - /** - * Salto Space credential service metadata for the phone. - */ - public PhoneSaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, - ) {} + /** + * Warnings associated with the phone. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. + */ + public string|null $warning_code, + ) {} + } } -/** - * Salto Space credential service metadata for the phone. - */ -class PhoneSaltoSpaceCredentialServiceMetadata -{ - public static function from_json( - mixed $json, - ): PhoneSaltoSpaceCredentialServiceMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\Phone\Properties { + /** + * ASSA ABLOY Credential Service metadata for the phone. + */ + class AssaAbloyCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self( + endpoints: array_map( + fn( + $e, + ) => AssaAbloyCredentialServiceMetadata\Endpoints::from_json( + $e, + ), + $json->endpoints ?? [], + ), + has_active_endpoint: $json->has_active_endpoint ?? null, + ); } - return new self(has_active_phone: $json->has_active_phone ?? null); + + public function __construct( + /** + * Endpoints associated with the phone. + */ + public array $endpoints, + /** + * Indicates whether the credential service has active endpoints associated with the phone. + */ + public bool|null $has_active_endpoint, + ) {} } - public function __construct( - /** - * Indicates whether the credential service has an active associated phone. - */ - public bool|null $has_active_phone, - ) {} + /** + * Salto Space credential service metadata for the phone. + */ + class SaltoSpaceCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): SaltoSpaceCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self(has_active_phone: $json->has_active_phone ?? null); + } + + public function __construct( + /** + * Indicates whether the credential service has an active associated phone. + */ + public bool|null $has_active_phone, + ) {} + } } -/** - * Warnings associated with the phone. - */ -class PhoneWarnings -{ - public static function from_json(mixed $json): PhoneWarnings|null +namespace Seam\Resources\Phone\Properties\AssaAbloyCredentialServiceMetadata { + /** + * Endpoints associated with the phone. + */ + class Endpoints { - if (!$json) { - return null; + public static function from_json(mixed $json): Endpoints|null + { + if (!$json) { + return null; + } + return new self( + endpoint_id: $json->endpoint_id ?? null, + is_active: $json->is_active ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * ID of the associated endpoint. + */ + public string|null $endpoint_id, + /** + * Indicated whether the endpoint is active. + */ + public bool|null $is_active, + ) {} + } } diff --git a/src/Resources/Space.php b/src/Resources/Space.php index 433326fa..eb7c6777 100644 --- a/src/Resources/Space.php +++ b/src/Resources/Space.php @@ -1,146 +1,148 @@ acs_entrance_count ?? null, + created_at: $json->created_at ?? null, + customer_data: isset($json->customer_data) + ? Space\CustomerData::from_json($json->customer_data) + : null, + customer_key: $json->customer_key ?? null, + device_count: $json->device_count ?? null, + display_name: $json->display_name ?? null, + geolocation: isset($json->geolocation) + ? Space\Geolocation::from_json($json->geolocation) + : null, + name: $json->name ?? null, + space_id: $json->space_id ?? null, + space_key: $json->space_key ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_entrance_count: $json->acs_entrance_count ?? null, - created_at: $json->created_at ?? null, - customer_data: isset($json->customer_data) - ? SpaceCustomerData::from_json($json->customer_data) - : null, - customer_key: $json->customer_key ?? null, - device_count: $json->device_count ?? null, - display_name: $json->display_name ?? null, - geolocation: isset($json->geolocation) - ? SpaceGeolocation::from_json($json->geolocation) - : null, - name: $json->name ?? null, - space_id: $json->space_id ?? null, - space_key: $json->space_key ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Number of entrances in the space. - */ - public float|null $acs_entrance_count, - /** - * Date and time at which the space was created. - */ - public string|null $created_at, - /** - * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). - */ - public SpaceCustomerData|null $customer_data, - /** - * Customer key associated with the space. - */ - public string|null $customer_key, - /** - * Number of devices in the space. - */ - public float|null $device_count, - /** - * Display name for the space. - */ - public string|null $display_name, - /** - * Geographic coordinates (latitude and longitude) of the space. - */ - public SpaceGeolocation|null $geolocation, - /** - * Name of the space. - */ - public string|null $name, - /** - * ID of the space. - */ - public string|null $space_id, - /** - * Unique key for the space within the workspace. - */ - public string|null $space_key, - /** - * ID of the workspace associated with the space. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Number of entrances in the space. + */ + public float|null $acs_entrance_count, + /** + * Date and time at which the space was created. + */ + public string|null $created_at, + /** + * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). + */ + public Space\CustomerData|null $customer_data, + /** + * Customer key associated with the space. + */ + public string|null $customer_key, + /** + * Number of devices in the space. + */ + public float|null $device_count, + /** + * Display name for the space. + */ + public string|null $display_name, + /** + * Geographic coordinates (latitude and longitude) of the space. + */ + public Space\Geolocation|null $geolocation, + /** + * Name of the space. + */ + public string|null $name, + /** + * ID of the space. + */ + public string|null $space_id, + /** + * Unique key for the space within the workspace. + */ + public string|null $space_key, + /** + * ID of the workspace associated with the space. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). - */ -class SpaceCustomerData -{ - public static function from_json(mixed $json): SpaceCustomerData|null +namespace Seam\Resources\Space { + /** + * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). + */ + class CustomerData { - if (!$json) { - return null; + public static function from_json(mixed $json): CustomerData|null + { + if (!$json) { + return null; + } + return new self( + address: $json->address ?? null, + default_checkin_time: $json->default_checkin_time ?? null, + default_checkout_time: $json->default_checkout_time ?? null, + time_zone: $json->time_zone ?? null, + ); } - return new self( - address: $json->address ?? null, - default_checkin_time: $json->default_checkin_time ?? null, - default_checkout_time: $json->default_checkout_time ?? null, - time_zone: $json->time_zone ?? null, - ); - } - public function __construct( - /** - * Postal address for the space. - */ - public string|null $address, - /** - * Default check-in time for reservations at the space, as HH:mm or HH:mm:ss. - */ - public string|null $default_checkin_time, - /** - * Default check-out time for reservations at the space, as HH:mm or HH:mm:ss. - */ - public string|null $default_checkout_time, - /** - * IANA time zone for the space, e.g. America/Los_Angeles. - */ - public string|null $time_zone, - ) {} -} + public function __construct( + /** + * Postal address for the space. + */ + public string|null $address, + /** + * Default check-in time for reservations at the space, as HH:mm or HH:mm:ss. + */ + public string|null $default_checkin_time, + /** + * Default check-out time for reservations at the space, as HH:mm or HH:mm:ss. + */ + public string|null $default_checkout_time, + /** + * IANA time zone for the space, e.g. America/Los_Angeles. + */ + public string|null $time_zone, + ) {} + } -/** - * Geographic coordinates (latitude and longitude) of the space. - */ -class SpaceGeolocation -{ - public static function from_json(mixed $json): SpaceGeolocation|null + /** + * Geographic coordinates (latitude and longitude) of the space. + */ + class Geolocation { - if (!$json) { - return null; + public static function from_json(mixed $json): Geolocation|null + { + if (!$json) { + return null; + } + return new self( + latitude: $json->latitude ?? null, + longitude: $json->longitude ?? null, + ); } - return new self( - latitude: $json->latitude ?? null, - longitude: $json->longitude ?? null, - ); - } - public function __construct( - /** - * Latitude of the space, in decimal degrees. - */ - public float|null $latitude, - /** - * Longitude of the space, in decimal degrees. - */ - public float|null $longitude, - ) {} + public function __construct( + /** + * Latitude of the space, in decimal degrees. + */ + public float|null $latitude, + /** + * Longitude of the space, in decimal degrees. + */ + public float|null $longitude, + ) {} + } } diff --git a/src/Resources/ThermostatDailyProgram.php b/src/Resources/ThermostatDailyProgram.php index 1232a323..1733a4dc 100644 --- a/src/Resources/ThermostatDailyProgram.php +++ b/src/Resources/ThermostatDailyProgram.php @@ -1,84 +1,86 @@ created_at ?? null, + device_id: $json->device_id ?? null, + name: $json->name ?? null, + periods: array_map( + fn($p) => ThermostatDailyProgram\Periods::from_json($p), + $json->periods ?? [], + ), + thermostat_daily_program_id: $json->thermostat_daily_program_id ?? + null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - name: $json->name ?? null, - periods: array_map( - fn($p) => ThermostatDailyProgramPeriods::from_json($p), - $json->periods ?? [], - ), - thermostat_daily_program_id: $json->thermostat_daily_program_id ?? - null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Date and time at which the thermostat daily program was created. - */ - public string|null $created_at, - /** - * ID of the thermostat device on which the thermostat daily program is configured. - */ - public string|null $device_id, - /** - * User-friendly name to identify the thermostat daily program. - */ - public string|null $name, - /** - * Array of thermostat daily program periods. - */ - public array $periods, - /** - * ID of the thermostat daily program. - */ - public string|null $thermostat_daily_program_id, - /** - * ID of the workspace that contains the thermostat daily program. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Date and time at which the thermostat daily program was created. + */ + public string|null $created_at, + /** + * ID of the thermostat device on which the thermostat daily program is configured. + */ + public string|null $device_id, + /** + * User-friendly name to identify the thermostat daily program. + */ + public string|null $name, + /** + * Array of thermostat daily program periods. + */ + public array $periods, + /** + * ID of the thermostat daily program. + */ + public string|null $thermostat_daily_program_id, + /** + * ID of the workspace that contains the thermostat daily program. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Array of thermostat daily program periods. - */ -class ThermostatDailyProgramPeriods -{ - public static function from_json( - mixed $json, - ): ThermostatDailyProgramPeriods|null { - if (!$json) { - return null; +namespace Seam\Resources\ThermostatDailyProgram { + /** + * Array of thermostat daily program periods. + */ + class Periods + { + public static function from_json(mixed $json): Periods|null + { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + starts_at_time: $json->starts_at_time ?? null, + ); } - return new self( - climate_preset_key: $json->climate_preset_key ?? null, - starts_at_time: $json->starts_at_time ?? null, - ); - } - public function __construct( - /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. - */ - public string|null $climate_preset_key, - /** - * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at_time, - ) {} + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. + */ + public string|null $climate_preset_key, + /** + * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at_time, + ) {} + } } diff --git a/src/Resources/ThermostatSchedule.php b/src/Resources/ThermostatSchedule.php index 6c44947c..9d719a87 100644 --- a/src/Resources/ThermostatSchedule.php +++ b/src/Resources/ThermostatSchedule.php @@ -1,113 +1,115 @@ climate_preset_key ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => ThermostatSchedule\Errors::from_json($e), + $json->errors ?? [], + ), + is_override_allowed: $json->is_override_allowed ?? null, + max_override_period_minutes: $json->max_override_period_minutes ?? + null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + thermostat_schedule_id: $json->thermostat_schedule_id ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - climate_preset_key: $json->climate_preset_key ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => ThermostatScheduleErrors::from_json($e), - $json->errors ?? [], - ), - is_override_allowed: $json->is_override_allowed ?? null, - max_override_period_minutes: $json->max_override_period_minutes ?? - null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - thermostat_schedule_id: $json->thermostat_schedule_id ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $climate_preset_key, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. - */ - public string|null $created_at, - /** - * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. - */ - public string|null $device_id, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public array $errors, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. - */ - public bool|null $is_override_allowed, - /** - * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - */ - public int|null $max_override_period_minutes, - /** - * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $name, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $thermostat_schedule_id, - /** - * ID of the workspace that contains the thermostat schedule. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $climate_preset_key, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. + */ + public string|null $created_at, + /** + * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. + */ + public string|null $device_id, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public array $errors, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. + */ + public bool|null $is_override_allowed, + /** + * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + */ + public int|null $max_override_period_minutes, + /** + * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $name, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $thermostat_schedule_id, + /** + * ID of the workspace that contains the thermostat schedule. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ -class ThermostatScheduleErrors -{ - public static function from_json(mixed $json): ThermostatScheduleErrors|null +namespace Seam\Resources\ThermostatSchedule { + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } } diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 4804b688..5855b3ac 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -1,340 +1,373 @@ access_code_id ?? null, - cannot_be_managed: $json->cannot_be_managed ?? null, - cannot_delete_unmanaged_access_code: $json->cannot_delete_unmanaged_access_code ?? - null, - code: $json->code ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - dormakaba_oracode_metadata: isset($json->dormakaba_oracode_metadata) - ? UnmanagedAccessCodeDormakabaOracodeMetadata::from_json( + public static function from_json(mixed $json): UnmanagedAccessCode|null + { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + cannot_be_managed: $json->cannot_be_managed ?? null, + cannot_delete_unmanaged_access_code: $json->cannot_delete_unmanaged_access_code ?? + null, + code: $json->code ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + dormakaba_oracode_metadata: isset( $json->dormakaba_oracode_metadata, ) - : null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => UnmanagedAccessCodeErrors::from_json($e), - $json->errors ?? [], - ), - is_managed: $json->is_managed ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - status: $json->status ?? null, - type: $json->type ?? null, - warnings: array_map( - fn($w) => UnmanagedAccessCodeWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + ? UnmanagedAccessCode\DormakabaOracodeMetadata::from_json( + $json->dormakaba_oracode_metadata, + ) + : null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => UnmanagedAccessCode\Errors::from_json($e), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + status: $json->status ?? null, + type: $json->type ?? null, + warnings: array_map( + fn($w) => UnmanagedAccessCode\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Unique identifier for the access code. - */ - public string|null $access_code_id, - /** - * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. - */ - public bool|null $cannot_be_managed, - /** - * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. - */ - public bool|null $cannot_delete_unmanaged_access_code, - /** - * Code used for access. Typically, a numeric or alphanumeric string. - */ - public string|null $code, - /** - * Date and time at which the access code was created. - */ - public string|null $created_at, - /** - * Unique identifier for the device associated with the access code. - */ - public string|null $device_id, - /** - * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. - */ - public UnmanagedAccessCodeDormakabaOracodeMetadata|null $dormakaba_oracode_metadata, - /** - * Date and time after which the time-bound access code becomes inactive. - */ - public string|null $ends_at, - /** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ - public array $errors, - /** - * Indicates that Seam does not manage the access code. - */ - public bool|null $is_managed, - /** - * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). - */ - public string|null $name, - /** - * Date and time at which the time-bound access code becomes active. - */ - public string|null $starts_at, - /** - * Current status of the access code within the operational lifecycle. `set` indicates that the code is active and operational. `unset` indicates that the code exists on the provider but is not usable on the device. - */ - public string|null $status, - /** - * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. - */ - public string|null $type, - /** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the access code. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Unique identifier for the access code. + */ + public string|null $access_code_id, + /** + * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. + */ + public bool|null $cannot_be_managed, + /** + * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. + */ + public bool|null $cannot_delete_unmanaged_access_code, + /** + * Code used for access. Typically, a numeric or alphanumeric string. + */ + public string|null $code, + /** + * Date and time at which the access code was created. + */ + public string|null $created_at, + /** + * Unique identifier for the device associated with the access code. + */ + public string|null $device_id, + /** + * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. + */ + public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + /** + * Date and time after which the time-bound access code becomes inactive. + */ + public string|null $ends_at, + /** + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + public array $errors, + /** + * Indicates that Seam does not manage the access code. + */ + public bool|null $is_managed, + /** + * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). + */ + public string|null $name, + /** + * Date and time at which the time-bound access code becomes active. + */ + public string|null $starts_at, + /** + * Current status of the access code within the operational lifecycle. `set` indicates that the code is active and operational. `unset` indicates that the code exists on the provider but is not usable on the device. + */ + public string|null $status, + /** + * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. + */ + public string|null $type, + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the access code. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. - */ -class UnmanagedAccessCodeDormakabaOracodeMetadata -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessCodeDormakabaOracodeMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessCode { + /** + * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. + */ + class DormakabaOracodeMetadata + { + public static function from_json( + mixed $json, + ): DormakabaOracodeMetadata|null { + if (!$json) { + return null; + } + return new self( + is_cancellable: $json->is_cancellable ?? null, + is_early_checkin_able: $json->is_early_checkin_able ?? null, + is_extendable: $json->is_extendable ?? null, + is_overridable: $json->is_overridable ?? null, + site_name: $json->site_name ?? null, + stay_id: $json->stay_id ?? null, + user_level_id: $json->user_level_id ?? null, + user_level_name: $json->user_level_name ?? null, + ); } - return new self( - is_cancellable: $json->is_cancellable ?? null, - is_early_checkin_able: $json->is_early_checkin_able ?? null, - is_extendable: $json->is_extendable ?? null, - is_overridable: $json->is_overridable ?? null, - site_name: $json->site_name ?? null, - stay_id: $json->stay_id ?? null, - user_level_id: $json->user_level_id ?? null, - user_level_name: $json->user_level_name ?? null, - ); - } - public function __construct( - /** - * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. - */ - public bool|null $is_cancellable, - /** - * Indicates whether early check-in is available for this stay. - */ - public bool|null $is_early_checkin_able, - /** - * Indicates whether the stay can be extended via the Dormakaba Oracode API. - */ - public bool|null $is_extendable, - /** - * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. - */ - public bool|null $is_overridable, - /** - * Dormakaba Oracode site name associated with this access code. - */ - public string|null $site_name, - /** - * Dormakaba Oracode stay ID associated with this access code. - */ - public float|null $stay_id, - /** - * Dormakaba Oracode user level ID associated with this access code. - */ - public string|null $user_level_id, - /** - * Dormakaba Oracode user level name associated with this access code. - */ - public string|null $user_level_name, - ) {} -} + public function __construct( + /** + * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. + */ + public bool|null $is_cancellable, + /** + * Indicates whether early check-in is available for this stay. + */ + public bool|null $is_early_checkin_able, + /** + * Indicates whether the stay can be extended via the Dormakaba Oracode API. + */ + public bool|null $is_extendable, + /** + * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. + */ + public bool|null $is_overridable, + /** + * Dormakaba Oracode site name associated with this access code. + */ + public string|null $site_name, + /** + * Dormakaba Oracode stay ID associated with this access code. + */ + public float|null $stay_id, + /** + * Dormakaba Oracode user level ID associated with this access code. + */ + public string|null $user_level_id, + /** + * Dormakaba Oracode user level name associated with this access code. + */ + public string|null $user_level_name, + ) {} + } -/** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ -class UnmanagedAccessCodeErrors -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessCodeErrors|null { - if (!$json) { - return null; + /** + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + managed_access_code_id: $json->managed_access_code_id ?? null, + message: $json->message ?? null, + modified_fields: array_map( + fn($m) => Errors\ModifiedFields::from_json($m), + $json->modified_fields ?? [], + ), + unmanaged_access_code_id: $json->unmanaged_access_code_id ?? + null, + ); } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_access_code_error: $json->is_access_code_error ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - managed_access_code_id: $json->managed_access_code_id ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => UnmanagedAccessCodeModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - unmanaged_access_code_id: $json->unmanaged_access_code_id ?? null, - ); + + public function __construct( + /** + * 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. + */ + public string|null $change_type, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public bool|null $is_access_code_error, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $managed_access_code_id, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * List of fields that were changed externally, with their previous and new values. + */ + public array $modified_fields, + /** + * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $unmanaged_access_code_id, + ) {} } - public function __construct( - /** - * 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. - */ - public string|null $change_type, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates that this is an access code error. - */ - public bool|null $is_access_code_error, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. - */ - public bool|null $is_connected_account_error, - /** - * Indicates that the error is not a device error. - */ - public bool|null $is_device_error, - /** - * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $managed_access_code_id, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array $modified_fields, - /** - * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $unmanaged_access_code_id, - ) {} + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + modified_fields: array_map( + fn($m) => Warnings\ModifiedFields::from_json($m), + $json->modified_fields ?? [], + ), + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * 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. + */ + public string|null $change_type, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * List of fields that were changed externally, with their previous and new values. + */ + public array $modified_fields, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * List of fields that were changed externally, with their previous and new values. - */ -class UnmanagedAccessCodeModifiedFields -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessCodeModifiedFields|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessCode\Errors { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); } - return new self( - field: $json->field ?? null, - from: $json->from ?? null, - to: $json->to ?? null, - ); - } - public function __construct( - /** - * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). - */ - public string|null $field, - /** - * The previous value of the field. - */ - public string|null $from, - /** - * The new value of the field. - */ - public string|null $to, - ) {} + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } } -/** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ -class UnmanagedAccessCodeWarnings -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessCodeWarnings|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessCode\Warnings { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => UnmanagedAccessCodeModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * 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. - */ - public string|null $change_type, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array $modified_fields, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } } diff --git a/src/Resources/UnmanagedAccessGrant.php b/src/Resources/UnmanagedAccessGrant.php index 6161249d..cc079819 100644 --- a/src/Resources/UnmanagedAccessGrant.php +++ b/src/Resources/UnmanagedAccessGrant.php @@ -1,428 +1,418 @@ access_grant_id ?? null, - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => UnmanagedAccessGrantErrors::from_json($e), - $json->errors ?? [], - ), - location_ids: $json->location_ids ?? null, - name: $json->name ?? null, - pending_mutations: array_map( - fn($p) => UnmanagedAccessGrantPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - requested_access_methods: array_map( - fn($r) => UnmanagedAccessGrantRequestedAccessMethods::from_json( - $r, + public static function from_json(mixed $json): UnmanagedAccessGrant|null + { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => UnmanagedAccessGrant\Errors::from_json($e), + $json->errors ?? [], ), - $json->requested_access_methods ?? [], - ), - reservation_key: $json->reservation_key ?? null, - space_ids: $json->space_ids ?? null, - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - warnings: array_map( - fn($w) => UnmanagedAccessGrantWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + location_ids: $json->location_ids ?? null, + name: $json->name ?? null, + pending_mutations: array_map( + fn($p) => UnmanagedAccessGrant\PendingMutations::from_json( + $p, + ), + $json->pending_mutations ?? [], + ), + requested_access_methods: array_map( + fn( + $r, + ) => UnmanagedAccessGrant\RequestedAccessMethods::from_json( + $r, + ), + $json->requested_access_methods ?? [], + ), + reservation_key: $json->reservation_key ?? null, + space_ids: $json->space_ids ?? null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + warnings: array_map( + fn($w) => UnmanagedAccessGrant\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * ID of the Access Grant. - */ - public string|null $access_grant_id, - /** - * IDs of the access methods created for the Access Grant. - */ - public array|null $access_method_ids, - /** - * Date and time at which the Access Grant was created. - */ - public string|null $created_at, - /** - * Display name of the Access Grant. - */ - public string|null $display_name, - /** - * Date and time at which the Access Grant ends. - */ - public string|null $ends_at, - /** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ - public array $errors, - /** - * @deprecated Use `space_ids`. - */ - public array|null $location_ids, - /** - * Name of the Access Grant. If not provided, the display name will be computed. - */ - public string|null $name, - /** - * List of pending mutations for the access grant. This shows updates that are in progress. - */ - public array $pending_mutations, - /** - * Access methods that the user requested for the Access Grant. - */ - public array $requested_access_methods, - /** - * Reservation key for the access grant. - */ - public string|null $reservation_key, - /** - * IDs of the spaces to which the Access Grant gives access. - */ - public array|null $space_ids, - /** - * Date and time at which the Access Grant starts. - */ - public string|null $starts_at, - /** - * ID of user identity to which the Access Grant gives access. - */ - public string|null $user_identity_id, - /** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ - public array $warnings, - /** - * ID of the Seam workspace associated with the Access Grant. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the Access Grant. + */ + public string|null $access_grant_id, + /** + * IDs of the access methods created for the Access Grant. + */ + public array|null $access_method_ids, + /** + * Date and time at which the Access Grant was created. + */ + public string|null $created_at, + /** + * Display name of the Access Grant. + */ + public string|null $display_name, + /** + * Date and time at which the Access Grant ends. + */ + public string|null $ends_at, + /** + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + public array $errors, + /** + * @deprecated Use `space_ids`. + */ + public array|null $location_ids, + /** + * Name of the Access Grant. If not provided, the display name will be computed. + */ + public string|null $name, + /** + * List of pending mutations for the access grant. This shows updates that are in progress. + */ + public array $pending_mutations, + /** + * Access methods that the user requested for the Access Grant. + */ + public array $requested_access_methods, + /** + * Reservation key for the access grant. + */ + public string|null $reservation_key, + /** + * IDs of the spaces to which the Access Grant gives access. + */ + public array|null $space_ids, + /** + * Date and time at which the Access Grant starts. + */ + public string|null $starts_at, + /** + * ID of user identity to which the Access Grant gives access. + */ + public string|null $user_identity_id, + /** + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + public array $warnings, + /** + * ID of the Seam workspace associated with the Access Grant. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ -class UnmanagedAccessGrantErrors -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantErrors|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessGrant { + /** + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. - */ - public array|null $missing_device_ids, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. + */ + public array|null $missing_device_ids, + ) {} + } -/** - * 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). - */ -class UnmanagedAccessGrantFailedDevices -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantFailedDevices|null { - if (!$json) { - return null; + /** + * List of pending mutations for the access grant. This shows updates that are in progress. + */ + class PendingMutations + { + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - device_id: $json->device_id ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Device whose access code could not be revoked. - */ - public string|null $device_id, - /** - * Reason the access code could not be revoked (e.g. `offline_access_code_not_revocable`). - */ - public string|null $error_code, - /** - * Human-readable description of why revocation failed. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * IDs of the access methods being updated. + */ + public array|null $access_method_ids, + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + ) {} + } -/** - * Previous location configuration. - */ -class UnmanagedAccessGrantFrom -{ - public static function from_json(mixed $json): UnmanagedAccessGrantFrom|null + /** + * Access methods that the user requested for the Access Grant. + */ + class RequestedAccessMethods { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): RequestedAccessMethods|null { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + created_access_method_ids: $json->created_access_method_ids ?? + null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + instant_key_max_use_count: $json->instant_key_max_use_count ?? + null, + mode: $json->mode ?? null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous device IDs where access codes existed. - */ - public array|null $device_ids, - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Specific PIN code to use for this access method. Only applicable when mode is 'code'. + */ + public string|null $code, + /** + * IDs of the access methods created for the requested access method. + */ + public array|null $created_access_method_ids, + /** + * Date and time at which the requested access method was added to the Access Grant. + */ + public string|null $created_at, + /** + * Display name of the access method. + */ + public string|null $display_name, + /** + * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. + */ + public int|null $instant_key_max_use_count, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + ) {} + } -/** - * List of pending mutations for the access grant. This shows updates that are in progress. - */ -class UnmanagedAccessGrantPendingMutations -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + failed_devices: array_map( + fn($f) => Warnings\FailedDevices::from_json($f), + $json->failed_devices ?? [], + ), + message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, + reason: $json->reason ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? UnmanagedAccessGrantFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? UnmanagedAccessGrantTo::from_json($json->to) - : null, - ); - } - public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous location configuration. - */ - public UnmanagedAccessGrantFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. - */ - public string|null $mutation_code, - /** - * New location configuration. - */ - public UnmanagedAccessGrantTo|null $to, - ) {} + public function __construct( + /** + * IDs of the access methods being updated. + */ + public array|null $access_method_ids, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + public string|null $device_id, + /** + * 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). + */ + public array $failed_devices, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * The new PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that was unavailable. + */ + public string|null $original_code, + /** + * Specific reason why the grant's times are not programmable on the device. + */ + public string|null $reason, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Access methods that the user requested for the Access Grant. - */ -class UnmanagedAccessGrantRequestedAccessMethods -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantRequestedAccessMethods|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessGrant\PendingMutations { + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - code: $json->code ?? null, - created_access_method_ids: $json->created_access_method_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - instant_key_max_use_count: $json->instant_key_max_use_count ?? null, - mode: $json->mode ?? null, - ); - } - public function __construct( - /** - * Specific PIN code to use for this access method. Only applicable when mode is 'code'. - */ - public string|null $code, - /** - * IDs of the access methods created for the requested access method. - */ - public array|null $created_access_method_ids, - /** - * Date and time at which the requested access method was added to the Access Grant. - */ - public string|null $created_at, - /** - * Display name of the access method. - */ - public string|null $display_name, - /** - * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. - */ - public int|null $instant_key_max_use_count, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - ) {} -} + public function __construct( + /** + * Previous device IDs where access codes existed. + */ + public array|null $device_ids, + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} + } -/** - * New location configuration. - */ -class UnmanagedAccessGrantTo -{ - public static function from_json(mixed $json): UnmanagedAccessGrantTo|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + common_code_key: $json->common_code_key ?? null, + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - common_code_key: $json->common_code_key ?? null, - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Common code key to ensure PIN code reuse across devices. - */ - public string|null $common_code_key, - /** - * New device IDs where access codes should be created. - */ - public array|null $device_ids, - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} + public function __construct( + /** + * Common code key to ensure PIN code reuse across devices. + */ + public string|null $common_code_key, + /** + * New device IDs where access codes should be created. + */ + public array|null $device_ids, + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } -/** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ -class UnmanagedAccessGrantWarnings -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantWarnings|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessGrant\Warnings { + /** + * 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). + */ + class FailedDevices + { + public static function from_json(mixed $json): FailedDevices|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - failed_devices: array_map( - fn($f) => UnmanagedAccessGrantFailedDevices::from_json($f), - $json->failed_devices ?? [], - ), - message: $json->message ?? null, - new_code: $json->new_code ?? null, - original_code: $json->original_code ?? null, - reason: $json->reason ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * ID of the device where the requested code was unavailable. - */ - public string|null $device_id, - /** - * 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). - */ - public array $failed_devices, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * The new PIN code that was assigned instead. - */ - public string|null $new_code, - /** - * The originally requested PIN code that was unavailable. - */ - public string|null $original_code, - /** - * Specific reason why the grant's times are not programmable on the device. - */ - public string|null $reason, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Device whose access code could not be revoked. + */ + public string|null $device_id, + /** + * Reason the access code could not be revoked (e.g. `offline_access_code_not_revocable`). + */ + public string|null $error_code, + /** + * Human-readable description of why revocation failed. + */ + public string|null $message, + ) {} + } } diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index b6ac79a7..ab715685 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -1,293 +1,276 @@ access_method_id ?? null, + code: $json->code ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => UnmanagedAccessMethod\Errors::from_json($e), + $json->errors ?? [], + ), + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_issued: $json->is_issued ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, + issued_at: $json->issued_at ?? null, + mode: $json->mode ?? null, + pending_mutations: array_map( + fn($p) => UnmanagedAccessMethod\PendingMutations::from_json( + $p, + ), + $json->pending_mutations ?? [], + ), + warnings: array_map( + fn($w) => UnmanagedAccessMethod\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_method_id: $json->access_method_id ?? null, - code: $json->code ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => UnmanagedAccessMethodErrors::from_json($e), - $json->errors ?? [], - ), - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, - is_issued: $json->is_issued ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, - issued_at: $json->issued_at ?? null, - mode: $json->mode ?? null, - pending_mutations: array_map( - fn($p) => UnmanagedAccessMethodPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - warnings: array_map( - fn($w) => UnmanagedAccessMethodWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the access method. - */ - public string|null $access_method_id, - /** - * The actual PIN code for code access methods. - */ - public string|null $code, - /** - * Date and time at which the access method was created. - */ - public string|null $created_at, - /** - * Display name of the access method. - */ - public string|null $display_name, - /** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ - public array $errors, - /** - * 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. - */ - public bool|null $is_assignment_required, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. - */ - public bool|null $is_encoding_required, - /** - * Indicates whether the access method has been issued. - */ - public bool|null $is_issued, - /** - * 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. - */ - public bool|null $is_ready_for_assignment, - /** - * 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. - */ - public bool|null $is_ready_for_encoding, - /** - * Date and time at which the access method was issued. - */ - public string|null $issued_at, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - public array $pending_mutations, - /** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ - public array $warnings, - /** - * ID of the Seam workspace associated with the access method. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the access method. + */ + public string|null $access_method_id, + /** + * The actual PIN code for code access methods. + */ + public string|null $code, + /** + * Date and time at which the access method was created. + */ + public string|null $created_at, + /** + * Display name of the access method. + */ + public string|null $display_name, + /** + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + public array $errors, + /** + * 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. + */ + public bool|null $is_assignment_required, + /** + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + */ + public bool|null $is_encoding_required, + /** + * Indicates whether the access method has been issued. + */ + public bool|null $is_issued, + /** + * 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. + */ + public bool|null $is_ready_for_assignment, + /** + * 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. + */ + public bool|null $is_ready_for_encoding, + /** + * Date and time at which the access method was issued. + */ + public string|null $issued_at, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + public array $pending_mutations, + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + public array $warnings, + /** + * ID of the Seam workspace associated with the access method. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ -class UnmanagedAccessMethodErrors -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessMethodErrors|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessMethod { + /** + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Previous device configuration. - */ -class UnmanagedAccessMethodFrom -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessMethodFrom|null { - if (!$json) { - return null; + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + class PendingMutations + { + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous device IDs where access was provisioned. - */ - public array|null $device_ids, - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + ) {} + } -/** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ -class UnmanagedAccessMethodPendingMutations -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessMethodPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? UnmanagedAccessMethodFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? UnmanagedAccessMethodTo::from_json($json->to) - : null, - ); - } - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous device configuration. - */ - public UnmanagedAccessMethodFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. - */ - public string|null $mutation_code, - /** - * New device configuration. - */ - public UnmanagedAccessMethodTo|null $to, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * New device configuration. - */ -class UnmanagedAccessMethodTo -{ - public static function from_json(mixed $json): UnmanagedAccessMethodTo|null +namespace Seam\Resources\UnmanagedAccessMethod\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New device IDs where access is being provisioned. - */ - public array|null $device_ids, - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + public array|null $device_ids, + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ -class UnmanagedAccessMethodWarnings -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessMethodWarnings|null { - if (!$json) { - return null; + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - original_access_method_id: $json->original_access_method_id ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + public array|null $device_ids, + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 0dc8fdd0..25d35b1e 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -1,523 +1,551 @@ can_configure_auto_lock ?? null, + can_hvac_cool: $json->can_hvac_cool ?? null, + can_hvac_heat: $json->can_hvac_heat ?? null, + can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, + can_program_offline_access_codes: $json->can_program_offline_access_codes ?? + null, + can_program_online_access_codes: $json->can_program_online_access_codes ?? + null, + can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? + null, + can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? + null, + can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? + null, + can_remotely_lock: $json->can_remotely_lock ?? null, + can_remotely_unlock: $json->can_remotely_unlock ?? null, + can_run_thermostat_programs: $json->can_run_thermostat_programs ?? + null, + can_simulate_connection: $json->can_simulate_connection ?? null, + can_simulate_disconnection: $json->can_simulate_disconnection ?? + null, + can_simulate_hub_connection: $json->can_simulate_hub_connection ?? + null, + can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? + null, + can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? + null, + can_simulate_removal: $json->can_simulate_removal ?? null, + can_turn_off_hvac: $json->can_turn_off_hvac ?? null, + can_unlock_with_code: $json->can_unlock_with_code ?? null, + capabilities_supported: $json->capabilities_supported ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + device_id: $json->device_id ?? null, + device_type: $json->device_type ?? null, + errors: array_map( + fn($e) => UnmanagedDevice\Errors::from_json($e), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + location: isset($json->location) + ? UnmanagedDevice\Location::from_json($json->location) + : null, + properties: isset($json->properties) + ? UnmanagedDevice\Properties::from_json($json->properties) + : null, + warnings: array_map( + fn($w) => UnmanagedDevice\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - can_configure_auto_lock: $json->can_configure_auto_lock ?? null, - can_hvac_cool: $json->can_hvac_cool ?? null, - can_hvac_heat: $json->can_hvac_heat ?? null, - can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, - can_program_offline_access_codes: $json->can_program_offline_access_codes ?? - null, - can_program_online_access_codes: $json->can_program_online_access_codes ?? - null, - can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? - null, - can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? - null, - can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? - null, - can_remotely_lock: $json->can_remotely_lock ?? null, - can_remotely_unlock: $json->can_remotely_unlock ?? null, - can_run_thermostat_programs: $json->can_run_thermostat_programs ?? - null, - can_simulate_connection: $json->can_simulate_connection ?? null, - can_simulate_disconnection: $json->can_simulate_disconnection ?? - null, - can_simulate_hub_connection: $json->can_simulate_hub_connection ?? - null, - can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? - null, - can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? - null, - can_simulate_removal: $json->can_simulate_removal ?? null, - can_turn_off_hvac: $json->can_turn_off_hvac ?? null, - can_unlock_with_code: $json->can_unlock_with_code ?? null, - capabilities_supported: $json->capabilities_supported ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - device_id: $json->device_id ?? null, - device_type: $json->device_type ?? null, - errors: array_map( - fn($e) => UnmanagedDeviceErrors::from_json($e), - $json->errors ?? [], - ), - is_managed: $json->is_managed ?? null, - location: isset($json->location) - ? UnmanagedDeviceLocation::from_json($json->location) - : null, - properties: isset($json->properties) - ? UnmanagedDeviceProperties::from_json($json->properties) - : null, - warnings: array_map( - fn($w) => UnmanagedDeviceWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Indicates whether the lock supports configuring automatic locking. - */ - public bool|null $can_configure_auto_lock, - /** - * Indicates whether the thermostat supports cooling. - */ - public bool|null $can_hvac_cool, - /** - * Indicates whether the thermostat supports heating. - */ - public bool|null $can_hvac_heat, - /** - * Indicates whether the thermostat supports simultaneous heating and cooling. - */ - public bool|null $can_hvac_heat_cool, - /** - * Indicates whether the device supports programming offline access codes. - */ - public bool|null $can_program_offline_access_codes, - /** - * Indicates whether the device supports programming online access codes. - */ - public bool|null $can_program_online_access_codes, - /** - * Indicates whether the thermostat supports different climate programs for each day of the week. - */ - public bool|null $can_program_thermostat_programs_as_different_each_day, - /** - * Indicates whether the thermostat supports a single climate program applied to every day. - */ - public bool|null $can_program_thermostat_programs_as_same_each_day, - /** - * Indicates whether the thermostat supports weekday/weekend climate programs. - */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, - /** - * Indicates whether the device supports remote locking. - */ - public bool|null $can_remotely_lock, - /** - * Indicates whether the device supports remote unlocking. - */ - public bool|null $can_remotely_unlock, - /** - * Indicates whether the thermostat supports running climate programs. - */ - public bool|null $can_run_thermostat_programs, - /** - * Indicates whether the device supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_connection, - /** - * Indicates whether the device supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_disconnection, - /** - * Indicates whether the hub supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_hub_connection, - /** - * Indicates whether the hub supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_hub_disconnection, - /** - * Indicates whether the device supports simulating a paid subscription in a sandbox. - */ - public bool|null $can_simulate_paid_subscription, - /** - * Indicates whether the device supports simulating removal in a sandbox. - */ - public bool|null $can_simulate_removal, - /** - * Indicates whether the thermostat can be turned off. - */ - public bool|null $can_turn_off_hvac, - /** - * Indicates whether the lock supports unlocking with an access code. - */ - public bool|null $can_unlock_with_code, - /** - * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). - */ - public array|null $capabilities_supported, - /** - * Unique identifier for the account associated with the device. - */ - public string|null $connected_account_id, - /** - * Date and time at which the device object was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - */ - public mixed $custom_metadata, - /** - * ID of the device. - */ - public string|null $device_id, - /** - * Type of the device. - */ - public string|null $device_type, - /** - * 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. - */ - public array $errors, - /** - * Indicates that Seam does not manage the device. - */ - public bool|null $is_managed, - /** - * Location information for the device. - */ - public UnmanagedDeviceLocation|null $location, - /** - * properties of the device. - */ - public UnmanagedDeviceProperties|null $properties, - /** - * 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. - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the device. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Indicates whether the lock supports configuring automatic locking. + */ + public bool|null $can_configure_auto_lock, + /** + * Indicates whether the thermostat supports cooling. + */ + public bool|null $can_hvac_cool, + /** + * Indicates whether the thermostat supports heating. + */ + public bool|null $can_hvac_heat, + /** + * Indicates whether the thermostat supports simultaneous heating and cooling. + */ + public bool|null $can_hvac_heat_cool, + /** + * Indicates whether the device supports programming offline access codes. + */ + public bool|null $can_program_offline_access_codes, + /** + * Indicates whether the device supports programming online access codes. + */ + public bool|null $can_program_online_access_codes, + /** + * Indicates whether the thermostat supports different climate programs for each day of the week. + */ + public bool|null $can_program_thermostat_programs_as_different_each_day, + /** + * Indicates whether the thermostat supports a single climate program applied to every day. + */ + public bool|null $can_program_thermostat_programs_as_same_each_day, + /** + * Indicates whether the thermostat supports weekday/weekend climate programs. + */ + public bool|null $can_program_thermostat_programs_as_weekday_weekend, + /** + * Indicates whether the device supports remote locking. + */ + public bool|null $can_remotely_lock, + /** + * Indicates whether the device supports remote unlocking. + */ + public bool|null $can_remotely_unlock, + /** + * Indicates whether the thermostat supports running climate programs. + */ + public bool|null $can_run_thermostat_programs, + /** + * Indicates whether the device supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_connection, + /** + * Indicates whether the device supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_disconnection, + /** + * Indicates whether the hub supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_hub_connection, + /** + * Indicates whether the hub supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_hub_disconnection, + /** + * Indicates whether the device supports simulating a paid subscription in a sandbox. + */ + public bool|null $can_simulate_paid_subscription, + /** + * Indicates whether the device supports simulating removal in a sandbox. + */ + public bool|null $can_simulate_removal, + /** + * Indicates whether the thermostat can be turned off. + */ + public bool|null $can_turn_off_hvac, + /** + * Indicates whether the lock supports unlocking with an access code. + */ + public bool|null $can_unlock_with_code, + /** + * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). + */ + public array|null $capabilities_supported, + /** + * Unique identifier for the account associated with the device. + */ + public string|null $connected_account_id, + /** + * Date and time at which the device object was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + */ + public mixed $custom_metadata, + /** + * ID of the device. + */ + public string|null $device_id, + /** + * Type of the device. + */ + public string|null $device_type, + /** + * 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. + */ + public array $errors, + /** + * Indicates that Seam does not manage the device. + */ + public bool|null $is_managed, + /** + * Location information for the device. + */ + public UnmanagedDevice\Location|null $location, + /** + * properties of the device. + */ + public UnmanagedDevice\Properties|null $properties, + /** + * 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. + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the device. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Accessory keypad properties and state. - */ -class UnmanagedDeviceAccessoryKeypad -{ - public static function from_json( - mixed $json, - ): UnmanagedDeviceAccessoryKeypad|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedDevice { + /** + * 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 Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); } - return new self( - battery: isset($json->battery) - ? UnmanagedDeviceBattery::from_json($json->battery) - : null, - is_connected: $json->is_connected ?? null, - ); - } - public function __construct( - /** - * Keypad battery properties. - */ - public UnmanagedDeviceBattery|null $battery, - /** - * Indicates if an accessory keypad is connected to the device. - */ - public bool|null $is_connected, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Keypad battery properties. - */ -class UnmanagedDeviceBattery -{ - public static function from_json(mixed $json): UnmanagedDeviceBattery|null + /** + * Location information for the device. + */ + class Location { - if (!$json) { - return null; + public static function from_json(mixed $json): Location|null + { + if (!$json) { + return null; + } + return new self( + location_name: $json->location_name ?? null, + time_zone: $json->time_zone ?? null, + timezone: $json->timezone ?? null, + ); } - return new self(level: $json->level ?? null); - } - public function __construct(public float|null $level) {} -} + public function __construct( + /** + * Name of the device location. + */ + public string|null $location_name, + /** + * Time zone of the device location. + */ + public string|null $time_zone, + /** + * Time zone of the device location. + * + * @deprecated Use `time_zone` instead. + */ + public string|null $timezone, + ) {} + } -/** - * 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 UnmanagedDeviceErrors -{ - public static function from_json(mixed $json): UnmanagedDeviceErrors|null + /** + * properties of the device. + */ + class Properties { - if (!$json) { - return null; + public static function from_json(mixed $json): Properties|null + { + if (!$json) { + return null; + } + return new self( + accessory_keypad: isset($json->accessory_keypad) + ? Properties\AccessoryKeypad::from_json( + $json->accessory_keypad, + ) + : null, + battery: isset($json->battery) + ? Properties\Battery::from_json($json->battery) + : null, + battery_level: $json->battery_level ?? null, + image_alt_text: $json->image_alt_text ?? null, + image_url: $json->image_url ?? null, + manufacturer: $json->manufacturer ?? null, + model: isset($json->model) + ? Properties\Model::from_json($json->model) + : null, + name: $json->name ?? null, + offline_access_codes_enabled: $json->offline_access_codes_enabled ?? + null, + online: $json->online ?? null, + online_access_codes_enabled: $json->online_access_codes_enabled ?? + null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. - */ - public bool|null $is_connected_account_error, - /** - * Indicates that the error is not a device error. - */ - public bool|null $is_device_error, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Accessory keypad properties and state. + */ + public Properties\AccessoryKeypad|null $accessory_keypad, + /** + * Represents the current status of the battery charge level. + */ + public Properties\Battery|null $battery, + /** + * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. + */ + public float|null $battery_level, + /** + * Alt text for the device image. + */ + public string|null $image_alt_text, + /** + * Image URL for the device. + */ + public string|null $image_url, + /** + * 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. + */ + public string|null $manufacturer, + /** + * Device model-related properties. + */ + public Properties\Model|null $model, + /** + * Name of the device. + * + * @deprecated use device.display_name instead + */ + public string|null $name, + /** + * Indicates whether it is currently possible to use offline access codes for the device. + * + * @deprecated use device.can_program_offline_access_codes + */ + public bool|null $offline_access_codes_enabled, + /** + * Indicates whether the device is online. + */ + public bool|null $online, + /** + * Indicates whether it is currently possible to use online access codes for the device. + * + * @deprecated use device.can_program_online_access_codes + */ + public bool|null $online_access_codes_enabled, + ) {} + } -/** - * Location information for the device. - */ -class UnmanagedDeviceLocation -{ - public static function from_json(mixed $json): UnmanagedDeviceLocation|null + /** + * 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 Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + active_access_code_count: $json->active_access_code_count ?? + null, + created_at: $json->created_at ?? null, + max_active_access_code_count: $json->max_active_access_code_count ?? + null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - location_name: $json->location_name ?? null, - time_zone: $json->time_zone ?? null, - timezone: $json->timezone ?? null, - ); - } - public function __construct( - /** - * Name of the device location. - */ - public string|null $location_name, - /** - * Time zone of the device location. - */ - public string|null $time_zone, - /** - * Time zone of the device location. - * - * @deprecated Use `time_zone` instead. - */ - public string|null $timezone, - ) {} + public function __construct( + /** + * Number of active access codes on the device when the warning was set. + */ + public int|null $active_access_code_count, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Maximum number of active access codes supported by the device. + */ + public int|null $max_active_access_code_count, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Device model-related properties. - */ -class UnmanagedDeviceModel -{ - public static function from_json(mixed $json): UnmanagedDeviceModel|null +namespace Seam\Resources\UnmanagedDevice\Properties { + /** + * Accessory keypad properties and state. + */ + class AccessoryKeypad { - if (!$json) { - return null; + public static function from_json(mixed $json): AccessoryKeypad|null + { + if (!$json) { + return null; + } + return new self( + battery: isset($json->battery) + ? AccessoryKeypad\Battery::from_json($json->battery) + : null, + is_connected: $json->is_connected ?? null, + ); } - return new self( - accessory_keypad_supported: $json->accessory_keypad_supported ?? - null, - can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? - null, - display_name: $json->display_name ?? null, - has_built_in_keypad: $json->has_built_in_keypad ?? null, - manufacturer_display_name: $json->manufacturer_display_name ?? null, - offline_access_codes_supported: $json->offline_access_codes_supported ?? - null, - online_access_codes_supported: $json->online_access_codes_supported ?? - null, - ); - } - public function __construct( - /** - * @deprecated use device.properties.model.can_connect_accessory_keypad - */ - public bool|null $accessory_keypad_supported, - /** - * Indicates whether the device can connect a accessory keypad. - */ - public bool|null $can_connect_accessory_keypad, - /** - * Display name of the device model. - */ - public string|null $display_name, - /** - * Indicates whether the device has a built in accessory keypad. - */ - public bool|null $has_built_in_keypad, - /** - * Display name that corresponds to the manufacturer-specific terminology for the device. - */ - public string|null $manufacturer_display_name, - /** - * @deprecated use device.can_program_offline_access_codes. - */ - public bool|null $offline_access_codes_supported, - /** - * @deprecated use device.can_program_online_access_codes. - */ - public bool|null $online_access_codes_supported, - ) {} -} + public function __construct( + /** + * Keypad battery properties. + */ + public AccessoryKeypad\Battery|null $battery, + /** + * Indicates if an accessory keypad is connected to the device. + */ + public bool|null $is_connected, + ) {} + } -/** - * properties of the device. - */ -class UnmanagedDeviceProperties -{ - public static function from_json( - mixed $json, - ): UnmanagedDeviceProperties|null { - if (!$json) { - return null; + /** + * Represents the current status of the battery charge level. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self( + level: $json->level ?? null, + status: $json->status ?? null, + ); } - return new self( - accessory_keypad: isset($json->accessory_keypad) - ? UnmanagedDeviceAccessoryKeypad::from_json( - $json->accessory_keypad, - ) - : null, - battery: isset($json->battery) - ? UnmanagedDeviceBattery::from_json($json->battery) - : null, - battery_level: $json->battery_level ?? null, - image_alt_text: $json->image_alt_text ?? null, - image_url: $json->image_url ?? null, - manufacturer: $json->manufacturer ?? null, - model: isset($json->model) - ? UnmanagedDeviceModel::from_json($json->model) - : null, - name: $json->name ?? null, - offline_access_codes_enabled: $json->offline_access_codes_enabled ?? - null, - online: $json->online ?? null, - online_access_codes_enabled: $json->online_access_codes_enabled ?? - null, - ); + + public function __construct( + /** + * Battery charge level as a value between 0 and 1, inclusive. + */ + public float|null $level, + /** + * 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. + */ + public string|null $status, + ) {} } - public function __construct( - /** - * Accessory keypad properties and state. - */ - public UnmanagedDeviceAccessoryKeypad|null $accessory_keypad, - /** - * Represents the current status of the battery charge level. - */ - public UnmanagedDeviceBattery|null $battery, - /** - * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. - */ - public float|null $battery_level, - /** - * Alt text for the device image. - */ - public string|null $image_alt_text, - /** - * Image URL for the device. - */ - public string|null $image_url, - /** - * 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. - */ - public string|null $manufacturer, - /** - * Device model-related properties. - */ - public UnmanagedDeviceModel|null $model, - /** - * Name of the device. - * - * @deprecated use device.display_name instead - */ - public string|null $name, - /** - * Indicates whether it is currently possible to use offline access codes for the device. - * - * @deprecated use device.can_program_offline_access_codes - */ - public bool|null $offline_access_codes_enabled, - /** - * Indicates whether the device is online. - */ - public bool|null $online, - /** - * Indicates whether it is currently possible to use online access codes for the device. - * - * @deprecated use device.can_program_online_access_codes - */ - public bool|null $online_access_codes_enabled, - ) {} + /** + * Device model-related properties. + */ + class Model + { + public static function from_json(mixed $json): Model|null + { + if (!$json) { + return null; + } + return new self( + accessory_keypad_supported: $json->accessory_keypad_supported ?? + null, + can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? + null, + display_name: $json->display_name ?? null, + has_built_in_keypad: $json->has_built_in_keypad ?? null, + manufacturer_display_name: $json->manufacturer_display_name ?? + null, + offline_access_codes_supported: $json->offline_access_codes_supported ?? + null, + online_access_codes_supported: $json->online_access_codes_supported ?? + null, + ); + } + + public function __construct( + /** + * @deprecated use device.properties.model.can_connect_accessory_keypad + */ + public bool|null $accessory_keypad_supported, + /** + * Indicates whether the device can connect a accessory keypad. + */ + public bool|null $can_connect_accessory_keypad, + /** + * Display name of the device model. + */ + public string|null $display_name, + /** + * Indicates whether the device has a built in accessory keypad. + */ + public bool|null $has_built_in_keypad, + /** + * Display name that corresponds to the manufacturer-specific terminology for the device. + */ + public string|null $manufacturer_display_name, + /** + * @deprecated use device.can_program_offline_access_codes. + */ + public bool|null $offline_access_codes_supported, + /** + * @deprecated use device.can_program_online_access_codes. + */ + public bool|null $online_access_codes_supported, + ) {} + } } -/** - * 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 UnmanagedDeviceWarnings -{ - public static function from_json(mixed $json): UnmanagedDeviceWarnings|null +namespace Seam\Resources\UnmanagedDevice\Properties\AccessoryKeypad { + /** + * Keypad battery properties. + */ + class Battery { - if (!$json) { - return null; + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); } - return new self( - active_access_code_count: $json->active_access_code_count ?? null, - created_at: $json->created_at ?? null, - max_active_access_code_count: $json->max_active_access_code_count ?? - null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Number of active access codes on the device when the warning was set. - */ - public int|null $active_access_code_count, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Maximum number of active access codes supported by the device. - */ - public int|null $max_active_access_code_count, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct(public float|null $level) {} + } } diff --git a/src/Resources/UnmanagedUserIdentity.php b/src/Resources/UnmanagedUserIdentity.php index 6f950ce7..a1da9a34 100644 --- a/src/Resources/UnmanagedUserIdentity.php +++ b/src/Resources/UnmanagedUserIdentity.php @@ -1,155 +1,156 @@ acs_user_ids ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + email_address: $json->email_address ?? null, + errors: array_map( + fn($e) => UnmanagedUserIdentity\Errors::from_json($e), + $json->errors ?? [], + ), + full_name: $json->full_name ?? null, + phone_number: $json->phone_number ?? null, + user_identity_id: $json->user_identity_id ?? null, + warnings: array_map( + fn($w) => UnmanagedUserIdentity\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_user_ids: $json->acs_user_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - email_address: $json->email_address ?? null, - errors: array_map( - fn($e) => UnmanagedUserIdentityErrors::from_json($e), - $json->errors ?? [], - ), - full_name: $json->full_name ?? null, - phone_number: $json->phone_number ?? null, - user_identity_id: $json->user_identity_id ?? null, - warnings: array_map( - fn($w) => UnmanagedUserIdentityWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Array of access system user IDs associated with the user identity. - */ - public array|null $acs_user_ids, - /** - * Date and time at which the user identity was created. - */ - public string|null $created_at, - /** - * Display name for the user identity. - */ - public string|null $display_name, - /** - * Unique email address for the user identity. - */ - public string|null $email_address, - /** - * 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. - */ - public array $errors, - /** - * Full name of the user associated with the user identity. - */ - public string|null $full_name, - /** - * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). - */ - public string|null $phone_number, - /** - * ID of the user identity. - */ - public string|null $user_identity_id, - /** - * 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. - */ - public array $warnings, - /** - * ID of the workspace that contains the user identity. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Array of access system user IDs associated with the user identity. + */ + public array|null $acs_user_ids, + /** + * Date and time at which the user identity was created. + */ + public string|null $created_at, + /** + * Display name for the user identity. + */ + public string|null $display_name, + /** + * Unique email address for the user identity. + */ + public string|null $email_address, + /** + * 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. + */ + public array $errors, + /** + * Full name of the user associated with the user identity. + */ + public string|null $full_name, + /** + * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). + */ + public string|null $phone_number, + /** + * ID of the user identity. + */ + public string|null $user_identity_id, + /** + * 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. + */ + public array $warnings, + /** + * ID of the workspace that contains the user identity. + */ + public string|null $workspace_id, + ) {} + } } -/** - * 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 UnmanagedUserIdentityErrors -{ - public static function from_json( - mixed $json, - ): UnmanagedUserIdentityErrors|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedUserIdentity { + /** + * 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 Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * ID of the access system that the user identity is associated with. - */ - public string|null $acs_system_id, - /** - * ID of the access system user that has an issue. - */ - public string|null $acs_user_id, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * ID of the access system that the user identity is associated with. + */ + public string|null $acs_system_id, + /** + * ID of the access system user that has an issue. + */ + public string|null $acs_user_id, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * 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 UnmanagedUserIdentityWarnings -{ - public static function from_json( - mixed $json, - ): UnmanagedUserIdentityWarnings|null { - if (!$json) { - return null; + /** + * 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 Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } diff --git a/src/Resources/UserIdentity.php b/src/Resources/UserIdentity.php index d96105cd..ededd870 100644 --- a/src/Resources/UserIdentity.php +++ b/src/Resources/UserIdentity.php @@ -1,158 +1,160 @@ acs_user_ids ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + email_address: $json->email_address ?? null, + errors: array_map( + fn($e) => UserIdentity\Errors::from_json($e), + $json->errors ?? [], + ), + full_name: $json->full_name ?? null, + phone_number: $json->phone_number ?? null, + user_identity_id: $json->user_identity_id ?? null, + user_identity_key: $json->user_identity_key ?? null, + warnings: array_map( + fn($w) => UserIdentity\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_user_ids: $json->acs_user_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - email_address: $json->email_address ?? null, - errors: array_map( - fn($e) => UserIdentityErrors::from_json($e), - $json->errors ?? [], - ), - full_name: $json->full_name ?? null, - phone_number: $json->phone_number ?? null, - user_identity_id: $json->user_identity_id ?? null, - user_identity_key: $json->user_identity_key ?? null, - warnings: array_map( - fn($w) => UserIdentityWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Array of access system user IDs associated with the user identity. - */ - public array|null $acs_user_ids, - /** - * Date and time at which the user identity was created. - */ - public string|null $created_at, - /** - * Display name for the user identity. - */ - public string|null $display_name, - /** - * Unique email address for the user identity. - */ - public string|null $email_address, - /** - * 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. - */ - public array $errors, - /** - * Full name of the user associated with the user identity. - */ - public string|null $full_name, - /** - * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). - */ - public string|null $phone_number, - /** - * ID of the user identity. - */ - public string|null $user_identity_id, - /** - * Unique key for the user identity. - */ - public string|null $user_identity_key, - /** - * 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. - */ - public array $warnings, - /** - * ID of the workspace that contains the user identity. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Array of access system user IDs associated with the user identity. + */ + public array|null $acs_user_ids, + /** + * Date and time at which the user identity was created. + */ + public string|null $created_at, + /** + * Display name for the user identity. + */ + public string|null $display_name, + /** + * Unique email address for the user identity. + */ + public string|null $email_address, + /** + * 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. + */ + public array $errors, + /** + * Full name of the user associated with the user identity. + */ + public string|null $full_name, + /** + * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). + */ + public string|null $phone_number, + /** + * ID of the user identity. + */ + public string|null $user_identity_id, + /** + * Unique key for the user identity. + */ + public string|null $user_identity_key, + /** + * 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. + */ + public array $warnings, + /** + * ID of the workspace that contains the user identity. + */ + public string|null $workspace_id, + ) {} + } } -/** - * 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 UserIdentityErrors -{ - public static function from_json(mixed $json): UserIdentityErrors|null +namespace Seam\Resources\UserIdentity { + /** + * 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 Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * ID of the access system that the user identity is associated with. - */ - public string|null $acs_system_id, - /** - * ID of the access system user that has an issue. - */ - public string|null $acs_user_id, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * ID of the access system that the user identity is associated with. + */ + public string|null $acs_system_id, + /** + * ID of the access system user that has an issue. + */ + public string|null $acs_user_id, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * 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 UserIdentityWarnings -{ - public static function from_json(mixed $json): UserIdentityWarnings|null + /** + * 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 Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } diff --git a/src/Resources/Webhook.php b/src/Resources/Webhook.php index 6a39a342..37eb5751 100644 --- a/src/Resources/Webhook.php +++ b/src/Resources/Webhook.php @@ -1,41 +1,41 @@ event_types ?? null, + secret: $json->secret ?? null, + url: $json->url ?? null, + webhook_id: $json->webhook_id ?? null, + ); } - return new self( - event_types: $json->event_types ?? null, - secret: $json->secret ?? null, - url: $json->url ?? null, - webhook_id: $json->webhook_id ?? null, - ); - } - public function __construct( - /** - * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. - */ - public array|null $event_types, - /** - * Secret associated with the [webhook](https://docs.seam.co/developer-tools/webhooks). - */ - public string|null $secret, - /** - * URL for the [webhook](https://docs.seam.co/developer-tools/webhooks). - */ - public string|null $url, - /** - * ID of the webhook. - */ - public string|null $webhook_id, - ) {} + public function __construct( + /** + * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. + */ + public array|null $event_types, + /** + * Secret associated with the [webhook](https://docs.seam.co/developer-tools/webhooks). + */ + public string|null $secret, + /** + * URL for the [webhook](https://docs.seam.co/developer-tools/webhooks). + */ + public string|null $url, + /** + * ID of the webhook. + */ + public string|null $webhook_id, + ) {} + } } diff --git a/src/Resources/Workspace.php b/src/Resources/Workspace.php index 86e4acb1..4901d453 100644 --- a/src/Resources/Workspace.php +++ b/src/Resources/Workspace.php @@ -1,116 +1,119 @@ company_name ?? null, - connect_partner_name: $json->connect_partner_name ?? null, - connect_webview_customization: isset( - $json->connect_webview_customization, - ) - ? WorkspaceConnectWebviewCustomization::from_json( + public static function from_json(mixed $json): Workspace|null + { + if (!$json) { + return null; + } + return new self( + company_name: $json->company_name ?? null, + connect_partner_name: $json->connect_partner_name ?? null, + connect_webview_customization: isset( $json->connect_webview_customization, ) - : null, - is_publishable_key_auth_enabled: $json->is_publishable_key_auth_enabled ?? - null, - is_sandbox: $json->is_sandbox ?? null, - is_suspended: $json->is_suspended ?? null, - name: $json->name ?? null, - organization_id: $json->organization_id ?? null, - publishable_key: $json->publishable_key ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } + ? Workspace\ConnectWebviewCustomization::from_json( + $json->connect_webview_customization, + ) + : null, + is_publishable_key_auth_enabled: $json->is_publishable_key_auth_enabled ?? + null, + is_sandbox: $json->is_sandbox ?? null, + is_suspended: $json->is_suspended ?? null, + name: $json->name ?? null, + organization_id: $json->organization_id ?? null, + publishable_key: $json->publishable_key ?? null, + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Company name associated with the [workspace](https://docs.seam.co/core-concepts/workspaces). - */ - public string|null $company_name, - /** - * @deprecated Use `company_name` instead. - */ - public string|null $connect_partner_name, - public WorkspaceConnectWebviewCustomization|null $connect_webview_customization, - /** - * Indicates whether publishable key authentication is enabled for this workspace. - */ - public bool|null $is_publishable_key_auth_enabled, - /** - * Indicates whether the workspace is a [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces). - */ - public bool|null $is_sandbox, - /** - * Indicates whether the [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is suspended. Seam suspends sandbox workspaces that have not been accessed in 14 days. - */ - public bool|null $is_suspended, - /** - * Name of the [workspace](https://docs.seam.co/core-concepts/workspaces). - */ - public string|null $name, - /** - * ID of the organization to which the workspace belongs, or `null` if the workspace is not assigned to an organization. - */ - public string|null $organization_id, - /** - * Publishable key for the [workspace](https://docs.seam.co/core-concepts/workspaces). This key is used to identify the workspace in client-side applications. - */ - public string|null $publishable_key, - /** - * ID of the workspace. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Company name associated with the [workspace](https://docs.seam.co/core-concepts/workspaces). + */ + public string|null $company_name, + /** + * @deprecated Use `company_name` instead. + */ + public string|null $connect_partner_name, + public Workspace\ConnectWebviewCustomization|null $connect_webview_customization, + /** + * Indicates whether publishable key authentication is enabled for this workspace. + */ + public bool|null $is_publishable_key_auth_enabled, + /** + * Indicates whether the workspace is a [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces). + */ + public bool|null $is_sandbox, + /** + * Indicates whether the [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is suspended. Seam suspends sandbox workspaces that have not been accessed in 14 days. + */ + public bool|null $is_suspended, + /** + * Name of the [workspace](https://docs.seam.co/core-concepts/workspaces). + */ + public string|null $name, + /** + * ID of the organization to which the workspace belongs, or `null` if the workspace is not assigned to an organization. + */ + public string|null $organization_id, + /** + * Publishable key for the [workspace](https://docs.seam.co/core-concepts/workspaces). This key is used to identify the workspace in client-side applications. + */ + public string|null $publishable_key, + /** + * ID of the workspace. + */ + public string|null $workspace_id, + ) {} + } } -class WorkspaceConnectWebviewCustomization -{ - public static function from_json( - mixed $json, - ): WorkspaceConnectWebviewCustomization|null { - if (!$json) { - return null; +namespace Seam\Resources\Workspace { + class ConnectWebviewCustomization + { + public static function from_json( + mixed $json, + ): ConnectWebviewCustomization|null { + if (!$json) { + return null; + } + return new self( + inviter_logo_url: $json->inviter_logo_url ?? null, + logo_shape: $json->logo_shape ?? null, + primary_button_color: $json->primary_button_color ?? null, + primary_button_text_color: $json->primary_button_text_color ?? + null, + success_message: $json->success_message ?? null, + ); } - return new self( - inviter_logo_url: $json->inviter_logo_url ?? null, - logo_shape: $json->logo_shape ?? null, - primary_button_color: $json->primary_button_color ?? null, - primary_button_text_color: $json->primary_button_text_color ?? null, - success_message: $json->success_message ?? null, - ); - } - public function __construct( - /** - * URL of the inviter logo for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $inviter_logo_url, - /** - * Logo shape for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $logo_shape, - /** - * Primary button color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $primary_button_color, - /** - * Primary button text color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $primary_button_text_color, - /** - * Success message for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $success_message, - ) {} + public function __construct( + /** + * URL of the inviter logo for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $inviter_logo_url, + /** + * Logo shape for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $logo_shape, + /** + * Primary button color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $primary_button_color, + /** + * Primary button text color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $primary_button_text_color, + /** + * Success message for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $success_message, + ) {} + } } diff --git a/tests/ResourceTest.php b/tests/ResourceTest.php new file mode 100644 index 00000000..58d19ed7 --- /dev/null +++ b/tests/ResourceTest.php @@ -0,0 +1,161 @@ + [ + "battery" => ["level" => 0.4, "status" => "low"], + "accessory_keypad" => ["battery" => ["level" => 0.25]], + ], + "errors" => [ + [ + "error_code" => "device_offline", + "is_device_error" => true, + ], + ], + ]), + ), + ); + + $this->assertNotNull($device); + + return $device; + } + + public function testNestedPropertyClassesAreNamespacedByTheirOwner(): void + { + $device = $this->device(); + + $this->assertInstanceOf(Device\Properties::class, $device->properties); + $this->assertInstanceOf( + Device\Properties\Battery::class, + $device->properties->battery, + ); + $this->assertInstanceOf( + Device\Properties\AccessoryKeypad::class, + $device->properties->accessory_keypad, + ); + $this->assertInstanceOf( + Device\Properties\AccessoryKeypad\Battery::class, + $device->properties->accessory_keypad->battery, + ); + } + + /** + * The two batteries used to collapse onto one Seam\Resources\DeviceBattery, + * and whichever was generated first won, so the device battery lost its + * status. + */ + public function testSameNamedPropertiesAtDifferentDepthsKeepTheirShapes(): void + { + $device = $this->device(); + + $this->assertNotSame( + Device\Properties\Battery::class, + Device\Properties\AccessoryKeypad\Battery::class, + ); + + $this->assertSame(0.4, $device->properties->battery->level); + $this->assertSame("low", $device->properties->battery->status); + + $this->assertSame( + 0.25, + $device->properties->accessory_keypad->battery->level, + ); + $this->assertObjectNotHasProperty( + "status", + $device->properties->accessory_keypad->battery, + ); + } + + public function testNestedClassesAreNotDeclaredInTheResourcesNamespace(): void + { + $this->assertFalse( + class_exists("Seam\\Resources\\DeviceBattery"), + "Nested classes must not be flattened into Seam\\Resources", + ); + $this->assertFalse( + class_exists("Seam\\Resources\\DeviceProperties"), + "Nested classes must not be flattened into Seam\\Resources", + ); + } + + /** + * A list of discriminated objects collapses into one class carrying the + * union of the variant properties, so a field only some variants declare is + * still readable. + */ + public function testDiscriminatedListMergesEveryVariantProperty(): void + { + $device = $this->device(); + + $error = $device->errors[0]; + + $this->assertInstanceOf(Device\Errors::class, $error); + $this->assertSame("device_offline", $error->error_code); + $this->assertTrue($error->is_device_error); + $this->assertObjectHasProperty("is_bridge_error", $error); + $this->assertNull($error->is_bridge_error); + } + + /** + * Every class in the file has to be registered, because src/Resources is + * autoloaded by classmap rather than PSR-4. + */ + public function testNestedClassesAreAutoloadable(): void + { + foreach ( + [ + Device\Properties\Battery::class, + Device\Properties\AccessoryKeypad\Battery::class, + Device\Properties\AvailableClimatePresets::class, + Device\Properties\AvailableClimatePresets\EcobeeMetadata::class, + ] + as $className + ) { + $this->assertTrue( + class_exists($className), + "{$className} should be autoloadable", + ); + } + } + + /** + * The climate preset metadata used to take the shape of the device level + * ecobee_metadata, losing these three fields. + */ + public function testClimatePresetMetadataKeepsItsOwnShape(): void + { + $preset = Device\Properties\AvailableClimatePresets\EcobeeMetadata::from_json( + json_decode( + json_encode([ + "climate_ref" => "sleep", + "is_optimized" => true, + "owner" => "user", + ]), + ), + ); + + $this->assertNotNull($preset); + $this->assertSame("sleep", $preset->climate_ref); + $this->assertTrue($preset->is_optimized); + $this->assertSame("user", $preset->owner); + } +} From 7e04f8e5ff1b0ed6c40fa3f72a11a8939d67bcb7 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 20:20:37 +0000 Subject: [PATCH 04/80] refactor: share the Ruby SDK's merge-properties verbatim The merge module was written from the specification rather than copied, so it had drifted from the Ruby SDK in details that matter for keeping the two in step: the merged list was not sorted, the path in a list recursion omitted the [] segment, the format list in the disagreement error was sorted, and the error messages were worded differently. Take codegen/lib/merge-properties.ts from seamapi/ruby as it stands, so the two SDKs share one implementation and a future change to the semantics is a single diff to port rather than a reconciliation. Only the sort is observable here: it reorders the properties of the two merged resources, which reorders the nested classes emitted for them. The class and namespace sets of ActionAttempt and Event are unchanged. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- codegen/lib/merge-properties.ts | 169 +++++++++----------- src/Resources/ActionAttempt.php | 120 +++++++-------- src/Resources/Event.php | 262 ++++++++++++++++---------------- 3 files changed, 262 insertions(+), 289 deletions(-) diff --git a/codegen/lib/merge-properties.ts b/codegen/lib/merge-properties.ts index 43a28523..70c7bd8e 100644 --- a/codegen/lib/merge-properties.ts +++ b/codegen/lib/merge-properties.ts @@ -1,70 +1,59 @@ -// Merges the property lists of a discriminated union into one list. -// -// Two things in the blueprint are unions the SDK collapses into a single -// class: the event and action attempt resources, which the blueprint models as -// one resource per variant, and any property that is a list of discriminated -// objects. -// -// This logic is shared with the Ruby and Python SDKs. Changing the semantics -// here means changing them there too, or the SDKs drift. - import type { Property } from '@seamapi/blueprint' -/** - * Unions property lists by name, recursively. - * - * A name carried by one variant is taken as is. A name carried by several is - * merged: objects and lists of objects recurse, lists of discriminated objects - * concatenate their variants for whoever consumes the list to merge in turn, - * and scalars collapse onto one another because they map to the same PHP type. - */ -export const mergeProperties = ( - propertyLists: Property[][], - path = '', -): Property[] => { - const occurrencesByName = new Map() +const formatKey = (property: Property): string => + property.format === 'list' ? `list<${property.itemFormat}>` : property.format - for (const properties of propertyLists) { - for (const property of properties) { - const occurrences = occurrencesByName.get(property.name) - if (occurrences == null) { - occurrencesByName.set(property.name, [property]) - continue - } - occurrences.push(property) - } - } +const isScalar = (property: Property): boolean => + property.format !== 'list' && property.format !== 'object' - return [...occurrencesByName.entries()].map(([name, occurrences]) => - mergeOccurrences(occurrences, path === '' ? name : `${path}.${name}`), - ) +interface MergedDocs { + description: string + isDeprecated: boolean + deprecationMessage: string } -const mergeOccurrences = (occurrences: Property[], path: string): Property => { - const [first] = occurrences +// Each variant documents a property for its own case, which is accurate there +// but not for the single class the variants merge into. Some are merely narrow +// ("Previous code configuration" on a shape that also covers names); others +// contradict each other outright ("the error is not a device error" against +// "the error is a device error"). No description beats a wrong one, so keep one +// only when every variant that documents the property agrees. +const mergeDocs = (occurrences: Property[]): MergedDocs => { + const descriptions = [ + ...new Set( + occurrences + .map((occurrence) => occurrence.description.trim()) + .filter((description) => description !== ''), + ), + ] + const deprecated = occurrences.find(({ isDeprecated }) => isDeprecated) - if (first == null) { - throw new Error(`Cannot merge ${path}: no occurrences`) + return { + description: descriptions.length === 1 ? (descriptions[0] ?? '') : '', + // Deprecating in any variant deprecates the merged property, so a warning + // is never dropped just because another variant omits it. + isDeprecated: deprecated != null, + deprecationMessage: deprecated?.deprecationMessage ?? '', } +} - if (occurrences.length === 1) return first +// The variants of a discriminated union collapse into a single class, so a +// property carried by more than one variant has to end up with every field any +// variant gives it. Keeping only the first occurrence silently drops the rest, +// which loses data once the merged shape is a typed class rather than a hash. +const mergeOccurrences = (occurrences: Property[], path: string): Property => { + const [first, ...rest] = occurrences + if (first == null) throw new Error(`Nothing to merge at ${path}.`) + if (rest.length === 0) return first - // Each variant documents the property for its own case, which is accurate - // there and not necessarily on the class the variants collapse into. Keep - // the text only when every variant that documents it agrees, since no - // documentation beats wrong documentation. const docs = mergeDocs(occurrences) - const formatKeys = new Set(occurrences.map(getFormatKey)) - - if (formatKeys.size > 1) { - if (occurrences.every(isScalar)) { - // Differing scalar formats map to the same PHP type, so any one will do. - return { ...first, ...docs } - } - + const formats = new Set(occurrences.map(formatKey)) + if (formats.size > 1) { + // Scalars all become a plain accessor, so any of them represents the rest. + if (occurrences.every(isScalar)) return { ...first, ...docs } throw new Error( - `Cannot merge ${path}: variants disagree on its shape (${[...formatKeys].sort().join(', ')})`, + `Cannot merge ${path}: variants disagree on its shape (${[...formats].join(', ')}).`, ) } @@ -73,8 +62,8 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { ...first, ...docs, properties: mergeProperties( - occurrences.map((occurrence) => - occurrence.format === 'object' ? occurrence.properties : [], + occurrences.map( + (occurrence) => (occurrence as typeof first).properties, ), path, ), @@ -86,25 +75,21 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { ...first, ...docs, itemProperties: mergeProperties( - occurrences.map((occurrence) => - occurrence.format === 'list' && occurrence.itemFormat === 'object' - ? occurrence.itemProperties - : [], + occurrences.map( + (occurrence) => (occurrence as typeof first).itemProperties, ), - path, + `${path}[]`, ), } } if (first.format === 'list' && first.itemFormat === 'discriminated_object') { + // Keep every variant. Whoever consumes this list merges them in turn. return { ...first, ...docs, - variants: occurrences.flatMap((occurrence) => - occurrence.format === 'list' && - occurrence.itemFormat === 'discriminated_object' - ? occurrence.variants - : [], + variants: occurrences.flatMap( + (occurrence) => (occurrence as typeof first).variants, ), } } @@ -112,37 +97,25 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { return { ...first, ...docs } } -interface MergedDocs { - description: string - isDeprecated: boolean - deprecationMessage: string -} - -const mergeDocs = (occurrences: Property[]): MergedDocs => { - const descriptions = [ - ...new Set( - occurrences - .map((occurrence) => occurrence.description.trim()) - .filter((description) => description !== ''), - ), - ] - - // Deprecating a property in any variant deprecates it on the merged class: - // first wins could silently undeprecate a field depending on the order the - // blueprint happens to list the variants in. - const deprecated = occurrences.find((occurrence) => occurrence.isDeprecated) - - return { - description: descriptions.length === 1 ? (descriptions[0] ?? '') : '', - isDeprecated: deprecated != null, - deprecationMessage: deprecated?.deprecationMessage ?? '', +export const mergeProperties = ( + propertyLists: Property[][], + path = '', +): Property[] => { + const occurrences = new Map() + for (const properties of propertyLists) { + for (const property of properties) { + const group = occurrences.get(property.name) + if (group == null) { + occurrences.set(property.name, [property]) + } else { + group.push(property) + } + } } -} - -// A list of objects and a list of scalars are different shapes, so the item -// format is part of the key. -const getFormatKey = (property: Property): string => - property.format === 'list' ? `list<${property.itemFormat}>` : property.format -const isScalar = (property: Property): boolean => - property.format !== 'list' && property.format !== 'object' + return [...occurrences.entries()] + .map(([name, group]) => + mergeOccurrences(group, path === '' ? name : `${path}.${name}`), + ) + .sort((a, b) => a.name.localeCompare(b.name)) +} diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 3a471b9d..2af52ae4 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -545,44 +545,6 @@ public function __construct( ) {} } - class Warnings - { - public static function from_json(mixed $json): Warnings|null - { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - original_access_method_id: $json->original_access_method_id ?? - null, - warning_code: $json->warning_code ?? null, - warning_message: $json->warning_message ?? null, - ); - } - - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id, - public string|null $warning_code, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $warning_message, - ) {} - } - /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -684,6 +646,53 @@ public function __construct( ) {} } + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + class PendingMutations + { + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + /** + * Previous access time configuration. + */ + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + /** + * Mutation code to indicate that Seam is in the process of updating the access times for this access method. + */ + public string|null $mutation_code, + /** + * New access time configuration. + */ + public PendingMutations\To|null $to, + ) {} + } + /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -743,50 +752,41 @@ public function __construct( ) {} } - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - class PendingMutations + class Warnings { - public static function from_json(mixed $json): PendingMutations|null + public static function from_json(mixed $json): Warnings|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, - from: isset($json->from) - ? PendingMutations\From::from_json($json->from) - : null, message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? PendingMutations\To::from_json($json->to) - : null, + original_access_method_id: $json->original_access_method_id ?? + null, + warning_code: $json->warning_code ?? null, + warning_message: $json->warning_message ?? null, ); } public function __construct( /** - * Date and time at which the mutation was created. + * Date and time at which Seam created the warning. */ public string|null $created_at, /** - * Previous access time configuration. - */ - public PendingMutations\From|null $from, - /** - * Detailed description of the mutation. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, /** - * Mutation code to indicate that Seam is in the process of updating the access times for this access method. + * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $mutation_code, + public string|null $original_access_method_id, + public string|null $warning_code, /** - * New access time configuration. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public PendingMutations\To|null $to, + public string|null $warning_message, ) {} } } diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 4ce80506..f11c863e 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -447,204 +447,167 @@ public function __construct( namespace Seam\Resources\Event { /** - * List of properties that changed on the access code. + * Errors associated with the access code. */ - class ChangedProperties - { - public static function from_json(mixed $json): ChangedProperties|null - { - if (!$json) { - return null; - } - return new self( - from: $json->from ?? null, - property: $json->property ?? null, - to: $json->to ?? null, - ); - } - - public function __construct( - /** - * Previous value of the property, or null if not set. - */ - public string|null $from, - /** - * Name of the property that changed (e.g. `code`). - */ - public string|null $property, - /** - * New value of the property, or null if cleared. - */ - public string|null $to, - ) {} - } - - class From + class AccessCodeErrors { - public static function from_json(mixed $json): From|null + public static function from_json(mixed $json): AccessCodeErrors|null { if (!$json) { return null; } return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Previous pin code. - */ - public string|null $code, - /** - * Previous end time. + * Date and time at which Seam created the error. */ - public string|null $ends_at, + public string|null $created_at, /** - * Previous name of the access code. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $name, + public string|null $error_code, /** - * Previous start time. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $starts_at, + public string|null $message, ) {} } - class To + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings { - public static function from_json(mixed $json): To|null + public static function from_json(mixed $json): AccessCodeWarnings|null { if (!$json) { return null; } return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * New pin code. - */ - public string|null $code, - /** - * New end time. + * Date and time at which Seam created the warning. */ - public string|null $ends_at, + public string|null $created_at, /** - * New name of the access code. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $name, + public string|null $message, /** - * New start time. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $starts_at, + public string|null $warning_code, ) {} } /** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. + * Errors associated with the access control system. */ - class RequestedMutations + class AcsSystemErrors { - public static function from_json(mixed $json): RequestedMutations|null + public static function from_json(mixed $json): AcsSystemErrors|null { if (!$json) { return null; } return new self( - from: $json->from ?? null, - mutation_code: $json->mutation_code ?? null, - to: $json->to ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + * Date and time at which Seam created the error. */ - public mixed $from, + public string|null $created_at, /** - * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $mutation_code, + public string|null $error_code, /** - * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public mixed $to, + public string|null $message, ) {} } /** - * Errors associated with the access code. + * Warnings associated with the access control system. */ - class AccessCodeErrors + class AcsSystemWarnings { - public static function from_json(mixed $json): AccessCodeErrors|null + public static function from_json(mixed $json): AcsSystemWarnings|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Date and time at which Seam created the error. + * Date and time at which Seam created the warning. */ public string|null $created_at, /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $error_code, + public string|null $message, /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $message, + public string|null $warning_code, ) {} } /** - * Warnings associated with the access code. + * List of properties that changed on the access code. */ - class AccessCodeWarnings + class ChangedProperties { - public static function from_json(mixed $json): AccessCodeWarnings|null + public static function from_json(mixed $json): ChangedProperties|null { if (!$json) { return null; } return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, + from: $json->from ?? null, + property: $json->property ?? null, + to: $json->to ?? null, ); } public function __construct( /** - * Date and time at which Seam created the warning. + * Previous value of the property, or null if not set. */ - public string|null $created_at, + public string|null $from, /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Name of the property that changed (e.g. `code`). */ - public string|null $message, + public string|null $property, /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * New value of the property, or null if cleared. */ - public string|null $warning_code, + public string|null $to, ) {} } @@ -782,97 +745,134 @@ public function __construct( ) {} } - /** - * Errors associated with the access control system. - */ - class AcsSystemErrors + class From { - public static function from_json(mixed $json): AcsSystemErrors|null + public static function from_json(mixed $json): From|null { if (!$json) { return null; } return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, ); } public function __construct( /** - * Date and time at which Seam created the error. + * Previous pin code. */ - public string|null $created_at, + public string|null $code, /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * Previous end time. */ - public string|null $error_code, + public string|null $ends_at, /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + * Previous name of the access code. */ - public string|null $message, + public string|null $name, + /** + * Previous start time. + */ + public string|null $starts_at, ) {} } /** - * Warnings associated with the access control system. + * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. */ - class AcsSystemWarnings + class Reason { - public static function from_json(mixed $json): AcsSystemWarnings|null + public static function from_json(mixed $json): Reason|null { if (!$json) { return null; } return new self( - created_at: $json->created_at ?? null, message: $json->message ?? null, - warning_code: $json->warning_code ?? null, + reason_code: $json->reason_code ?? null, ); } public function __construct( /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Human-readable explanation of why access was denied. */ public string|null $message, /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. */ - public string|null $warning_code, + public string|null $reason_code, ) {} } /** - * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + * Array of mutations requested on the access code, each containing the mutation type and from/to values. */ - class Reason + class RequestedMutations { - public static function from_json(mixed $json): Reason|null + public static function from_json(mixed $json): RequestedMutations|null { if (!$json) { return null; } return new self( - message: $json->message ?? null, - reason_code: $json->reason_code ?? null, + from: $json->from ?? null, + mutation_code: $json->mutation_code ?? null, + to: $json->to ?? null, ); } public function __construct( /** - * Human-readable explanation of why access was denied. + * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. */ - public string|null $message, + public mixed $from, /** - * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. + * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. */ - public string|null $reason_code, + public string|null $mutation_code, + /** + * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + */ + public mixed $to, + ) {} + } + + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * New pin code. + */ + public string|null $code, + /** + * New end time. + */ + public string|null $ends_at, + /** + * New name of the access code. + */ + public string|null $name, + /** + * New start time. + */ + public string|null $starts_at, ) {} } } From 4ed18b39fb36967a98462225c5963a18d8b502f9 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 20:33:59 +0000 Subject: [PATCH 05/80] fix: lock dependencies against the minimum supported PHP composer.json requires php ^8.1 but set no resolution platform, so the lock was resolved against whatever PHP generated it. Generated on 8.4, that selected Symfony 8.x, which requires php >=8.4.1, and composer install then refused the lockfile on 8.1, 8.2 and 8.3: Your lock file does not contain a compatible set of packages. - symfony/console is locked to version v8.1.2 ... - symfony/console v8.1.2 requires php >=8.4.1 -> your php version (8.2.33) does not satisfy that requirement. Pin config.platform.php to the oldest PHP this package supports so the lock represents that platform rather than the machine that happened to write it. Symfony drops to 6.4 LTS; Psalm and PHPUnit are unchanged. The patch version is 8.1.31 because Psalm 6 requires ~8.1.31 on the 8.1 line. The Install jobs passed throughout because they synthesize a composer.json and never read this lockfile, which is why only Test and Lint caught it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- composer.json | 3 + composer.lock | 235 ++++++++++++++++---------------------------------- 2 files changed, 76 insertions(+), 162 deletions(-) diff --git a/composer.json b/composer.json index 486452eb..d5125706 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,9 @@ } }, "config": { + "platform": { + "php": "8.1.31" + }, "sort-packages": true }, "archive": { diff --git a/composer.lock b/composer.lock index a604302c..65ea9390 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "25d67117228a0425a94bd9f40c53f692", + "content-hash": "05bbb1b6caed944f0bc5ea91d511b7de", "packages": [ { "name": "caseyamcl/guzzle_retry_middleware", @@ -2400,20 +2400,20 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.4", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.0" }, "conflict": { "doctrine/collections": "<1.6.8", @@ -2448,15 +2448,15 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + "source": "https://github.com/myclabs/DeepCopy/tree/1.14.0" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://github.com/mnapoli", + "type": "github" } ], - "time": "2025-08-01T08:46:24+00:00" + "time": "2026-08-11T10:17:44+00:00" }, { "name": "netresearch/jsonmapper", @@ -4266,16 +4266,16 @@ }, { "name": "sebastian/recursion-context", - "version": "5.0.1", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + "reference": "5d32fe257a9b39cb63146924d6b4e32a22d4502a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5d32fe257a9b39cb63146924d6b4e32a22d4502a", + "reference": "5d32fe257a9b39cb63146924d6b4e32a22d4502a", "shasum": "" }, "require": { @@ -4318,7 +4318,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.2" }, "funding": [ { @@ -4338,7 +4338,7 @@ "type": "tidelift" } ], - "time": "2025-08-10T07:50:56+00:00" + "time": "2026-08-11T05:27:39+00:00" }, { "name": "sebastian/type", @@ -4519,49 +4519,47 @@ }, { "name": "symfony/console", - "version": "v8.1.2", + "version": "v6.4.43", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15" + "reference": "3b643aa587acbc42f967a429af088a56ed8f046d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/535e18a1b8925f6c01a55b171d157ab66c2ace15", - "reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15", + "url": "https://api.github.com/repos/symfony/console/zipball/3b643aa587acbc42f967a429af088a56ed8f046d", + "reference": "3b643aa587acbc42f967a429af088a56ed8f046d", "shasum": "" }, "require": { - "php": ">=8.4.1", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php85": "^1.32", + "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.4.6|^8.0.6" + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/dependency-injection": "<8.1", - "symfony/event-dispatcher": "<8.1" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^8.1", - "symfony/event-dispatcher": "^8.1", - "symfony/filesystem": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/lock": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/mime": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0", - "symfony/stopwatch": "^7.4|^8.0", - "symfony/uid": "^7.4|^8.0", - "symfony/validator": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -4595,7 +4593,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.1.2" + "source": "https://github.com/symfony/console/tree/v6.4.43" }, "funding": [ { @@ -4615,30 +4613,29 @@ "type": "tidelift" } ], - "time": "2026-07-27T13:58:19+00:00" + "time": "2026-07-26T14:44:19+00:00" }, { "name": "symfony/filesystem", - "version": "v8.1.2", + "version": "v6.4.43", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "17856b7a222664a26a5ea1cb06ee0721c2438217" + "reference": "9ff03da12d67649fbd1f34ca95951554624d0a16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/17856b7a222664a26a5ea1cb06ee0721c2438217", - "reference": "17856b7a222664a26a5ea1cb06ee0721c2438217", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/9ff03da12d67649fbd1f34ca95951554624d0a16", + "reference": "9ff03da12d67649fbd1f34ca95951554624d0a16", "shasum": "" }, "require": { - "php": ">=8.4.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^7.4|^8.0" + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", "autoload": { @@ -4666,7 +4663,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.1.2" + "source": "https://github.com/symfony/filesystem/tree/v6.4.43" }, "funding": [ { @@ -4686,7 +4683,7 @@ "type": "tidelift" } ], - "time": "2026-07-22T15:42:13+00:00" + "time": "2026-06-27T10:13:35+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5103,86 +5100,6 @@ ], "time": "2026-05-26T12:51:13+00:00" }, - { - "name": "symfony/polyfill-php85", - "version": "v1.41.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", - "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php85\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-07-01T12:47:55+00:00" - }, { "name": "symfony/service-contracts", "version": "v3.7.1", @@ -5272,34 +5189,33 @@ }, { "name": "symfony/string", - "version": "v8.1.2", + "version": "v6.4.43", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc" + "reference": "2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", - "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", + "url": "https://api.github.com/repos/symfony/string/zipball/2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49", + "reference": "2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49", "shasum": "" }, "require": { - "php": ">=8.4.1", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-intl-grapheme": "^1.33", - "symfony/polyfill-intl-normalizer": "^1.0", - "symfony/polyfill-mbstring": "^1.0" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.4|^8.0", - "symfony/http-client": "^7.4|^8.0", - "symfony/intl": "^7.4|^8.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^7.4|^8.0" + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -5338,7 +5254,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.1.2" + "source": "https://github.com/symfony/string/tree/v6.4.43" }, "funding": [ { @@ -5358,7 +5274,7 @@ "type": "tidelift" } ], - "time": "2026-07-28T07:35:25+00:00" + "time": "2026-07-28T07:28:15+00:00" }, { "name": "theseer/tokenizer", @@ -5530,23 +5446,23 @@ }, { "name": "webmozart/assert", - "version": "2.4.1", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", - "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { "ext-ctype": "*", "ext-date": "*", "ext-filter": "*", - "php": "^8.2" + "php": "^7.2 || ^8.0" }, "suggest": { "ext-intl": "", @@ -5555,12 +5471,8 @@ }, "type": "library", "extra": { - "psalm": { - "pluginClass": "Webmozart\\Assert\\PsalmPlugin" - }, "branch-alias": { - "dev-master": "2.0-dev", - "dev-feature/2-0": "2.0-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -5576,10 +5488,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" - }, - { - "name": "Woody Gilk", - "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -5590,9 +5498,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.4.1" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2026-06-15T15:31:57+00:00" + "time": "2025-10-29T15:56:20+00:00" } ], "aliases": [], @@ -5604,5 +5512,8 @@ "php": "^8.1" }, "platform-dev": {}, + "platform-overrides": { + "php": "8.1.31" + }, "plugin-api-version": "2.6.0" } From 76ae49f32b1050f49b85076d522a810f4b1d0d2b Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:22:37 +0000 Subject: [PATCH 06/80] feat: drop support for PHP 8.1 PHP 8.1 reached end of life in December 2025, so the oldest version this package supports is now 8.2, which has security support through 2026. Raise the floor in composer.json, move the resolution platform to 8.2.27, the patch Psalm 6 requires on the 8.2 line, and drop 8.1 from the CI matrices. Symfony moves up to 7.4 now that 8.2 is the target. BREAKING CHANGE: PHP 8.2 or later is required. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- .github/workflows/check.yml | 4 +- README.md | 9 +-- composer.json | 4 +- composer.lock | 116 ++++++++++++++++++++---------------- 4 files changed, 71 insertions(+), 62 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 749b0e39..e9772866 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,7 +20,6 @@ jobs: os: - ubuntu-latest php: - - '8.1' - '8.2' - '8.3' - '8.4' @@ -47,7 +46,7 @@ jobs: fail-fast: false matrix: php: - - '8.1' + - '8.2' - '8.5' steps: - name: Checkout @@ -76,7 +75,6 @@ jobs: os: - ubuntu-latest php: - - '8.1' - '8.2' - '8.3' - '8.4' diff --git a/README.md b/README.md index 9ba25b03..1eaa1324 100644 --- a/README.md +++ b/README.md @@ -391,7 +391,8 @@ try { Version 4 brings this SDK in line with the Seam SDKs for other languages. -- PHP 8.1 or later is now required. +- PHP 8.2 or later is now required. PHP 8.1 reached end of life in + December 2025. - The client class is `Seam\Seam`. `Seam\SeamClient` still works as a deprecated alias. - The constructor takes named options. `$endpoint` is no longer the second @@ -416,7 +417,7 @@ Version 4 brings this SDK in line with the Seam SDKs for other languages. ### Quickstart -Install [PHP](https://www.php.net/) 8.1 or later, +Install [PHP](https://www.php.net/) 8.2 or later, [Composer](https://getcomposer.org/) and [Node.js](https://nodejs.org/), then run @@ -471,8 +472,8 @@ create pressure to change the generator. ### Requirements -This package supports PHP 8.1 and later. -Continuous integration exercises every supported version, PHP 8.1 through 8.5. +This package supports PHP 8.2 and later. +Continuous integration exercises every supported version, PHP 8.2 through 8.5. The test suite runs against [@seamapi/fake-seam-connect], which is started automatically for each test, so `npm install` must have been run first. diff --git a/composer.json b/composer.json index d5125706..fb1f683d 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "source": "https://github.com/seamapi/php" }, "require": { - "php": "^8.1", + "php": "^8.2", "caseyamcl/guzzle_retry_middleware": "^2.13", "guzzlehttp/guzzle": "^7.5", "svix/svix": "^1.40" @@ -49,7 +49,7 @@ }, "config": { "platform": { - "php": "8.1.31" + "php": "8.2.27" }, "sort-packages": true }, diff --git a/composer.lock b/composer.lock index 65ea9390..29d6834a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "05bbb1b6caed944f0bc5ea91d511b7de", + "content-hash": "fcadd2bfc0a095fb22d744660cd07366", "packages": [ { "name": "caseyamcl/guzzle_retry_middleware", @@ -4519,47 +4519,47 @@ }, { "name": "symfony/console", - "version": "v6.4.43", + "version": "v7.4.16", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3b643aa587acbc42f967a429af088a56ed8f046d" + "reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3b643aa587acbc42f967a429af088a56ed8f046d", - "reference": "3b643aa587acbc42f967a429af088a56ed8f046d", + "url": "https://api.github.com/repos/symfony/console/zipball/f4c69c9aed03abf933b294257d618bdd9b30a06d", + "reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^7.2|^8.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4593,7 +4593,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.43" + "source": "https://github.com/symfony/console/tree/v7.4.16" }, "funding": [ { @@ -4613,29 +4613,29 @@ "type": "tidelift" } ], - "time": "2026-07-26T14:44:19+00:00" + "time": "2026-07-31T12:37:14+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.43", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "9ff03da12d67649fbd1f34ca95951554624d0a16" + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/9ff03da12d67649fbd1f34ca95951554624d0a16", - "reference": "9ff03da12d67649fbd1f34ca95951554624d0a16", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ff16a16bf87fdf264638b8f6995b3515975e3c79", + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" + "symfony/process": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4663,7 +4663,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.43" + "source": "https://github.com/symfony/filesystem/tree/v7.4.15" }, "funding": [ { @@ -4683,7 +4683,7 @@ "type": "tidelift" } ], - "time": "2026-06-27T10:13:35+00:00" + "time": "2026-07-22T07:36:05+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5189,22 +5189,23 @@ }, { "name": "symfony/string", - "version": "v6.4.43", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49" + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49", - "reference": "2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.33", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, @@ -5212,10 +5213,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5254,7 +5256,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.43" + "source": "https://github.com/symfony/string/tree/v7.4.15" }, "funding": [ { @@ -5274,7 +5276,7 @@ "type": "tidelift" } ], - "time": "2026-07-28T07:28:15+00:00" + "time": "2026-07-28T07:33:02+00:00" }, { "name": "theseer/tokenizer", @@ -5446,23 +5448,23 @@ }, { "name": "webmozart/assert", - "version": "1.12.1", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", "shasum": "" }, "require": { "ext-ctype": "*", "ext-date": "*", "ext-filter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.2" }, "suggest": { "ext-intl": "", @@ -5471,8 +5473,12 @@ }, "type": "library", "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, "branch-alias": { - "dev-master": "1.10-dev" + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" } }, "autoload": { @@ -5488,6 +5494,10 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -5498,9 +5508,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" + "source": "https://github.com/webmozarts/assert/tree/2.4.1" }, - "time": "2025-10-29T15:56:20+00:00" + "time": "2026-06-15T15:31:57+00:00" } ], "aliases": [], @@ -5509,11 +5519,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.1" + "php": "^8.2" }, "platform-dev": {}, "platform-overrides": { - "php": "8.1.31" + "php": "8.2.27" }, "plugin-api-version": "2.6.0" } From 094b05c61148a3eebeb4f49330048fd18e989758 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:27:40 +0000 Subject: [PATCH 07/80] docs: call endpoint methods with named arguments PHP cannot declare a parameter as name-only the way Python's keyword-only marker and Ruby's required keyword arguments do, so the calling convention can only be documented rather than enforced. Parameter order comes from the API definition, so an endpoint that gains a required parameter can reorder the ones already there, and a positional call then binds a value to the wrong parameter with nothing to catch it. Every example now passes arguments by name, and the usage section says why. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1eaa1324..83446cd6 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,14 @@ $ composer require seamapi/seam > [!NOTE] > These examples assume `SEAM_API_KEY` is set in your environment. +Endpoint methods take [named arguments], which is the supported way to call +them. PHP allows the parameters to be passed positionally too, but their order +is derived from the API definition and can change as endpoints gain parameters, +so a positional call can start binding a value to the wrong parameter after an +upgrade. Passing them by name is stable. + +[named arguments]: https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments + ### Examples #### List devices @@ -55,7 +63,7 @@ $devices = $seam->devices->list(); $seam = new Seam\Seam(); $lock = $seam->locks->get(name: "Front Door"); -$seam->locks->unlock_door($lock->device_id); +$seam->locks->unlock_door(device_id: $lock->device_id); ``` ### Authentication Method @@ -122,7 +130,7 @@ use Seam\ActionAttemptFailedError; use Seam\ActionAttemptTimeoutError; try { - $seam->locks->unlock_door($device_id); + $seam->locks->unlock_door(device_id: $device_id); } catch (ActionAttemptFailedError $error) { print "Could not unlock the door: " . $error->getMessage(); print "Error code: " . $error->getErrorCode(); @@ -137,7 +145,7 @@ Waiting may be disabled for the whole client: ```php $seam = new Seam\Seam(wait_for_action_attempt: false); -$action_attempt = $seam->locks->unlock_door($device_id); +$action_attempt = $seam->locks->unlock_door(device_id: $device_id); $action_attempt->status; // "pending" ``` @@ -145,7 +153,7 @@ or for a single request: ```php $action_attempt = $seam->locks->unlock_door( - $device_id, + device_id: $device_id, wait_for_action_attempt: false ); ``` @@ -159,7 +167,7 @@ $seam = new Seam\Seam( ); $seam->locks->unlock_door( - $device_id, + device_id: $device_id, wait_for_action_attempt: ["timeout" => 5.0] ); ``` @@ -377,7 +385,7 @@ use Seam\HttpApiError; use Seam\HttpInvalidInputError; try { - $seam->devices->get($device_id); + $seam->devices->get(device_id: $device_id); } catch (HttpInvalidInputError $error) { print_r($error->getValidationErrorMessages("device_id")); } catch (HttpApiError $error) { From 38afcd49994dde5da3dd2ff9a86957c67e11cec0 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:36:33 +0000 Subject: [PATCH 08/80] refactor: rename the multi workspace client to without workspace The JavaScript SDK renamed this client to SeamHttpWithoutWorkspace and left SeamHttpMultiWorkspace behind as a deprecated alias, since what the class actually does is reach the endpoints that take no workspace in scope rather than several workspaces at once. This SDK is introducing the class now, so it can start from the current name with no alias to carry. Seam\SeamMultiWorkspace becomes Seam\SeamWithoutWorkspace, mirroring Seam\Seam the way the JavaScript name mirrors SeamHttp. The auth helper and the README section follow, so multi workspace is gone as vocabulary. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 6 ++--- src/Auth.php | 2 +- ...Workspace.php => SeamWithoutWorkspace.php} | 4 ++-- src/WorkspacesProxy.php | 2 +- tests/PersonalAccessTokenTest.php | 22 +++++++++---------- 5 files changed, 18 insertions(+), 18 deletions(-) rename src/{SeamMultiWorkspace.php => SeamWithoutWorkspace.php} (95%) diff --git a/README.md b/README.md index 83446cd6..98d661eb 100644 --- a/README.md +++ b/README.md @@ -259,13 +259,13 @@ $pages = $seam->createPaginator( $connectedAccounts = $pages->flattenToArray(); ``` -### Interacting with Multiple Workspaces +### Requests without a Workspace in scope -Some endpoints are not scoped to a workspace. Use `SeamMultiWorkspace` with a +Some endpoints are not scoped to a workspace. Use `SeamWithoutWorkspace` with a personal access token to reach them. ```php -$seam = Seam\SeamMultiWorkspace::from_personal_access_token( +$seam = Seam\SeamWithoutWorkspace::from_personal_access_token( "your-personal-access-token" ); diff --git a/src/Auth.php b/src/Auth.php index f35f5b68..6023be71 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -109,7 +109,7 @@ public static function get_auth_headers_for_personal_access_token( * * @return array */ - public static function get_auth_headers_for_multi_workspace_personal_access_token( + public static function get_auth_headers_for_personal_access_token_without_workspace( string $personal_access_token, ): array { self::assert_personal_access_token($personal_access_token); diff --git a/src/SeamMultiWorkspace.php b/src/SeamWithoutWorkspace.php similarity index 95% rename from src/SeamMultiWorkspace.php rename to src/SeamWithoutWorkspace.php index c1dfaad2..b62bcb5d 100644 --- a/src/SeamMultiWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -12,7 +12,7 @@ * Authenticates with a personal access token and exposes only the workspace * listing and creation endpoints. Use Seam\Seam for everything else. */ -class SeamMultiWorkspace +class SeamWithoutWorkspace { public const LTS_VERSION = SeamHttpClient::LTS_VERSION; @@ -43,7 +43,7 @@ public function __construct( $this->client = new SeamHttpClient( Options::get_endpoint($endpoint), - Auth::get_auth_headers_for_multi_workspace_personal_access_token( + Auth::get_auth_headers_for_personal_access_token_without_workspace( $personal_access_token, ), $guzzle_options, diff --git a/src/WorkspacesProxy.php b/src/WorkspacesProxy.php index 904d338d..0c596e60 100644 --- a/src/WorkspacesProxy.php +++ b/src/WorkspacesProxy.php @@ -7,7 +7,7 @@ /** * Exposes only the workspace endpoints that work without a workspace in - * scope, which are the only ones a SeamMultiWorkspace client can call. + * scope, which are the only ones a SeamWithoutWorkspace client can call. */ class WorkspacesProxy { diff --git a/tests/PersonalAccessTokenTest.php b/tests/PersonalAccessTokenTest.php index bce47735..850e1016 100644 --- a/tests/PersonalAccessTokenTest.php +++ b/tests/PersonalAccessTokenTest.php @@ -7,7 +7,7 @@ use Seam\InvalidOptionsError; use Seam\InvalidTokenError; use Seam\Seam; -use Seam\SeamMultiWorkspace; +use Seam\SeamWithoutWorkspace; use Tests\Support\FakeSeamConnectTestCase; final class PersonalAccessTokenTest extends FakeSeamConnectTestCase @@ -78,9 +78,9 @@ public function testPersonalAccessTokenFormatIsChecked(): void ); } - public function testMultiWorkspaceClientListsWorkspaces(): void + public function testWithoutWorkspaceClientListsWorkspaces(): void { - $seam = SeamMultiWorkspace::from_personal_access_token( + $seam = SeamWithoutWorkspace::from_personal_access_token( $this->seed["seam_at1_token"], endpoint: $this->endpoint, ); @@ -95,9 +95,9 @@ public function testMultiWorkspaceClientListsWorkspaces(): void $this->assertContains($this->seed["seed_workspace_1"], $workspace_ids); } - public function testMultiWorkspaceConstructorListsWorkspaces(): void + public function testWithoutWorkspaceConstructorListsWorkspaces(): void { - $seam = new SeamMultiWorkspace( + $seam = new SeamWithoutWorkspace( personal_access_token: $this->seed["seam_at1_token"], endpoint: $this->endpoint, ); @@ -105,9 +105,9 @@ public function testMultiWorkspaceConstructorListsWorkspaces(): void $this->assertNotEmpty($seam->workspaces->list()); } - public function testMultiWorkspaceClientCreatesAWorkspace(): void + public function testWithoutWorkspaceClientCreatesAWorkspace(): void { - $seam = SeamMultiWorkspace::from_personal_access_token( + $seam = SeamWithoutWorkspace::from_personal_access_token( $this->seed["seam_at1_token"], endpoint: $this->endpoint, ); @@ -121,18 +121,18 @@ public function testMultiWorkspaceClientCreatesAWorkspace(): void $this->assertSame("Test Workspace", $workspace->name); } - public function testMultiWorkspaceClientRequiresAToken(): void + public function testWithoutWorkspaceClientRequiresAToken(): void { $this->expectException(InvalidOptionsError::class); - new SeamMultiWorkspace(endpoint: $this->endpoint); + new SeamWithoutWorkspace(endpoint: $this->endpoint); } - public function testMultiWorkspaceClientChecksTheTokenFormat(): void + public function testWithoutWorkspaceClientChecksTheTokenFormat(): void { $this->expectException(InvalidTokenError::class); - SeamMultiWorkspace::from_personal_access_token( + SeamWithoutWorkspace::from_personal_access_token( $this->seed["seam_apikey1_token"], endpoint: $this->endpoint, ); From c13b28dc8db417e9960beb47c5680cfefa4fd95e Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:52:33 +0000 Subject: [PATCH 09/80] refactor: make the client the Guzzle client $seam->client was a wrapper that had to be unwrapped with get_client() to reach the Guzzle client underneath. It is now the Guzzle client itself, so anything Guzzle can do is reachable without a detour, and there is one client object in the public surface rather than two. Error mapping moves to Guzzle middleware, which fits better than the wrapper did: it sits outside the retry middleware, so it only sees the response a request finally settled on, and it holds the real request rather than a fabricated one when raising a transport error. Reading the response body moves to Seam\Http\Body, called by the generated route methods. The timeout drops from 60 to 30 seconds and becomes an option of its own rather than something to bury in guzzle_options, alongside retries. It covers connecting as well as reading. Seam::request() is gone; use $seam->client->request(). BREAKING CHANGE: $seam->client is now the Guzzle client, so $seam->client->get_client() no longer exists, and Seam\Http\SeamHttpClient is replaced by Seam\Http\ClientFactory. Seam::request() is removed. Requests now time out after 30 seconds rather than 60. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 165 ++++++---- codegen/layouts/partials/client-class.hbs | 4 +- codegen/layouts/partials/route-method.hbs | 6 +- codegen/layouts/seam-client.hbs | 56 ++-- codegen/lib/layouts/route.ts | 9 +- src/ActionAttemptFailedError.php | 3 +- src/Http/Body.php | 36 +++ src/Http/ClientFactory.php | 147 +++++++++ src/Http/ErrorMiddleware.php | 109 +++++++ src/Http/ResolveActionAttempt.php | 20 +- src/Http/SeamHttpClient.php | 287 ------------------ src/Options.php | 2 +- src/Routes/AccessCodesClient.php | 83 +++-- src/Routes/AccessCodesSimulateClient.php | 17 +- src/Routes/AccessCodesUnmanagedClient.php | 41 ++- src/Routes/AccessGrantsClient.php | 65 ++-- src/Routes/AccessGrantsUnmanagedClient.php | 31 +- src/Routes/AccessMethodsClient.php | 63 ++-- src/Routes/AccessMethodsUnmanagedClient.php | 23 +- src/Routes/AcsAccessGroupsClient.php | 65 ++-- src/Routes/AcsClient.php | 6 +- src/Routes/AcsCredentialsClient.php | 73 +++-- src/Routes/AcsEncodersClient.php | 49 +-- src/Routes/AcsEncodersSimulateClient.php | 14 +- src/Routes/AcsEntrancesClient.php | 49 +-- src/Routes/AcsSystemsClient.php | 41 +-- src/Routes/AcsUsersClient.php | 91 +++--- src/Routes/ActionAttemptsClient.php | 23 +- src/Routes/ClientSessionsClient.php | 63 ++-- src/Routes/ConnectWebviewsClient.php | 39 ++- src/Routes/ConnectedAccountsClient.php | 47 ++- .../ConnectedAccountsSimulateClient.php | 8 +- src/Routes/CustomersClient.php | 31 +- src/Routes/DevicesClient.php | 47 ++- src/Routes/DevicesSimulateClient.php | 48 ++- src/Routes/DevicesUnmanagedClient.php | 31 +- src/Routes/EventsClient.php | 23 +- src/Routes/InstantKeysClient.php | 31 +- src/Routes/LocksClient.php | 47 +-- src/Routes/LocksSimulateClient.php | 27 +- src/Routes/NoiseSensorsClient.php | 15 +- .../NoiseSensorsNoiseThresholdsClient.php | 41 +-- src/Routes/NoiseSensorsSimulateClient.php | 8 +- src/Routes/PhonesClient.php | 31 +- src/Routes/PhonesSimulateClient.php | 17 +- src/Routes/SpacesClient.php | 103 +++---- src/Routes/ThermostatsClient.php | 111 ++++--- src/Routes/ThermostatsDailyProgramsClient.php | 35 ++- src/Routes/ThermostatsSchedulesClient.php | 47 ++- src/Routes/ThermostatsSimulateClient.php | 10 +- src/Routes/UserIdentitiesClient.php | 115 +++---- src/Routes/UserIdentitiesUnmanagedClient.php | 31 +- src/Routes/WebhooksClient.php | 41 ++- src/Routes/WorkspacesClient.php | 31 +- src/Seam.php | 59 ++-- src/SeamClient.php | 9 - src/SeamWithoutWorkspace.php | 17 +- tests/ClientTest.php | 65 ++-- tests/HeadersTest.php | 4 +- tests/HttpErrorTest.php | 30 +- tests/PackageVersionTest.php | 2 +- tests/RetryTest.php | 10 +- tests/Support/FakeSeamConnect.php | 3 +- tests/WaitForActionAttemptTest.php | 8 +- 64 files changed, 1436 insertions(+), 1427 deletions(-) create mode 100644 src/Http/Body.php create mode 100644 src/Http/ClientFactory.php create mode 100644 src/Http/ErrorMiddleware.php delete mode 100644 src/Http/SeamHttpClient.php delete mode 100644 src/SeamClient.php diff --git a/README.md b/README.md index 98d661eb..5b8ab1b3 100644 --- a/README.md +++ b/README.md @@ -317,27 +317,29 @@ the client the SDK builds, so the authorization and SDK headers are kept. ```php $seam = new Seam\Seam( guzzle_options: [ - "timeout" => 30, "headers" => ["X-Custom-Header" => "value"], "proxy" => "http://localhost:8125", ] ); ``` -> [!NOTE] -> Unlike the other Seam SDKs, this one sets a default request timeout of 60 -> seconds so a hung connection eventually fails. Pass `timeout` to change it, -> or `0` to disable it. +#### Setting the timeout + +Requests time out after 30 seconds by default, covering both connecting and +reading. Pass `timeout` in seconds to change it, or `0` to disable it. + +```php +$seam = new Seam\Seam(timeout: 60.0); +``` #### Retries Failed requests are retried twice by default, with exponential backoff. -A request that never reached the server, e.g. a connection failure, is always -retried. A request that did reach the server is only retried on a retryable -status when the HTTP method is idempotent. Every Seam endpoint is a `POST`, so -retrying one that the server may already have processed could duplicate a -write. The other Seam SDKs make the same trade. +A request that never reached the server, such as a connection failure, is +always retried, since it cannot have had an effect. A request the server may +already have processed is retried only when repeating it is safe, so a retry +never risks duplicating a write. ```php // Retry more times @@ -347,6 +349,21 @@ $seam = new Seam\Seam(retries: 5); $seam = new Seam\Seam(retries: 0); ``` +#### Using the Guzzle client + +`$seam->client` is the [Guzzle] client, already carrying the endpoint and +authorization, so it can be used to reach an endpoint the SDK does not expose. + +[Guzzle]: https://docs.guzzlephp.org/ + +```php +$response = $seam->client->request("POST", "/devices/list", [ + "json" => (object) ["limit" => 10], +]); + +$devices = Seam\Http\Body::decode($response)->devices; +``` + #### Overriding the client Pass an already configured Guzzle client. It carries its own endpoint and @@ -363,22 +380,11 @@ $seam = Seam\Seam::from_client($client); #### Errors -Every exception the SDK raises implements `Seam\SeamException`. - -| Error | Raised when | -| --------------------------- | -------------------------------------------------- | -| `HttpApiError` | The API returned an error response. | -| `HttpUnauthorizedError` | The credentials were rejected. | -| `HttpInvalidInputError` | The request parameters were rejected. | -| `InvalidOptionsError` | The client options are incomplete or incompatible. | -| `InvalidTokenError` | The token is of the wrong kind or format. | -| `ActionAttemptFailedError` | An action attempt finished in the error state. | -| `ActionAttemptTimeoutError` | An action attempt did not finish in time. | - -An error response that is not shaped like a Seam error, e.g. a gateway -returning HTML, surfaces as the underlying `GuzzleHttp\Exception\ -BadResponseException` instead. Transport failures surface as the -corresponding Guzzle exception. +Every exception the SDK raises implements `Seam\SeamException`, so it can be +caught as a group. An API error is a `Seam\HttpApiError` carrying +`getErrorCode()`, `getStatusCode()` and `getRequestId()`, with +`Seam\HttpUnauthorizedError` and `Seam\HttpInvalidInputError` as the two +specific cases worth catching on their own. ```php use Seam\HttpApiError; @@ -390,36 +396,29 @@ try { print_r($error->getValidationErrorMessages("device_id")); } catch (HttpApiError $error) { print $error->getErrorCode(); - print $error->getStatusCode(); - print $error->getRequestId(); } ``` +A response that is not shaped like a Seam error, such as a gateway returning +HTML, raises the underlying Guzzle exception instead. + ## Upgrading from 3.x -Version 4 brings this SDK in line with the Seam SDKs for other languages. - -- PHP 8.2 or later is now required. PHP 8.1 reached end of life in - December 2025. -- The client class is `Seam\Seam`. `Seam\SeamClient` still works as a - deprecated alias. -- The constructor takes named options. `$endpoint` is no longer the second - positional argument, and `$throw_http_errors` is gone; API errors always - raise a Seam exception. -- The exception classes keep their `Seam\` namespace. They now all implement - a shared `Seam\SeamException` interface, and `Seam\InvalidOptionsError` and - `Seam\InvalidTokenError` are new. -- `$seam->action_attempts->poll_until_ready()` was removed. Use - `wait_for_action_attempt`, which now also accepts a `timeout` and - `polling_interval`. The defaults changed from 20s/0.4s to 10s/1s. -- `$seam->client` is a `Seam\Http\SeamHttpClient`. The Guzzle client is - available with `$seam->client->get_client()`. -- The `$seam->api_key` property and the global `LTS_VERSION` constant were - removed. Use `Seam\Seam::LTS_VERSION`. -- Responses in the 3xx range are no longer treated as successful. -- Requests are now retried; see [Retries](#retries). -- Pagination metadata is a `Seam\Pagination` object rather than a - `stdClass`. +- PHP 8.2 or later is required. PHP 8.1 reached end of life in December 2025. +- The client class is `Seam\Seam`, replacing `Seam\SeamClient`. +- The constructor takes named options, so `$endpoint` is no longer the second + positional argument, and `$throw_http_errors` is gone: an API error always + raises. +- The exception classes stay in the `Seam\` namespace and now share a + `Seam\SeamException` interface. +- `$seam->action_attempts->poll_until_ready()` is gone. Use + `wait_for_action_attempt`, which also takes a `timeout` and + `polling_interval`. +- `$seam->client` is the Guzzle client, and `$seam->api_key` and the global + `LTS_VERSION` constant are gone. Use `Seam\Seam::LTS_VERSION`. +- A response in the 3xx range is no longer treated as successful. +- Requests time out after 30 seconds and are retried twice. +- Pagination metadata is a `Seam\Pagination` rather than a `stdClass`. ## Development and Testing @@ -457,6 +456,17 @@ Formatting is handled by [Prettier](https://prettier.io/) via so PHP, TypeScript, JSON, YAML and Markdown are all formatted by `npm run format`. +### Source code + +The [source code] is hosted on GitHub. +Clone the project with + +``` +$ git clone git@github.com:seamapi/php.git +``` + +[source code]: https://github.com/seamapi/php + ### Running Tests Run the full suite with @@ -524,3 +534,56 @@ picks up the new tag from its GitHub webhook. Development files are kept out of the published package with `export-ignore` rules in `.gitattributes`, which `git archive` honours when GitHub builds the archives Composer downloads as `dist`. + +## GitHub Actions + +_GitHub Actions should already be configured: this section is for reference only._ + +Publishing is handled by [Packagist], which reads new versions from the git +tags this repository pushes, so no registry token is needed. + +[Packagist]: https://packagist.org/packages/seamapi/seam + +### Secrets for Optional GitHub Actions + +The version, format, generate, and semantic-release GitHub actions +require a user with write access to the repository. +Set these additional secrets to enable the action: + +- `GH_TOKEN`: A personal access token for the user. +- `GIT_USER_NAME`: The GitHub user's real name. +- `GIT_USER_EMAIL`: The GitHub user's email. +- `GPG_PRIVATE_KEY`: The GitHub user's [GPG private key]. +- `GPG_PASSPHRASE`: The GitHub user's GPG passphrase. + +[GPG private key]: https://github.com/marketplace/actions/import-gpg#prerequisites + +## Contributing + +Please submit and comment on bug reports and feature requests. + +To submit a patch: + +1. Fork it (https://github.com/seamapi/php/fork). +2. Create your feature branch (`git checkout -b my-new-feature`). +3. Make changes. +4. Commit your changes (`git commit -am 'Add some feature'`). +5. Push to the branch (`git push origin my-new-feature`). +6. Create a new Pull Request. + +## License + +This PHP package is licensed under the MIT license. + +## Warranty + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are +disclaimed. In no event shall the copyright holder or contributors be liable for +any direct, indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused and on +any theory of liability, whether in contract, strict liability, or tort +(including negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/codegen/layouts/partials/client-class.hbs b/codegen/layouts/partials/client-class.hbs index 79d6dd3d..8a36f141 100644 --- a/codegen/layouts/partials/client-class.hbs +++ b/codegen/layouts/partials/client-class.hbs @@ -1,6 +1,6 @@ class {{clientName}}Client { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class {{clientName}}Client /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index bf344f74..95efcd7d 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -14,13 +14,13 @@ {{/each}} {{/if}} - {{#unless returnsVoid}}$res = {{/unless}}$this->client->request( + {{#unless returnsVoid}}$res = Body::decode({{/unless}}$this->client->request( "POST", "{{path}}", {{#if hasParams}} - json: (object) $request_payload, + ["json" => (object) $request_payload], {{/if}} - ); + ){{#unless returnsVoid}}){{/unless}}; {{#if usesActionAttempt}} return ResolveActionAttempt::resolve_action_attempt( diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index c452d625..c07c5d63 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -6,8 +6,9 @@ namespace Seam; use {{this}}; {{/each}} +use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; -use Seam\Http\SeamHttpClient; +use Seam\Http\ClientFactory; /** * Client for the Seam API. @@ -27,9 +28,12 @@ class Seam /** * The long term support version of the Seam API this SDK targets. */ - public const LTS_VERSION = SeamHttpClient::LTS_VERSION; + public const LTS_VERSION = ClientFactory::LTS_VERSION; - public SeamHttpClient $client; + /** + * The Guzzle client this instance makes its requests with. + */ + public ClientInterface $client; /** * Default request options applied to every call, currently just @@ -41,9 +45,10 @@ class Seam /** * @param bool|array{timeout?: float, polling_interval?: float}|null $wait_for_action_attempt Whether to wait for action attempts to finish, optionally with timeout and polling_interval in seconds. Defaults to true. - * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. timeout or headers. + * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. headers or proxy. * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. - * @param ClientInterface|null $client A preconfigured Guzzle client. It carries its own endpoint and authorization, so no other authentication option may be given with it. + * @param float|null $timeout Request timeout in seconds, covering connecting and reading. Defaults to 30; pass 0 to disable. + * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so no other authentication option applies to it. */ public function __construct( ?string $api_key = null, @@ -53,6 +58,7 @@ class Seam bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ?ClientInterface $client = null ) { $this->defaults = [ @@ -61,14 +67,13 @@ class Seam // A client carries its own endpoint and authorization, so the // authentication options are only read when one has to be built. - $this->client = $client !== null - ? SeamHttpClient::from_client($client) - : new SeamHttpClient( - Options::get_endpoint($endpoint), - Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), - $guzzle_options, - $retries - ); + $this->client = $client ?? ClientFactory::create( + Options::get_endpoint($endpoint), + Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), + $guzzle_options, + $retries, + $timeout + ); {{#each parentClients}} $this->{{namespace}} = new {{clientName}}Client($this->client, $this->defaults); @@ -83,14 +88,16 @@ class Seam ?string $endpoint = null, bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], - ?int $retries = null + ?int $retries = null, + ?float $timeout = null ): static { return new static( api_key: $api_key, endpoint: $endpoint, wait_for_action_attempt: $wait_for_action_attempt, guzzle_options: $guzzle_options, - retries: $retries + retries: $retries, + timeout: $timeout ); } @@ -104,7 +111,8 @@ class Seam ?string $endpoint = null, bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], - ?int $retries = null + ?int $retries = null, + ?float $timeout = null ): static { return new static( personal_access_token: $personal_access_token, @@ -112,6 +120,7 @@ class Seam endpoint: $endpoint, wait_for_action_attempt: $wait_for_action_attempt, retries: $retries, + timeout: $timeout, guzzle_options: $guzzle_options ); } @@ -134,21 +143,6 @@ class Seam return self::LTS_VERSION; } - /** - * Makes a request against the Seam API with this client's authorization. - * - * @param array|object|null $json - * @param array|null $query - */ - public function request( - string $method, - string $path, - mixed $json = null, - ?array $query = null - ): mixed { - return $this->client->request($method, $path, $json, $query); - } - /** * Creates a paginator for a list endpoint. * diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 0cc1ac3e..8dc8c110 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -8,7 +8,8 @@ import { sortPhpClientMethodParameters, } from '../class-model.js' -const seamHttpClientClass = 'Seam\\Http\\SeamHttpClient' +const clientInterfaceClass = 'GuzzleHttp\\ClientInterface' +const bodyClass = 'Seam\\Http\\Body' const resolveActionAttemptClass = 'Seam\\Http\\ResolveActionAttempt' const resourcesNamespace = 'Seam\\Resources' @@ -147,8 +148,12 @@ const getUseStatements = (client: PhpClient): string[] => { const usesActionAttempt = resourceNames.has('ActionAttempt') + // Void endpoints never read the response, so they do not decode it. + const readsBody = client.methods.some((m) => m.returnResource !== '') + return [ - seamHttpClientClass, + clientInterfaceClass, + ...(readsBody ? [bodyClass] : []), ...(usesActionAttempt ? [resolveActionAttemptClass] : []), ...[...resourceNames].map((name) => `${resourcesNamespace}\\${name}`), ].sort((a, b) => a.localeCompare(b)) diff --git a/src/ActionAttemptFailedError.php b/src/ActionAttemptFailedError.php index af5c5d16..a69311f5 100644 --- a/src/ActionAttemptFailedError.php +++ b/src/ActionAttemptFailedError.php @@ -24,8 +24,7 @@ public function __construct(ActionAttempt $actionAttempt) * The action attempt error type. * * Named `getErrorCode` rather than `getCode` because `Exception::getCode` - * is final and returns an int. This is the equivalent of the `code` - * property on the same error in the other Seam SDKs. + * is final and returns an int. */ public function getErrorCode(): string { diff --git a/src/Http/Body.php b/src/Http/Body.php new file mode 100644 index 00000000..a63321a7 --- /dev/null +++ b/src/Http/Body.php @@ -0,0 +1,36 @@ +getBody(); + $body->rewind(); + $contents = $body->getContents(); + + if ($contents === "") { + return null; + } + + try { + return Utils::jsonDecode($contents); + } catch (\InvalidArgumentException) { + return null; + } + } +} diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php new file mode 100644 index 00000000..b51f3136 --- /dev/null +++ b/src/Http/ClientFactory.php @@ -0,0 +1,147 @@ + $auth_headers + * @param array $guzzle_options + */ + public static function create( + string $endpoint, + array $auth_headers, + array $guzzle_options = [], + ?int $retries = null, + ?float $timeout = null, + ): Client { + $retries ??= self::DEFAULT_RETRIES; + $timeout ??= self::DEFAULT_TIMEOUT; + + // Middleware has to be on the handler stack the client is built with: + // a stack pushed onto after construction does not apply. + $handler = $guzzle_options["handler"] ?? HandlerStack::create(); + + if ($handler instanceof HandlerStack) { + // Pushed first, so it wraps the retry middleware and only sees the + // response a request finally settled on. + $handler->push(ErrorMiddleware::create(), "seam_error"); + $handler->push( + self::retry_options_middleware(), + "seam_retry_options", + ); + $handler->push( + GuzzleRetryMiddleware::factory([ + "retry_enabled" => $retries > 0, + "max_retry_attempts" => $retries, + "retry_on_timeout" => true, + // Set per request by the middleware above. + "retry_on_status" => [], + ]), + "seam_retry", + ); + } + + $headers = array_merge( + $auth_headers, + $guzzle_options["headers"] ?? [], + self::sdk_headers(), + ); + + return new Client( + array_merge( + [ + "base_uri" => $endpoint, + "timeout" => $timeout, + "connect_timeout" => $timeout, + ], + $guzzle_options, + [ + "handler" => $handler, + "headers" => $headers, + // ErrorMiddleware raises instead, so that a Seam error + // response becomes a Seam exception. + "http_errors" => false, + ], + ), + ); + } + + /** + * Opts a request into status based retries when repeating it is safe. + */ + private static function retry_options_middleware(): callable + { + return static fn(callable $handler): callable => static function ( + RequestInterface $request, + array $options, + ) use ($handler): PromiseInterface { + if ( + in_array( + strtoupper($request->getMethod()), + self::IDEMPOTENT_METHODS, + true, + ) + ) { + $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; + } + + return $handler($request, $options); + }; + } + + /** + * @return array + */ + private static function sdk_headers(): array + { + $version = PackageVersion::get(); + + return [ + "User-Agent" => "seam-php/" . $version, + "seam-sdk-name" => "seamapi/php", + "seam-sdk-version" => $version, + "seam-lts-version" => self::LTS_VERSION, + ]; + } +} diff --git a/src/Http/ErrorMiddleware.php b/src/Http/ErrorMiddleware.php new file mode 100644 index 00000000..a709af15 --- /dev/null +++ b/src/Http/ErrorMiddleware.php @@ -0,0 +1,109 @@ + static fn( + RequestInterface $request, + array $options, + ): PromiseInterface => $handler($request, $options)->then( + static function (ResponseInterface $response) use ( + $request, + ): ResponseInterface { + $status_code = $response->getStatusCode(); + + if ($status_code >= 200 && $status_code < 300) { + return $response; + } + + throw self::to_exception($request, $response); + }, + ); + } + + private static function to_exception( + RequestInterface $request, + ResponseInterface $response, + ): \Throwable { + $status_code = $response->getStatusCode(); + $request_id = self::get_request_id($response); + + if ($status_code === 401) { + return new HttpUnauthorizedError($request_id); + } + + $error = self::get_error($response); + + if ($error === null) { + return BadResponseException::create($request, $response); + } + + if (($error->type ?? null) === "invalid_input") { + return new HttpInvalidInputError($error, $status_code, $request_id); + } + + return new HttpApiError($error, $status_code, $request_id); + } + + /** + * The error from a Seam error envelope, i.e. JSON holding an `error` + * object with a string `type` and `message`, or null when the response is + * not one. + */ + private static function get_error(ResponseInterface $response): ?object + { + if ( + !str_starts_with( + $response->getHeaderLine("content-type"), + "application/json", + ) + ) { + return null; + } + + $body = Body::decode($response); + + if (!is_object($body)) { + return null; + } + + $error = $body->error ?? null; + + if (!is_object($error)) { + return null; + } + + return is_string($error->type ?? null) && + is_string($error->message ?? null) + ? $error + : null; + } + + private static function get_request_id(ResponseInterface $response): ?string + { + return $response->hasHeader("seam-request-id") + ? $response->getHeaderLine("seam-request-id") + : null; + } +} diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index 29ba92b9..b0bb60fd 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -2,6 +2,7 @@ namespace Seam\Http; +use GuzzleHttp\ClientInterface; use Seam\ActionAttemptFailedError; use Seam\ActionAttemptTimeoutError; use Seam\Resources\ActionAttempt; @@ -9,9 +10,8 @@ /** * Waits for an action attempt to reach a terminal state. * - * Mirrors resolveActionAttempt in the JavaScript SDK: a successful attempt is - * returned as is, a failed one raises, and a pending one is polled until it - * finishes or the timeout elapses. + * A successful attempt is returned as is, a failed one raises, and a pending + * one is polled until it finishes or the timeout elapses. */ final class ResolveActionAttempt { @@ -24,7 +24,7 @@ final class ResolveActionAttempt */ public static function resolve_action_attempt( ActionAttempt $action_attempt, - SeamHttpClient $client, + ClientInterface $client, bool|array|null $wait_for_action_attempt, ): ActionAttempt { if ($wait_for_action_attempt === false) { @@ -45,7 +45,7 @@ public static function resolve_action_attempt( private static function poll( ActionAttempt $action_attempt, - SeamHttpClient $client, + ClientInterface $client, float $timeout, float $polling_interval, ): ActionAttempt { @@ -74,13 +74,13 @@ private static function poll( } private static function get_action_attempt( - SeamHttpClient $client, + ClientInterface $client, string $action_attempt_id, ): ActionAttempt { - $res = $client->request( - "POST", - "/action_attempts/get", - json: (object) ["action_attempt_id" => $action_attempt_id], + $res = Body::decode( + $client->request("POST", "/action_attempts/get", [ + "json" => (object) ["action_attempt_id" => $action_attempt_id], + ]), ); $action_attempt = ActionAttempt::from_json( diff --git a/src/Http/SeamHttpClient.php b/src/Http/SeamHttpClient.php deleted file mode 100644 index 2ad04030..00000000 --- a/src/Http/SeamHttpClient.php +++ /dev/null @@ -1,287 +0,0 @@ - $auth_headers - * @param array $guzzle_options - */ - public function __construct( - string $endpoint, - array $auth_headers, - array $guzzle_options = [], - ?int $retries = null, - ?ClientInterface $client = null, - ) { - $this->client = - $client ?? - new GuzzleClient( - self::create_config( - $endpoint, - $auth_headers, - $guzzle_options, - $retries ?? self::DEFAULT_RETRIES, - ), - ); - } - - /** - * Wraps an already configured Guzzle client. The client carries its own - * endpoint and authorization, so none are resolved here. - */ - public static function from_client(ClientInterface $client): self - { - return new self("", [], [], null, $client); - } - - public function get_client(): ClientInterface - { - return $this->client; - } - - /** - * @param array $auth_headers - * @param array $guzzle_options - * @return array - */ - private static function create_config( - string $endpoint, - array $auth_headers, - array $guzzle_options, - int $retries, - ): array { - // The middleware has to be on the handler stack the client is built - // with: a stack pushed onto after construction does not apply. - $handler = $guzzle_options["handler"] ?? HandlerStack::create(); - - if ($handler instanceof HandlerStack) { - $handler->push( - GuzzleRetryMiddleware::factory([ - "retry_enabled" => $retries > 0, - "max_retry_attempts" => $retries, - // A connection failure never reached the server, so it is - // safe to retry whatever the method. Status codes are - // opted into per request, see request(). - "retry_on_timeout" => true, - "retry_on_status" => [], - ]), - "seam_retry", - ); - } - - $headers = array_merge( - $auth_headers, - $guzzle_options["headers"] ?? [], - self::sdk_headers(), - ); - - return array_merge( - [ - "base_uri" => $endpoint, - "timeout" => self::DEFAULT_TIMEOUT, - ], - $guzzle_options, - [ - "handler" => $handler, - "headers" => $headers, - // Error mapping happens in request(); letting Guzzle throw - // first would bypass it entirely. - "http_errors" => false, - ], - ); - } - - /** - * @return array - */ - private static function sdk_headers(): array - { - $version = PackageVersion::get(); - - return [ - "User-Agent" => "seam-php/" . $version, - "seam-sdk-name" => "seamapi/php", - "seam-sdk-version" => $version, - "seam-lts-version" => self::LTS_VERSION, - ]; - } - - /** - * @param array|object|null $json - * @param array|null $query - */ - public function request( - string $method, - string $path, - mixed $json = null, - ?array $query = null, - ): mixed { - $options = array_filter( - [ - "json" => $json, - "query" => $query, - ], - fn($option) => $option !== null, - ); - - if (in_array(strtoupper($method), self::IDEMPOTENT_METHODS, true)) { - $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; - } - - $response = $this->client->request($method, $path, $options); - $status_code = $response->getStatusCode(); - - if ($status_code < 200 || $status_code >= 300) { - $this->handle_error_response( - new Request($method, $path), - $response, - ); - } - - return self::decode_body($response); - } - - private function handle_error_response( - RequestInterface $request, - ResponseInterface $response, - ): void { - $status_code = $response->getStatusCode(); - $request_id = self::get_request_id($response); - - if ($status_code === 401) { - throw new HttpUnauthorizedError($request_id); - } - - if (!self::is_api_error_response($response)) { - // Not a Seam error response, so there is nothing to map onto a - // Seam exception. The other Seam SDKs surface the underlying - // transport error here too. - throw BadResponseException::create($request, $response); - } - - $error = self::decode_body($response)->error; - - if (($error->type ?? null) === "invalid_input") { - throw new HttpInvalidInputError($error, $status_code, $request_id); - } - - throw new HttpApiError($error, $status_code, $request_id); - } - - /** - * True when the response body is a Seam error envelope, i.e. JSON holding - * an `error` object with a string `type` and `message`. - */ - private static function is_api_error_response( - ResponseInterface $response, - ): bool { - if ( - !str_starts_with( - $response->getHeaderLine("content-type"), - "application/json", - ) - ) { - return false; - } - - $body = self::decode_body($response); - - if (!is_object($body)) { - return false; - } - - $error = $body->error ?? null; - - if (!is_object($error)) { - return false; - } - - return is_string($error->type ?? null) && - is_string($error->message ?? null); - } - - private static function decode_body(ResponseInterface $response): mixed - { - $body = $response->getBody(); - $body->rewind(); - $contents = $body->getContents(); - - if ($contents === "") { - return null; - } - - try { - return Utils::jsonDecode($contents); - } catch (\InvalidArgumentException) { - return null; - } - } - - private static function get_request_id(ResponseInterface $response): ?string - { - return $response->hasHeader("seam-request-id") - ? $response->getHeaderLine("seam-request-id") - : null; - } -} diff --git a/src/Options.php b/src/Options.php index 7fc4dcc1..e3d22aa3 100644 --- a/src/Options.php +++ b/src/Options.php @@ -4,7 +4,7 @@ /** * Resolves the API endpoint and validates mutually exclusive authentication - * options, mirroring the option handling in the other Seam SDKs. + * options. */ final class Options { diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index a3465a8d..3c446642 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AccessCode; class AccessCodesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class AccessCodesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -134,10 +135,10 @@ public function create( ] = $use_offline_access_code; } - $res = $this->client->request( - "POST", - "/access_codes/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/create", [ + "json" => (object) $request_payload, + ]), ); return AccessCode::from_json($res->access_code); @@ -239,10 +240,10 @@ public function create_multiple( ] = $use_backup_access_code_pool; } - $res = $this->client->request( - "POST", - "/access_codes/create_multiple", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/create_multiple", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -269,11 +270,9 @@ public function delete( $request_payload["device_id"] = $device_id; } - $this->client->request( - "POST", - "/access_codes/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -288,10 +287,10 @@ public function generate_code(string $device_id): AccessCode $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/access_codes/generate_code", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/generate_code", [ + "json" => (object) $request_payload, + ]), ); return AccessCode::from_json($res->generated_code); @@ -324,10 +323,10 @@ public function get( $request_payload["device_id"] = $device_id; } - $res = $this->client->request( - "POST", - "/access_codes/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/get", [ + "json" => (object) $request_payload, + ]), ); return AccessCode::from_json($res->access_code); @@ -397,10 +396,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/access_codes/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -433,10 +432,12 @@ public function pull_backup_access_code(string $access_code_id): AccessCode $request_payload["access_code_id"] = $access_code_id; - $res = $this->client->request( - "POST", - "/access_codes/pull_backup_access_code", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/access_codes/pull_backup_access_code", + ["json" => (object) $request_payload], + ), ); return AccessCode::from_json($res->access_code); @@ -477,7 +478,7 @@ public function report_device_constraints( $this->client->request( "POST", "/access_codes/report_device_constraints", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -601,11 +602,9 @@ public function update( ] = $use_offline_access_code; } - $this->client->request( - "POST", - "/access_codes/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/update", [ + "json" => (object) $request_payload, + ]); } /** @@ -646,10 +645,8 @@ public function update_multiple( $request_payload["starts_at"] = $starts_at; } - $this->client->request( - "POST", - "/access_codes/update_multiple", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/update_multiple", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index 0750e822..a2897660 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedAccessCode; class AccessCodesSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AccessCodesSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -42,10 +43,12 @@ public function create_unmanaged_access_code( $request_payload["device_id"] = $device_id; $request_payload["name"] = $name; - $res = $this->client->request( - "POST", - "/access_codes/simulate/create_unmanaged_access_code", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/access_codes/simulate/create_unmanaged_access_code", + ["json" => (object) $request_payload], + ), ); return UnmanagedAccessCode::from_json($res->access_code); diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index e18b0ab3..75a59b15 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedAccessCode; class AccessCodesUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AccessCodesUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -62,7 +63,7 @@ public function convert_to_managed( $this->client->request( "POST", "/access_codes/unmanaged/convert_to_managed", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -78,11 +79,9 @@ public function delete(string $access_code_id): void $request_payload["access_code_id"] = $access_code_id; - $this->client->request( - "POST", - "/access_codes/unmanaged/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/unmanaged/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -112,10 +111,10 @@ public function get( $request_payload["device_id"] = $device_id; } - $res = $this->client->request( - "POST", - "/access_codes/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedAccessCode::from_json($res->access_code); @@ -156,10 +155,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/access_codes/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -207,10 +206,8 @@ public function update( ] = $is_external_modification_allowed; } - $this->client->request( - "POST", - "/access_codes/unmanaged/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/unmanaged/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 188412e5..e6a8a8b8 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AccessGrant; use Seam\Resources\Batch; class AccessGrantsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class AccessGrantsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -112,10 +113,10 @@ public function create( $request_payload["starts_at"] = $starts_at; } - $res = $this->client->request( - "POST", - "/access_grants/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/create", [ + "json" => (object) $request_payload, + ]), ); return AccessGrant::from_json($res->access_grant); @@ -133,11 +134,9 @@ public function delete(string $access_grant_id): void $request_payload["access_grant_id"] = $access_grant_id; - $this->client->request( - "POST", - "/access_grants/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_grants/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -160,10 +159,10 @@ public function get( $request_payload["access_grant_key"] = $access_grant_key; } - $res = $this->client->request( - "POST", - "/access_grants/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/get", [ + "json" => (object) $request_payload, + ]), ); return AccessGrant::from_json($res->access_grant); @@ -199,10 +198,10 @@ public function get_related( $request_payload["include"] = $include; } - $res = $this->client->request( - "POST", - "/access_grants/get_related", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/get_related", [ + "json" => (object) $request_payload, + ]), ); return Batch::from_json($res->batch); @@ -285,10 +284,10 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/access_grants/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -319,10 +318,12 @@ public function request_access_methods( "requested_access_methods" ] = $requested_access_methods; - $res = $this->client->request( - "POST", - "/access_grants/request_access_methods", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/access_grants/request_access_methods", + ["json" => (object) $request_payload], + ), ); return AccessGrant::from_json($res->access_grant); @@ -363,10 +364,8 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->client->request( - "POST", - "/access_grants/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_grants/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 72955f56..dfe5df2a 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedAccessGrant; class AccessGrantsUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AccessGrantsUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,10 +36,10 @@ public function get(string $access_grant_id): UnmanagedAccessGrant $request_payload["access_grant_id"] = $access_grant_id; - $res = $this->client->request( - "POST", - "/access_grants/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedAccessGrant::from_json($res->access_grant); @@ -86,10 +87,10 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/access_grants/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -127,10 +128,8 @@ public function update( $request_payload["access_grant_key"] = $access_grant_key; } - $this->client->request( - "POST", - "/access_grants/unmanaged/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_grants/unmanaged/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 09421e8e..a038f4f9 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -2,15 +2,16 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\AccessMethod; use Seam\Resources\ActionAttempt; use Seam\Resources\Batch; class AccessMethodsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -20,7 +21,7 @@ class AccessMethodsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -45,10 +46,10 @@ public function assign_card( $request_payload["access_method_id"] = $access_method_id; $request_payload["card_number"] = $card_number; - $res = $this->client->request( - "POST", - "/access_methods/assign_card", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/assign_card", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -84,11 +85,9 @@ public function delete( $request_payload["reservation_key"] = $reservation_key; } - $this->client->request( - "POST", - "/access_methods/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_methods/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -109,10 +108,10 @@ public function encode( $request_payload["access_method_id"] = $access_method_id; $request_payload["acs_encoder_id"] = $acs_encoder_id; - $res = $this->client->request( - "POST", - "/access_methods/encode", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/encode", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -135,10 +134,10 @@ public function get(string $access_method_id): AccessMethod $request_payload["access_method_id"] = $access_method_id; - $res = $this->client->request( - "POST", - "/access_methods/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/get", [ + "json" => (object) $request_payload, + ]), ); return AccessMethod::from_json($res->access_method); @@ -167,10 +166,10 @@ public function get_related( $request_payload["include"] = $include; } - $res = $this->client->request( - "POST", - "/access_methods/get_related", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/get_related", [ + "json" => (object) $request_payload, + ]), ); return Batch::from_json($res->batch); @@ -228,10 +227,10 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->client->request( - "POST", - "/access_methods/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -262,10 +261,10 @@ public function unlock_door( $request_payload["access_method_id"] = $access_method_id; $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->client->request( - "POST", - "/access_methods/unlock_door", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/unlock_door", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 4599a2b4..0aaa359c 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedAccessMethod; class AccessMethodsUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AccessMethodsUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,10 +36,10 @@ public function get(string $access_method_id): UnmanagedAccessMethod $request_payload["access_method_id"] = $access_method_id; - $res = $this->client->request( - "POST", - "/access_methods/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedAccessMethod::from_json($res->access_method); @@ -72,10 +73,10 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->client->request( - "POST", - "/access_methods/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 8fea3af4..109f7141 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsAccessGroup; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; class AcsAccessGroupsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class AcsAccessGroupsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -48,11 +49,9 @@ public function add_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/access_groups/add_user", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/access_groups/add_user", [ + "json" => (object) $request_payload, + ]); } /** @@ -67,11 +66,9 @@ public function delete(string $acs_access_group_id): void $request_payload["acs_access_group_id"] = $acs_access_group_id; - $this->client->request( - "POST", - "/acs/access_groups/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/access_groups/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -86,10 +83,10 @@ public function get(string $acs_access_group_id): AcsAccessGroup $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->client->request( - "POST", - "/acs/access_groups/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/access_groups/get", [ + "json" => (object) $request_payload, + ]), ); return AcsAccessGroup::from_json($res->acs_access_group); @@ -125,10 +122,10 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/access_groups/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/access_groups/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -150,10 +147,12 @@ public function list_accessible_entrances( $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->client->request( - "POST", - "/acs/access_groups/list_accessible_entrances", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/access_groups/list_accessible_entrances", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -174,10 +173,10 @@ public function list_users(string $acs_access_group_id): array $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->client->request( - "POST", - "/acs/access_groups/list_users", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/access_groups/list_users", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => AcsUser::from_json($r), $res->acs_users); @@ -206,10 +205,8 @@ public function remove_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/access_groups/remove_user", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/access_groups/remove_user", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AcsClient.php b/src/Routes/AcsClient.php index 1d5f11e0..bee3e615 100644 --- a/src/Routes/AcsClient.php +++ b/src/Routes/AcsClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class AcsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -21,7 +21,7 @@ class AcsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 43b41e38..4dc13187 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; class AcsCredentialsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class AcsCredentialsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -47,11 +48,9 @@ public function assign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/credentials/assign", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/credentials/assign", [ + "json" => (object) $request_payload, + ]); } /** @@ -135,10 +134,10 @@ public function create( $request_payload["visionline_metadata"] = $visionline_metadata; } - $res = $this->client->request( - "POST", - "/acs/credentials/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/credentials/create", [ + "json" => (object) $request_payload, + ]), ); return AcsCredential::from_json($res->acs_credential); @@ -156,11 +155,9 @@ public function delete(string $acs_credential_id): void $request_payload["acs_credential_id"] = $acs_credential_id; - $this->client->request( - "POST", - "/acs/credentials/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/credentials/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -175,10 +172,10 @@ public function get(string $acs_credential_id): AcsCredential $request_payload["acs_credential_id"] = $acs_credential_id; - $res = $this->client->request( - "POST", - "/acs/credentials/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/credentials/get", [ + "json" => (object) $request_payload, + ]), ); return AcsCredential::from_json($res->acs_credential); @@ -238,10 +235,10 @@ public function list( $request_payload["search"] = $search; } - $res = $this->client->request( - "POST", - "/acs/credentials/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/credentials/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -266,10 +263,12 @@ public function list_accessible_entrances(string $acs_credential_id): array $request_payload["acs_credential_id"] = $acs_credential_id; - $res = $this->client->request( - "POST", - "/acs/credentials/list_accessible_entrances", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/credentials/list_accessible_entrances", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -301,11 +300,9 @@ public function unassign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/credentials/unassign", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/credentials/unassign", [ + "json" => (object) $request_payload, + ]); } /** @@ -331,10 +328,8 @@ public function update( $request_payload["ends_at"] = $ends_at; } - $this->client->request( - "POST", - "/acs/credentials/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/credentials/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index dd12962a..6383100f 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\AcsEncoder; use Seam\Resources\ActionAttempt; class AcsEncodersClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class AcsEncodersClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -51,10 +52,10 @@ public function encode_credential( $request_payload["acs_credential_id"] = $acs_credential_id; } - $res = $this->client->request( - "POST", - "/acs/encoders/encode_credential", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/encoders/encode_credential", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -77,10 +78,10 @@ public function get(string $acs_encoder_id): AcsEncoder $request_payload["acs_encoder_id"] = $acs_encoder_id; - $res = $this->client->request( - "POST", - "/acs/encoders/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/encoders/get", [ + "json" => (object) $request_payload, + ]), ); return AcsEncoder::from_json($res->acs_encoder); @@ -123,10 +124,10 @@ public function list( $request_payload["page_cursor"] = $page_cursor; } - $res = $this->client->request( - "POST", - "/acs/encoders/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/encoders/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -159,10 +160,10 @@ public function scan_credential( $request_payload["salto_ks_metadata"] = $salto_ks_metadata; } - $res = $this->client->request( - "POST", - "/acs/encoders/scan_credential", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/encoders/scan_credential", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -203,10 +204,12 @@ public function scan_to_assign_credential( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/encoders/scan_to_assign_credential", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/encoders/scan_to_assign_credential", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/AcsEncodersSimulateClient.php b/src/Routes/AcsEncodersSimulateClient.php index 54ce9aaf..f1616fea 100644 --- a/src/Routes/AcsEncodersSimulateClient.php +++ b/src/Routes/AcsEncodersSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class AcsEncodersSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class AcsEncodersSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -48,7 +48,7 @@ public function next_credential_encode_will_fail( $this->client->request( "POST", "/acs/encoders/simulate/next_credential_encode_will_fail", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -73,7 +73,7 @@ public function next_credential_encode_will_succeed( $this->client->request( "POST", "/acs/encoders/simulate/next_credential_encode_will_succeed", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -105,7 +105,7 @@ public function next_credential_scan_will_fail( $this->client->request( "POST", "/acs/encoders/simulate/next_credential_scan_will_fail", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -137,7 +137,7 @@ public function next_credential_scan_will_succeed( $this->client->request( "POST", "/acs/encoders/simulate/next_credential_scan_will_succeed", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 270cfb24..0c6a65e5 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -2,15 +2,16 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; use Seam\Resources\ActionAttempt; class AcsEntrancesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -20,7 +21,7 @@ class AcsEntrancesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -38,10 +39,10 @@ public function get(string $acs_entrance_id): AcsEntrance $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->client->request( - "POST", - "/acs/entrances/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/entrances/get", [ + "json" => (object) $request_payload, + ]), ); return AcsEntrance::from_json($res->acs_entrance); @@ -70,11 +71,9 @@ public function grant_access( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/entrances/grant_access", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/entrances/grant_access", [ + "json" => (object) $request_payload, + ]); } /** @@ -144,10 +143,10 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->client->request( - "POST", - "/acs/entrances/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/entrances/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -178,10 +177,12 @@ public function list_credentials_with_access( $request_payload["include_if"] = $include_if; } - $res = $this->client->request( - "POST", - "/acs/entrances/list_credentials_with_access", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/entrances/list_credentials_with_access", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -208,10 +209,10 @@ public function unlock( $request_payload["acs_credential_id"] = $acs_credential_id; $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->client->request( - "POST", - "/acs/entrances/unlock", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/entrances/unlock", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index cc519bf5..18863753 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsSystem; class AcsSystemsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AcsSystemsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,10 +36,10 @@ public function get(string $acs_system_id): AcsSystem $request_payload["acs_system_id"] = $acs_system_id; - $res = $this->client->request( - "POST", - "/acs/systems/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/systems/get", [ + "json" => (object) $request_payload, + ]), ); return AcsSystem::from_json($res->acs_system); @@ -71,10 +72,10 @@ public function list( $request_payload["search"] = $search; } - $res = $this->client->request( - "POST", - "/acs/systems/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/systems/list", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); @@ -95,10 +96,12 @@ public function list_compatible_credential_manager_acs_systems( $request_payload["acs_system_id"] = $acs_system_id; - $res = $this->client->request( - "POST", - "/acs/systems/list_compatible_credential_manager_acs_systems", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/systems/list_compatible_credential_manager_acs_systems", + ["json" => (object) $request_payload], + ), ); return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); @@ -127,10 +130,8 @@ public function report_devices( $request_payload["acs_entrances"] = $acs_entrances; } - $this->client->request( - "POST", - "/acs/systems/report_devices", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/systems/report_devices", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 422a0502..65f72306 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; class AcsUsersClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class AcsUsersClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -40,11 +41,9 @@ public function add_to_access_group( $request_payload["acs_access_group_id"] = $acs_access_group_id; $request_payload["acs_user_id"] = $acs_user_id; - $this->client->request( - "POST", - "/acs/users/add_to_access_group", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/add_to_access_group", [ + "json" => (object) $request_payload, + ]); } /** @@ -93,10 +92,10 @@ public function create( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/users/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/users/create", [ + "json" => (object) $request_payload, + ]), ); return AcsUser::from_json($res->acs_user); @@ -127,11 +126,9 @@ public function delete( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -159,10 +156,10 @@ public function get( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/users/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/users/get", [ + "json" => (object) $request_payload, + ]), ); return AcsUser::from_json($res->acs_user); @@ -224,10 +221,10 @@ public function list( ] = $user_identity_phone_number; } - $res = $this->client->request( - "POST", - "/acs/users/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/users/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -262,10 +259,12 @@ public function list_accessible_entrances( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/users/list_accessible_entrances", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/users/list_accessible_entrances", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -297,11 +296,9 @@ public function remove_from_access_group( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/remove_from_access_group", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/remove_from_access_group", [ + "json" => (object) $request_payload, + ]); } /** @@ -332,7 +329,7 @@ public function revoke_access_to_all_entrances( $this->client->request( "POST", "/acs/users/revoke_access_to_all_entrances", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -361,11 +358,9 @@ public function suspend( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/suspend", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/suspend", [ + "json" => (object) $request_payload, + ]); } /** @@ -393,11 +388,9 @@ public function unsuspend( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/unsuspend", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/unsuspend", [ + "json" => (object) $request_payload, + ]); } /** @@ -455,10 +448,8 @@ public function update( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 971a286e..3f03a968 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; class ActionAttemptsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class ActionAttemptsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -39,10 +40,10 @@ public function get( $request_payload["action_attempt_id"] = $action_attempt_id; - $res = $this->client->request( - "POST", - "/action_attempts/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/action_attempts/get", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -87,10 +88,10 @@ public function list( $request_payload["page_cursor"] = $page_cursor; } - $res = $this->client->request( - "POST", - "/action_attempts/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/action_attempts/list", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 93b81d8c..99d86e76 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\ClientSession; class ClientSessionsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class ClientSessionsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -73,10 +74,10 @@ public function create( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->client->request( - "POST", - "/client_sessions/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/client_sessions/create", [ + "json" => (object) $request_payload, + ]), ); return ClientSession::from_json($res->client_session); @@ -94,11 +95,9 @@ public function delete(string $client_session_id): void $request_payload["client_session_id"] = $client_session_id; - $this->client->request( - "POST", - "/client_sessions/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/client_sessions/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -121,10 +120,10 @@ public function get( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/client_sessions/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/client_sessions/get", [ + "json" => (object) $request_payload, + ]), ); return ClientSession::from_json($res->client_session); @@ -170,10 +169,10 @@ public function get_or_create( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->client->request( - "POST", - "/client_sessions/get_or_create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/client_sessions/get_or_create", [ + "json" => (object) $request_payload, + ]), ); return ClientSession::from_json($res->client_session); @@ -219,11 +218,9 @@ public function grant_access( $request_payload["user_identity_ids"] = $user_identity_ids; } - $this->client->request( - "POST", - "/client_sessions/grant_access", - json: (object) $request_payload, - ); + $this->client->request("POST", "/client_sessions/grant_access", [ + "json" => (object) $request_payload, + ]); } /** @@ -263,10 +260,10 @@ public function list( ] = $without_user_identifier_key; } - $res = $this->client->request( - "POST", - "/client_sessions/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/client_sessions/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -289,10 +286,8 @@ public function revoke(string $client_session_id): void $request_payload["client_session_id"] = $client_session_id; - $this->client->request( - "POST", - "/client_sessions/revoke", - json: (object) $request_payload, - ); + $this->client->request("POST", "/client_sessions/revoke", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 760d8edb..22549171 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\ConnectWebview; class ConnectWebviewsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class ConnectWebviewsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -95,10 +96,10 @@ public function create( ] = $wait_for_device_creation; } - $res = $this->client->request( - "POST", - "/connect_webviews/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connect_webviews/create", [ + "json" => (object) $request_payload, + ]), ); return ConnectWebview::from_json($res->connect_webview); @@ -118,11 +119,9 @@ public function delete(string $connect_webview_id): void $request_payload["connect_webview_id"] = $connect_webview_id; - $this->client->request( - "POST", - "/connect_webviews/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/connect_webviews/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -139,10 +138,10 @@ public function get(string $connect_webview_id): ConnectWebview $request_payload["connect_webview_id"] = $connect_webview_id; - $res = $this->client->request( - "POST", - "/connect_webviews/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connect_webviews/get", [ + "json" => (object) $request_payload, + ]), ); return ConnectWebview::from_json($res->connect_webview); @@ -190,10 +189,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/connect_webviews/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connect_webviews/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 9725597a..14b2b6f0 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\ConnectedAccount; class ConnectedAccountsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class ConnectedAccountsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -43,11 +44,9 @@ public function delete(string $connected_account_id): void $request_payload["connected_account_id"] = $connected_account_id; - $this->client->request( - "POST", - "/connected_accounts/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/connected_accounts/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -70,10 +69,10 @@ public function get( $request_payload["email"] = $email; } - $res = $this->client->request( - "POST", - "/connected_accounts/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connected_accounts/get", [ + "json" => (object) $request_payload, + ]), ); return ConnectedAccount::from_json($res->connected_account); @@ -126,10 +125,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/connected_accounts/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connected_accounts/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -154,11 +153,9 @@ public function sync(string $connected_account_id): void $request_payload["connected_account_id"] = $connected_account_id; - $this->client->request( - "POST", - "/connected_accounts/sync", - json: (object) $request_payload, - ); + $this->client->request("POST", "/connected_accounts/sync", [ + "json" => (object) $request_payload, + ]); } /** @@ -201,10 +198,8 @@ public function update( $request_payload["display_name"] = $display_name; } - $this->client->request( - "POST", - "/connected_accounts/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/connected_accounts/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/ConnectedAccountsSimulateClient.php b/src/Routes/ConnectedAccountsSimulateClient.php index 4eef7cb8..85e0612e 100644 --- a/src/Routes/ConnectedAccountsSimulateClient.php +++ b/src/Routes/ConnectedAccountsSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class ConnectedAccountsSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class ConnectedAccountsSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -37,7 +37,7 @@ public function disconnect(string $connected_account_id): void $this->client->request( "POST", "/connected_accounts/simulate/disconnect", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index 3fe55788..3f26137a 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\CustomerPortal; class CustomersClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class CustomersClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -92,10 +93,10 @@ public function create_portal( $request_payload["customer_data"] = $customer_data; } - $res = $this->client->request( - "POST", - "/customers/create_portal", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/customers/create_portal", [ + "json" => (object) $request_payload, + ]), ); return CustomerPortal::from_json($res->customer_portal); @@ -207,11 +208,9 @@ public function delete_data( $request_payload["user_keys"] = $user_keys; } - $this->client->request( - "POST", - "/customers/delete_data", - json: (object) $request_payload, - ); + $this->client->request("POST", "/customers/delete_data", [ + "json" => (object) $request_payload, + ]); } /** @@ -322,10 +321,8 @@ public function push_data( $request_payload["users"] = $users; } - $this->client->request( - "POST", - "/customers/push_data", - json: (object) $request_payload, - ); + $this->client->request("POST", "/customers/push_data", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 61ed5303..32be9949 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Device; use Seam\Resources\DeviceProvider; class DevicesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class DevicesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -47,10 +48,10 @@ public function get(?string $device_id = null, ?string $name = null): Device $request_payload["name"] = $name; } - $res = $this->client->request( - "POST", - "/devices/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/get", [ + "json" => (object) $request_payload, + ]), ); return Device::from_json($res->device); @@ -148,10 +149,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/devices/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -180,10 +181,10 @@ public function list_device_providers( $request_payload["provider_category"] = $provider_category; } - $res = $this->client->request( - "POST", - "/devices/list_device_providers", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/list_device_providers", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -204,11 +205,9 @@ public function report_provider_metadata(array $devices): void $request_payload["devices"] = $devices; - $this->client->request( - "POST", - "/devices/report_provider_metadata", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/report_provider_metadata", [ + "json" => (object) $request_payload, + ]); } /** @@ -253,10 +252,8 @@ public function update( $request_payload["properties"] = $properties; } - $this->client->request( - "POST", - "/devices/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/DevicesSimulateClient.php b/src/Routes/DevicesSimulateClient.php index 2a14d57d..b33bceac 100644 --- a/src/Routes/DevicesSimulateClient.php +++ b/src/Routes/DevicesSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class DevicesSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class DevicesSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -34,11 +34,9 @@ public function connect(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/devices/simulate/connect", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/connect", [ + "json" => (object) $request_payload, + ]); } /** @@ -56,11 +54,9 @@ public function connect_to_hub(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/devices/simulate/connect_to_hub", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/connect_to_hub", [ + "json" => (object) $request_payload, + ]); } /** @@ -75,11 +71,9 @@ public function disconnect(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/devices/simulate/disconnect", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/disconnect", [ + "json" => (object) $request_payload, + ]); } /** @@ -101,7 +95,7 @@ public function disconnect_from_hub(string $device_id): void $this->client->request( "POST", "/devices/simulate/disconnect_from_hub", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -121,11 +115,9 @@ public function paid_subscription(string $device_id, bool $is_expired): void $request_payload["device_id"] = $device_id; $request_payload["is_expired"] = $is_expired; - $this->client->request( - "POST", - "/devices/simulate/paid_subscription", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/paid_subscription", [ + "json" => (object) $request_payload, + ]); } /** @@ -140,10 +132,8 @@ public function remove(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/devices/simulate/remove", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/remove", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 541be58e..9b2007f4 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedDevice; class DevicesUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class DevicesUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -47,10 +48,10 @@ public function get( $request_payload["name"] = $name; } - $res = $this->client->request( - "POST", - "/devices/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedDevice::from_json($res->device); @@ -150,10 +151,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/devices/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -191,10 +192,8 @@ public function update( $request_payload["is_managed"] = $is_managed; } - $this->client->request( - "POST", - "/devices/unmanaged/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/unmanaged/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index f2f1f65c..0bf6a533 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Event; class EventsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class EventsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -48,10 +49,10 @@ public function get( $request_payload["event_type"] = $event_type; } - $res = $this->client->request( - "POST", - "/events/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/events/get", [ + "json" => (object) $request_payload, + ]), ); return Event::from_json($res->event); @@ -207,10 +208,10 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/events/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/events/list", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => Event::from_json($r), $res->events); diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index e91f4f30..895a9351 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\InstantKey; class InstantKeysClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class InstantKeysClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,11 +36,9 @@ public function delete(string $instant_key_id): void $request_payload["instant_key_id"] = $instant_key_id; - $this->client->request( - "POST", - "/instant_keys/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/instant_keys/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -62,10 +61,10 @@ public function get( $request_payload["instant_key_url"] = $instant_key_url; } - $res = $this->client->request( - "POST", - "/instant_keys/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/instant_keys/get", [ + "json" => (object) $request_payload, + ]), ); return InstantKey::from_json($res->instant_key); @@ -85,10 +84,10 @@ public function list(?string $user_identity_id = null): array $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/instant_keys/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/instant_keys/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index d54afadf..c4b2c7b4 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; class LocksClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class LocksClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -51,10 +52,10 @@ public function configure_auto_lock( ] = $auto_lock_delay_seconds; } - $res = $this->client->request( - "POST", - "/locks/configure_auto_lock", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/configure_auto_lock", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -84,10 +85,10 @@ public function get(?string $device_id = null, ?string $name = null): Device $request_payload["name"] = $name; } - $res = $this->client->request( - "POST", - "/locks/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/get", [ + "json" => (object) $request_payload, + ]), ); return Device::from_json($res->device); @@ -185,10 +186,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/locks/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -213,10 +214,10 @@ public function lock_door( $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/locks/lock_door", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/lock_door", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -242,10 +243,10 @@ public function unlock_door( $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/locks/unlock_door", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/unlock_door", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index ca91968c..70a5f08d 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; class LocksSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class LocksSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -42,10 +43,12 @@ public function keypad_code_entry( $request_payload["code"] = $code; $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/locks/simulate/keypad_code_entry", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/locks/simulate/keypad_code_entry", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( @@ -71,10 +74,12 @@ public function manual_lock_via_keypad( $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/locks/simulate/manual_lock_via_keypad", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/locks/simulate/manual_lock_via_keypad", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index 3f199835..59025cba 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Device; class NoiseSensorsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class NoiseSensorsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -121,10 +122,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/noise_sensors/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/noise_sensors/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index af7ad0de..39723a23 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\NoiseThreshold; class NoiseSensorsNoiseThresholdsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class NoiseSensorsNoiseThresholdsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -59,10 +60,12 @@ public function create( $request_payload["noise_threshold_nrs"] = $noise_threshold_nrs; } - $res = $this->client->request( - "POST", - "/noise_sensors/noise_thresholds/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/noise_sensors/noise_thresholds/create", + ["json" => (object) $request_payload], + ), ); return NoiseThreshold::from_json($res->noise_threshold); @@ -85,7 +88,7 @@ public function delete(string $device_id, string $noise_threshold_id): void $this->client->request( "POST", "/noise_sensors/noise_thresholds/delete", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -101,10 +104,12 @@ public function get(string $noise_threshold_id): NoiseThreshold $request_payload["noise_threshold_id"] = $noise_threshold_id; - $res = $this->client->request( - "POST", - "/noise_sensors/noise_thresholds/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/noise_sensors/noise_thresholds/get", + ["json" => (object) $request_payload], + ), ); return NoiseThreshold::from_json($res->noise_threshold); @@ -122,10 +127,12 @@ public function list(string $device_id): array $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/noise_sensors/noise_thresholds/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/noise_sensors/noise_thresholds/list", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -180,7 +187,7 @@ public function update( $this->client->request( "POST", "/noise_sensors/noise_thresholds/update", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/NoiseSensorsSimulateClient.php b/src/Routes/NoiseSensorsSimulateClient.php index 7388b50b..7d387ece 100644 --- a/src/Routes/NoiseSensorsSimulateClient.php +++ b/src/Routes/NoiseSensorsSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class NoiseSensorsSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class NoiseSensorsSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -37,7 +37,7 @@ public function trigger_noise_threshold(string $device_id): void $this->client->request( "POST", "/noise_sensors/simulate/trigger_noise_threshold", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index b97d5243..b710c78c 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Phone; class PhonesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class PhonesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -36,11 +37,9 @@ public function deactivate(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/phones/deactivate", - json: (object) $request_payload, - ); + $this->client->request("POST", "/phones/deactivate", [ + "json" => (object) $request_payload, + ]); } /** @@ -55,10 +54,10 @@ public function get(string $device_id): Phone $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/phones/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/phones/get", [ + "json" => (object) $request_payload, + ]), ); return Phone::from_json($res->phone); @@ -86,10 +85,10 @@ public function list( ] = $owner_user_identity_id; } - $res = $this->client->request( - "POST", - "/phones/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/phones/list", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => Phone::from_json($r), $res->phones); diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index 85c5e1ff..ba99c52e 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Phone; class PhonesSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class PhonesSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -53,10 +54,12 @@ public function create_sandbox_phone( $request_payload["phone_metadata"] = $phone_metadata; } - $res = $this->client->request( - "POST", - "/phones/simulate/create_sandbox_phone", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/phones/simulate/create_sandbox_phone", + ["json" => (object) $request_payload], + ), ); return Phone::from_json($res->phone); diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 46db212d..633e115a 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Batch; use Seam\Resources\Space; class SpacesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class SpacesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -40,11 +41,9 @@ public function add_acs_entrances( $request_payload["acs_entrance_ids"] = $acs_entrance_ids; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/add_acs_entrances", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/add_acs_entrances", [ + "json" => (object) $request_payload, + ]); } /** @@ -63,11 +62,9 @@ public function add_connected_account( $request_payload["connected_account_id"] = $connected_account_id; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/add_connected_account", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/add_connected_account", [ + "json" => (object) $request_payload, + ]); } /** @@ -84,11 +81,9 @@ public function add_devices(array $device_ids, string $space_id): void $request_payload["device_ids"] = $device_ids; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/add_devices", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/add_devices", [ + "json" => (object) $request_payload, + ]); } /** @@ -134,10 +129,10 @@ public function create( $request_payload["space_key"] = $space_key; } - $res = $this->client->request( - "POST", - "/spaces/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/create", [ + "json" => (object) $request_payload, + ]), ); return Space::from_json($res->space); @@ -155,11 +150,9 @@ public function delete(string $space_id): void $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -182,10 +175,10 @@ public function get( $request_payload["space_key"] = $space_key; } - $res = $this->client->request( - "POST", - "/spaces/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/get", [ + "json" => (object) $request_payload, + ]), ); return Space::from_json($res->space); @@ -221,10 +214,10 @@ public function get_related( $request_payload["space_keys"] = $space_keys; } - $res = $this->client->request( - "POST", - "/spaces/get_related", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/get_related", [ + "json" => (object) $request_payload, + ]), ); return Batch::from_json($res->batch); @@ -267,10 +260,10 @@ public function list( $request_payload["space_key"] = $space_key; } - $res = $this->client->request( - "POST", - "/spaces/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -296,11 +289,9 @@ public function remove_acs_entrances( $request_payload["acs_entrance_ids"] = $acs_entrance_ids; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/remove_acs_entrances", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/remove_acs_entrances", [ + "json" => (object) $request_payload, + ]); } /** @@ -319,11 +310,9 @@ public function remove_connected_account( $request_payload["connected_account_id"] = $connected_account_id; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/remove_connected_account", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/remove_connected_account", [ + "json" => (object) $request_payload, + ]); } /** @@ -340,11 +329,9 @@ public function remove_devices(array $device_ids, string $space_id): void $request_payload["device_ids"] = $device_ids; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/remove_devices", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/remove_devices", [ + "json" => (object) $request_payload, + ]); } /** @@ -387,10 +374,10 @@ public function update( $request_payload["space_key"] = $space_key; } - $res = $this->client->request( - "POST", - "/spaces/update", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/update", [ + "json" => (object) $request_payload, + ]), ); return Space::from_json($res->space); diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 7d7c4b4d..c73d00de 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; class ThermostatsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -21,7 +22,7 @@ class ThermostatsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -51,10 +52,12 @@ public function activate_climate_preset( $request_payload["climate_preset_key"] = $climate_preset_key; $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/thermostats/activate_climate_preset", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/thermostats/activate_climate_preset", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( @@ -94,10 +97,10 @@ public function cool( ] = $cooling_set_point_fahrenheit; } - $res = $this->client->request( - "POST", - "/thermostats/cool", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/cool", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -184,11 +187,9 @@ public function create_climate_preset( $request_payload["name"] = $name; } - $this->client->request( - "POST", - "/thermostats/create_climate_preset", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/create_climate_preset", [ + "json" => (object) $request_payload, + ]); } /** @@ -207,11 +208,9 @@ public function delete_climate_preset( $request_payload["climate_preset_key"] = $climate_preset_key; $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/thermostats/delete_climate_preset", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/delete_climate_preset", [ + "json" => (object) $request_payload, + ]); } /** @@ -243,10 +242,10 @@ public function heat( ] = $heating_set_point_fahrenheit; } - $res = $this->client->request( - "POST", - "/thermostats/heat", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/heat", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -300,10 +299,10 @@ public function heat_cool( ] = $heating_set_point_fahrenheit; } - $res = $this->client->request( - "POST", - "/thermostats/heat_cool", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/heat_cool", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -406,10 +405,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/thermostats/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -434,10 +433,10 @@ public function off( $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/thermostats/off", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/off", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -467,7 +466,7 @@ public function set_fallback_climate_preset( $this->client->request( "POST", "/thermostats/set_fallback_climate_preset", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -496,10 +495,10 @@ public function set_fan_mode( $request_payload["fan_mode_setting"] = $fan_mode_setting; } - $res = $this->client->request( - "POST", - "/thermostats/set_fan_mode", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/set_fan_mode", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -556,10 +555,10 @@ public function set_hvac_mode( ] = $heating_set_point_fahrenheit; } - $res = $this->client->request( - "POST", - "/thermostats/set_hvac_mode", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/set_hvac_mode", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -610,7 +609,7 @@ public function set_temperature_threshold( $this->client->request( "POST", "/thermostats/set_temperature_threshold", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -690,11 +689,9 @@ public function update_climate_preset( $request_payload["name"] = $name; } - $this->client->request( - "POST", - "/thermostats/update_climate_preset", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/update_climate_preset", [ + "json" => (object) $request_payload, + ]); } /** @@ -747,10 +744,12 @@ public function update_weekly_program( $request_payload["wednesday_program_id"] = $wednesday_program_id; } - $res = $this->client->request( - "POST", - "/thermostats/update_weekly_program", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/thermostats/update_weekly_program", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 19f8196f..9fc26e01 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\ThermostatDailyProgram; class ThermostatsDailyProgramsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class ThermostatsDailyProgramsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -44,10 +45,12 @@ public function create( $request_payload["name"] = $name; $request_payload["periods"] = $periods; - $res = $this->client->request( - "POST", - "/thermostats/daily_programs/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/thermostats/daily_programs/create", + ["json" => (object) $request_payload], + ), ); return ThermostatDailyProgram::from_json( @@ -69,11 +72,9 @@ public function delete(string $thermostat_daily_program_id): void "thermostat_daily_program_id" ] = $thermostat_daily_program_id; - $this->client->request( - "POST", - "/thermostats/daily_programs/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/daily_programs/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -99,10 +100,12 @@ public function update( "thermostat_daily_program_id" ] = $thermostat_daily_program_id; - $res = $this->client->request( - "POST", - "/thermostats/daily_programs/update", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/thermostats/daily_programs/update", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index 51864c69..4e857221 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\ThermostatSchedule; class ThermostatsSchedulesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class ThermostatsSchedulesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -62,10 +63,10 @@ public function create( $request_payload["name"] = $name; } - $res = $this->client->request( - "POST", - "/thermostats/schedules/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/schedules/create", [ + "json" => (object) $request_payload, + ]), ); return ThermostatSchedule::from_json($res->thermostat_schedule); @@ -83,11 +84,9 @@ public function delete(string $thermostat_schedule_id): void $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $this->client->request( - "POST", - "/thermostats/schedules/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/schedules/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -102,10 +101,10 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $res = $this->client->request( - "POST", - "/thermostats/schedules/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/schedules/get", [ + "json" => (object) $request_payload, + ]), ); return ThermostatSchedule::from_json($res->thermostat_schedule); @@ -129,10 +128,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/thermostats/schedules/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/schedules/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -186,10 +185,8 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->client->request( - "POST", - "/thermostats/schedules/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/schedules/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/ThermostatsSimulateClient.php b/src/Routes/ThermostatsSimulateClient.php index 9b00648f..244539cd 100644 --- a/src/Routes/ThermostatsSimulateClient.php +++ b/src/Routes/ThermostatsSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class ThermostatsSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class ThermostatsSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -69,7 +69,7 @@ public function hvac_mode_adjusted( $this->client->request( "POST", "/thermostats/simulate/hvac_mode_adjusted", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -101,7 +101,7 @@ public function temperature_reached( $this->client->request( "POST", "/thermostats/simulate/temperature_reached", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index e70ad961..3260b177 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -2,7 +2,8 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsSystem; use Seam\Resources\AcsUser; @@ -12,7 +13,7 @@ class UserIdentitiesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -22,7 +23,7 @@ class UserIdentitiesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -59,11 +60,9 @@ public function add_acs_user( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request( - "POST", - "/user_identities/add_acs_user", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/add_acs_user", [ + "json" => (object) $request_payload, + ]); } /** @@ -101,10 +100,10 @@ public function create( $request_payload["user_identity_key"] = $user_identity_key; } - $res = $this->client->request( - "POST", - "/user_identities/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/create", [ + "json" => (object) $request_payload, + ]), ); return UserIdentity::from_json($res->user_identity); @@ -122,11 +121,9 @@ public function delete(string $user_identity_id): void $request_payload["user_identity_id"] = $user_identity_id; - $this->client->request( - "POST", - "/user_identities/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -154,10 +151,12 @@ public function generate_instant_key( $request_payload["max_use_count"] = $max_use_count; } - $res = $this->client->request( - "POST", - "/user_identities/generate_instant_key", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/user_identities/generate_instant_key", + ["json" => (object) $request_payload], + ), ); return InstantKey::from_json($res->instant_key); @@ -183,10 +182,10 @@ public function get( $request_payload["user_identity_key"] = $user_identity_key; } - $res = $this->client->request( - "POST", - "/user_identities/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/get", [ + "json" => (object) $request_payload, + ]), ); return UserIdentity::from_json($res->user_identity); @@ -211,7 +210,7 @@ public function grant_access_to_device( $this->client->request( "POST", "/user_identities/grant_access_to_device", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -259,10 +258,10 @@ public function list( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->client->request( - "POST", - "/user_identities/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -287,10 +286,12 @@ public function list_accessible_devices(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/list_accessible_devices", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/user_identities/list_accessible_devices", + ["json" => (object) $request_payload], + ), ); return array_map(fn($r) => Device::from_json($r), $res->devices); @@ -308,10 +309,12 @@ public function list_accessible_entrances(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/list_accessible_entrances", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/user_identities/list_accessible_entrances", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -332,10 +335,12 @@ public function list_acs_systems(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/list_acs_systems", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/user_identities/list_acs_systems", + ["json" => (object) $request_payload], + ), ); return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); @@ -353,10 +358,10 @@ public function list_acs_users(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/list_acs_users", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/list_acs_users", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => AcsUser::from_json($r), $res->acs_users); @@ -378,11 +383,9 @@ public function remove_acs_user( $request_payload["acs_user_id"] = $acs_user_id; $request_payload["user_identity_id"] = $user_identity_id; - $this->client->request( - "POST", - "/user_identities/remove_acs_user", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/remove_acs_user", [ + "json" => (object) $request_payload, + ]); } /** @@ -404,7 +407,7 @@ public function revoke_access_to_device( $this->client->request( "POST", "/user_identities/revoke_access_to_device", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -441,10 +444,8 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request( - "POST", - "/user_identities/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 6683b8a2..4190e614 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedUserIdentity; class UserIdentitiesUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class UserIdentitiesUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,10 +36,10 @@ public function get(string $user_identity_id): UnmanagedUserIdentity $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedUserIdentity::from_json($res->user_identity); @@ -76,10 +77,10 @@ public function list( $request_payload["search"] = $search; } - $res = $this->client->request( - "POST", - "/user_identities/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -115,10 +116,8 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request( - "POST", - "/user_identities/unmanaged/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/unmanaged/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 4d95a1b7..efe061fa 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Webhook; class WebhooksClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class WebhooksClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -39,10 +40,10 @@ public function create(string $url, ?array $event_types = null): Webhook $request_payload["event_types"] = $event_types; } - $res = $this->client->request( - "POST", - "/webhooks/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/webhooks/create", [ + "json" => (object) $request_payload, + ]), ); return Webhook::from_json($res->webhook); @@ -60,11 +61,9 @@ public function delete(string $webhook_id): void $request_payload["webhook_id"] = $webhook_id; - $this->client->request( - "POST", - "/webhooks/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/webhooks/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -79,10 +78,10 @@ public function get(string $webhook_id): Webhook $request_payload["webhook_id"] = $webhook_id; - $res = $this->client->request( - "POST", - "/webhooks/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/webhooks/get", [ + "json" => (object) $request_payload, + ]), ); return Webhook::from_json($res->webhook); @@ -95,7 +94,7 @@ public function get(string $webhook_id): Webhook */ public function list(): array { - $res = $this->client->request("POST", "/webhooks/list"); + $res = Body::decode($this->client->request("POST", "/webhooks/list")); return array_map(fn($r) => Webhook::from_json($r), $res->webhooks); } @@ -114,10 +113,8 @@ public function update(array $event_types, string $webhook_id): void $request_payload["event_types"] = $event_types; $request_payload["webhook_id"] = $webhook_id; - $this->client->request( - "POST", - "/webhooks/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/webhooks/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 42a52ec5..84462b8a 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Workspace; class WorkspacesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class WorkspacesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -91,10 +92,10 @@ public function create( ] = $webview_success_message; } - $res = $this->client->request( - "POST", - "/workspaces/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/workspaces/create", [ + "json" => (object) $request_payload, + ]), ); return Workspace::from_json($res->workspace); @@ -107,7 +108,7 @@ public function create( */ public function get(): Workspace { - $res = $this->client->request("POST", "/workspaces/get"); + $res = Body::decode($this->client->request("POST", "/workspaces/get")); return Workspace::from_json($res->workspace); } @@ -119,7 +120,7 @@ public function get(): Workspace */ public function list(): array { - $res = $this->client->request("POST", "/workspaces/list"); + $res = Body::decode($this->client->request("POST", "/workspaces/list")); return array_map(fn($r) => Workspace::from_json($r), $res->workspaces); } @@ -133,7 +134,9 @@ public function list(): array public function reset_sandbox( bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - $res = $this->client->request("POST", "/workspaces/reset_sandbox"); + $res = Body::decode( + $this->client->request("POST", "/workspaces/reset_sandbox"), + ); return ResolveActionAttempt::resolve_action_attempt( ActionAttempt::from_json($res->action_attempt), @@ -187,10 +190,8 @@ public function update( $request_payload["organization_id"] = $organization_id; } - $this->client->request( - "POST", - "/workspaces/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/workspaces/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Seam.php b/src/Seam.php index 588c7617..cecd0646 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -23,8 +23,9 @@ use Seam\Routes\WebhooksClient; use Seam\Routes\WorkspacesClient; +use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; -use Seam\Http\SeamHttpClient; +use Seam\Http\ClientFactory; /** * Client for the Seam API. @@ -61,9 +62,12 @@ class Seam /** * The long term support version of the Seam API this SDK targets. */ - public const LTS_VERSION = SeamHttpClient::LTS_VERSION; + public const LTS_VERSION = ClientFactory::LTS_VERSION; - public SeamHttpClient $client; + /** + * The Guzzle client this instance makes its requests with. + */ + public ClientInterface $client; /** * Default request options applied to every call, currently just @@ -75,9 +79,10 @@ class Seam /** * @param bool|array{timeout?: float, polling_interval?: float}|null $wait_for_action_attempt Whether to wait for action attempts to finish, optionally with timeout and polling_interval in seconds. Defaults to true. - * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. timeout or headers. + * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. headers or proxy. * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. - * @param ClientInterface|null $client A preconfigured Guzzle client. It carries its own endpoint and authorization, so no other authentication option may be given with it. + * @param float|null $timeout Request timeout in seconds, covering connecting and reading. Defaults to 30; pass 0 to disable. + * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so no other authentication option applies to it. */ public function __construct( ?string $api_key = null, @@ -87,6 +92,7 @@ public function __construct( bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ?ClientInterface $client = null, ) { $this->defaults = [ @@ -96,18 +102,18 @@ public function __construct( // A client carries its own endpoint and authorization, so the // authentication options are only read when one has to be built. $this->client = - $client !== null - ? SeamHttpClient::from_client($client) - : new SeamHttpClient( - Options::get_endpoint($endpoint), - Auth::get_auth_headers( - $api_key, - $personal_access_token, - $workspace_id, - ), - $guzzle_options, - $retries, - ); + $client ?? + ClientFactory::create( + Options::get_endpoint($endpoint), + Auth::get_auth_headers( + $api_key, + $personal_access_token, + $workspace_id, + ), + $guzzle_options, + $retries, + $timeout, + ); $this->access_codes = new AccessCodesClient( $this->client, @@ -176,6 +182,7 @@ public static function from_api_key( bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ): static { return new static( api_key: $api_key, @@ -183,6 +190,7 @@ public static function from_api_key( wait_for_action_attempt: $wait_for_action_attempt, guzzle_options: $guzzle_options, retries: $retries, + timeout: $timeout, ); } @@ -197,6 +205,7 @@ public static function from_personal_access_token( bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ): static { return new static( personal_access_token: $personal_access_token, @@ -204,6 +213,7 @@ public static function from_personal_access_token( endpoint: $endpoint, wait_for_action_attempt: $wait_for_action_attempt, retries: $retries, + timeout: $timeout, guzzle_options: $guzzle_options, ); } @@ -226,21 +236,6 @@ public function lts_version(): string return self::LTS_VERSION; } - /** - * Makes a request against the Seam API with this client's authorization. - * - * @param array|object|null $json - * @param array|null $query - */ - public function request( - string $method, - string $path, - mixed $json = null, - ?array $query = null, - ): mixed { - return $this->client->request($method, $path, $json, $query); - } - /** * Creates a paginator for a list endpoint. * diff --git a/src/SeamClient.php b/src/SeamClient.php deleted file mode 100644 index c43d7e1b..00000000 --- a/src/SeamClient.php +++ /dev/null @@ -1,9 +0,0 @@ -client = SeamHttpClient::from_client($client); + $this->client = $client; } else { if ($personal_access_token === null) { throw new InvalidOptionsError( @@ -41,13 +45,14 @@ public function __construct( ); } - $this->client = new SeamHttpClient( + $this->client = ClientFactory::create( Options::get_endpoint($endpoint), Auth::get_auth_headers_for_personal_access_token_without_workspace( $personal_access_token, ), $guzzle_options, $retries, + $timeout, ); } @@ -67,12 +72,14 @@ public static function from_personal_access_token( ?string $endpoint = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ): static { return new static( personal_access_token: $personal_access_token, endpoint: $endpoint, guzzle_options: $guzzle_options, retries: $retries, + timeout: $timeout, ); } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 739c263b..46c143c2 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -4,7 +4,8 @@ namespace Tests; -use Seam\Http\SeamHttpClient; +use Seam\Http\Body; +use Seam\Http\ClientFactory; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; @@ -14,12 +15,12 @@ public function testClientCanMakeRequests(): void { $seam = $this->seam(); - $res = $seam->client->request( - "POST", - "/devices/get", - (object) [ - "device_id" => $this->seed["august_device_1"], - ], + $res = Body::decode( + $seam->client->request("POST", "/devices/get", [ + "json" => (object) [ + "device_id" => $this->seed["august_device_1"], + ], + ]), ); $this->assertSame( @@ -32,21 +33,11 @@ public function testClientCanMakeRequests(): void ); } - public function testRequestDelegatesToTheClient(): void + public function testClientIsTheGuzzleClient(): void { - $seam = $this->seam(); - - $res = $seam->request( - "POST", - "/devices/get", - (object) [ - "device_id" => $this->seed["august_device_1"], - ], - ); - - $this->assertSame( - $this->seed["august_device_1"], - $res->device->device_id, + $this->assertInstanceOf( + \GuzzleHttp\ClientInterface::class, + $this->seam()->client, ); } @@ -58,7 +49,7 @@ public function testFromClientNeedsNoCredentials(): void { $authorized = $this->seam(); - $seam = Seam::from_client($authorized->client->get_client()); + $seam = Seam::from_client($authorized->client); $device = $seam->devices->get($this->seed["august_device_1"]); @@ -71,7 +62,7 @@ public function testFromClientNeedsNoCredentials(): void public function testClientOptionReusesAnotherInstancesClient(): void { - $seam = new Seam(client: $this->seam()->client->get_client()); + $seam = new Seam(client: $this->seam()->client); $device = $seam->devices->get($this->seed["august_device_1"]); @@ -87,7 +78,7 @@ public function testGuzzleOptionsAreMergedIntoTheClient(): void ], ); - $config = $seam->client->get_client()->getConfig(); + $config = $seam->client->getConfig(); $this->assertSame(30, $config["timeout"]); $this->assertSame("Test-Value", $config["headers"]["Custom-Header"]); @@ -111,11 +102,27 @@ public function testGuzzleOptionsStillAuthorizeRequests(): void $this->assertSame($this->seed["august_device_1"], $device->device_id); } - public function testTimeoutDefaultsToSixtySeconds(): void + public function testTimeoutDefaultsToThirtySeconds(): void + { + $config = $this->seam()->client->getConfig(); + + $this->assertSame(30.0, ClientFactory::DEFAULT_TIMEOUT); + $this->assertSame(30.0, $config["timeout"]); + $this->assertSame(30.0, $config["connect_timeout"]); + } + + public function testTimeoutCanBeSet(): void { - $config = $this->seam()->client->get_client()->getConfig(); + $seam = Seam::from_api_key( + $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + timeout: 5.0, + ); - $this->assertSame(SeamHttpClient::DEFAULT_TIMEOUT, $config["timeout"]); + $config = $seam->client->getConfig(); + + $this->assertSame(5.0, $config["timeout"]); + $this->assertSame(5.0, $config["connect_timeout"]); } /** @@ -140,9 +147,7 @@ public function testWaitForActionAttemptDefaultsToTrueEverywhere(): void $this->seed["seed_workspace_1"], endpoint: $this->endpoint, ), - "from_client" => Seam::from_client( - $this->seam()->client->get_client(), - ), + "from_client" => Seam::from_client($this->seam()->client), ]; foreach ($clients as $name => $seam) { diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index 3f8cf4c7..d752ad4f 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -5,7 +5,7 @@ namespace Tests; use PHPUnit\Framework\TestCase; -use Seam\Http\SeamHttpClient; +use Seam\Http\ClientFactory; use Seam\Seam; use Seam\Utils\PackageVersion; use Tests\Support\RecordingClient; @@ -50,7 +50,7 @@ public function testSendsDefaultHeaders(): void $request->getHeaderLine("seam-sdk-version"), ); $this->assertSame( - SeamHttpClient::LTS_VERSION, + ClientFactory::LTS_VERSION, $request->getHeaderLine("seam-lts-version"), ); $this->assertSame( diff --git a/tests/HttpErrorTest.php b/tests/HttpErrorTest.php index d445defc..25af1c50 100644 --- a/tests/HttpErrorTest.php +++ b/tests/HttpErrorTest.php @@ -50,13 +50,9 @@ public function testThrowsApiErrorOnStandardErrorResponse(): void public function testThrowsInvalidInputErrorWithValidationMessages(): void { try { - $this->seam()->client->request( - "POST", - "/devices/list", - (object) [ - "device_ids" => 4242, - ], - ); + $this->seam()->client->request("POST", "/devices/list", [ + "json" => (object) ["device_ids" => 4242], + ]); $this->fail("Expected HttpInvalidInputError"); } catch (HttpInvalidInputError $error) { $this->assertSame(400, $error->getStatusCode()); @@ -75,13 +71,9 @@ public function testThrowsInvalidInputErrorWithValidationMessages(): void public function testValidationMessagesAreEmptyForAnUnknownParam(): void { try { - $this->seam()->client->request( - "POST", - "/devices/list", - (object) [ - "device_ids" => 4242, - ], - ); + $this->seam()->client->request("POST", "/devices/list", [ + "json" => (object) ["device_ids" => 4242], + ]); $this->fail("Expected HttpInvalidInputError"); } catch (HttpInvalidInputError $error) { $this->assertSame( @@ -94,20 +86,18 @@ public function testValidationMessagesAreEmptyForAnUnknownParam(): void /** * A workspace outage answers with a 503 that is not a Seam error * envelope, so it surfaces as the underlying transport error rather than - * a Seam exception, which is what the other Seam SDKs do too. + * a Seam exception. */ public function testWorkspaceOutageSurfacesTheTransportError(): void { $seam = $this->seam(retries: 0); - $seam->client->request( - "POST", - "/_fake/simulate_workspace_outage", - (object) [ + $seam->client->request("POST", "/_fake/simulate_workspace_outage", [ + "json" => (object) [ "workspace_id" => $this->seed["seed_workspace_1"], "routes" => ["/devices/list"], ], - ); + ]); try { $seam->devices->list(); diff --git a/tests/PackageVersionTest.php b/tests/PackageVersionTest.php index 7b1d7f9a..7d7d9fd3 100644 --- a/tests/PackageVersionTest.php +++ b/tests/PackageVersionTest.php @@ -29,7 +29,7 @@ public function testVersionMatchesPackageJson(): void public function testVersionIsUsedAsTheSdkVersionHeader(): void { $seam = new \Seam\Seam("seam_apikey1_token"); - $headers = $seam->client->get_client()->getConfig("headers"); + $headers = $seam->client->getConfig("headers"); $this->assertSame(PackageVersion::get(), $headers["seam-sdk-version"]); } diff --git a/tests/RetryTest.php b/tests/RetryTest.php index 322697e7..a001e125 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -7,6 +7,7 @@ use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Psr7\Request; use PHPUnit\Framework\TestCase; +use Seam\Http\Body; use Seam\Seam; use Tests\Support\RecordingClient; @@ -41,9 +42,8 @@ private function seam(RecordingClient $recorder, ?int $retries = null): Seam } /** - * Every Seam endpoint is a POST. Retrying one the server may already have - * processed could duplicate a write, so a status code never triggers a - * retry. The Ruby SDK asserts the same thing. + * A POST the server may already have processed could duplicate a write if + * repeated, so a status code never triggers a retry for one. */ public function testDoesNotRetryPostOnServiceUnavailable(): void { @@ -131,7 +131,9 @@ public function testRetriesIdempotentRequestsOnServiceUnavailable(): void self::devices(), ]); - $res = $this->seam($recorder)->client->request("GET", "/devices/list"); + $res = Body::decode( + $this->seam($recorder)->client->request("GET", "/devices/list"), + ); $this->assertSame([], $res->devices); $this->assertSame(3, $recorder->attempt_count()); diff --git a/tests/Support/FakeSeamConnect.php b/tests/Support/FakeSeamConnect.php index 93976532..7a8cbe2e 100644 --- a/tests/Support/FakeSeamConnect.php +++ b/tests/Support/FakeSeamConnect.php @@ -6,8 +6,7 @@ * Runs a fake Seam Connect server for the duration of a single test. * * Prefer this over stubbing HTTP responses: the fake exercises the SDK against - * a real server and seeded records, which is how the other Seam SDKs are - * tested. + * a real server and seeded records. */ class FakeSeamConnect { diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index 32dae081..4df89ea2 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -32,15 +32,13 @@ private function set_status( string $status, ?array $error = null, ): void { - $seam->client->request( - "POST", - "/_fake/update_action_attempt", - (object) array_filter([ + $seam->client->request("POST", "/_fake/update_action_attempt", [ + "json" => (object) array_filter([ "action_attempt_id" => $action_attempt->action_attempt_id, "status" => $status, "error" => $error, ]), - ); + ]); } public function testWaitsByDefault(): void From 795ff59ff943cc4a857a1e3407fd37d6848e0ce1 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:55:57 +0000 Subject: [PATCH 10/80] ci: construct the client the install check smoke tests The install check builds the package, requires it from a scratch project and constructs a client to prove the published archive autoloads. It still named Seam\SeamClient, which no longer exists, so the check failed on every PHP version once the alias went away. Construct Seam\Seam instead, by name, matching how the README documents calls. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e9772866..54db6700 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -121,7 +121,7 @@ jobs: contents: | Date: Thu, 13 Aug 2026 00:04:56 +0000 Subject: [PATCH 11/80] feat: read the personal access token and workspace id from the environment SEAM_PERSONAL_ACCESS_TOKEN and SEAM_WORKSPACE_ID now fall back into place the way SEAM_API_KEY already did, so a client can be constructed with no arguments under either authentication method. Defining both credential variables at once is ambiguous and raises an InvalidOptionsError. SeamWithoutWorkspace reads SEAM_PERSONAL_ACCESS_TOKEN as well. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 7 +++ codegen/layouts/seam-client.hbs | 3 +- src/Auth.php | 42 ++++++++++++- src/Seam.php | 3 +- src/SeamWithoutWorkspace.php | 19 +++--- tests/EnvTest.php | 102 +++++++++++++++++++++++++++++++- 6 files changed, 158 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5b8ab1b3..71a5fc55 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,9 @@ A Personal Access Token is scoped to a Seam Console user. It must be used with a workspace id. ```php +// Set the SEAM_PERSONAL_ACCESS_TOKEN and SEAM_WORKSPACE_ID environment variables +$seam = new Seam\Seam(); + // Pass as options to the constructor $seam = new Seam\Seam( personal_access_token: "your-personal-access-token", @@ -265,6 +268,10 @@ Some endpoints are not scoped to a workspace. Use `SeamWithoutWorkspace` with a personal access token to reach them. ```php +// Set the SEAM_PERSONAL_ACCESS_TOKEN environment variable +$seam = new Seam\SeamWithoutWorkspace(); + +// Use the factory method $seam = Seam\SeamWithoutWorkspace::from_personal_access_token( "your-personal-access-token" ); diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index c07c5d63..215622ad 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -15,7 +15,8 @@ use Seam\Http\ClientFactory; * * Authenticate with an API key, which is scoped to a single workspace, or with * a personal access token together with the id of the workspace to act on. - * When neither is given, the SEAM_API_KEY environment variable is used. + * When neither is given, the SEAM_API_KEY or SEAM_PERSONAL_ACCESS_TOKEN and + * SEAM_WORKSPACE_ID environment variables are used. * * @see https://docs.seam.co/ */ diff --git a/src/Auth.php b/src/Auth.php index 6023be71..55151b89 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -24,8 +24,20 @@ public static function get_auth_headers( // a stray SEAM_API_KEY. if ($api_key === null && $personal_access_token === null) { $api_key = Options::get_env("SEAM_API_KEY"); + $personal_access_token = Options::get_env( + "SEAM_PERSONAL_ACCESS_TOKEN", + ); + + if ($api_key !== null && $personal_access_token !== null) { + throw new InvalidOptionsError( + "Both SEAM_API_KEY and SEAM_PERSONAL_ACCESS_TOKEN environment variables are defined. " . + "Please use only one authentication method.", + ); + } } + $workspace_id ??= Options::get_env("SEAM_WORKSPACE_ID"); + if ( Options::is_seam_options_with_api_key( $api_key, @@ -50,8 +62,34 @@ public static function get_auth_headers( throw new InvalidOptionsError( "Must specify an api_key or personal_access_token. " . - "Attempted reading configuration from the environment, " . - "but the environment variable SEAM_API_KEY is not set.", + "Attempted reading configuration from the environment, but neither the " . + "SEAM_API_KEY nor the SEAM_PERSONAL_ACCESS_TOKEN environment variable is set.", + ); + } + + /** + * Builds the headers for a client that is not scoped to a workspace, + * falling back to the environment when no token is given. + * + * @return array + */ + public static function get_auth_headers_without_workspace( + ?string $personal_access_token = null, + ): array { + $personal_access_token ??= Options::get_env( + "SEAM_PERSONAL_ACCESS_TOKEN", + ); + + if ($personal_access_token === null) { + throw new InvalidOptionsError( + "Must specify a personal_access_token. " . + "Attempted reading configuration from the environment, " . + "but the environment variable SEAM_PERSONAL_ACCESS_TOKEN is not set.", + ); + } + + return self::get_auth_headers_for_personal_access_token_without_workspace( + $personal_access_token, ); } diff --git a/src/Seam.php b/src/Seam.php index cecd0646..855b1d4e 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -32,7 +32,8 @@ * * Authenticate with an API key, which is scoped to a single workspace, or with * a personal access token together with the id of the workspace to act on. - * When neither is given, the SEAM_API_KEY environment variable is used. + * When neither is given, the SEAM_API_KEY or SEAM_PERSONAL_ACCESS_TOKEN and + * SEAM_WORKSPACE_ID environment variables are used. * * @see https://docs.seam.co/ */ diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index e8981729..0423e5eb 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -11,6 +11,9 @@ * * Authenticates with a personal access token and exposes only the workspace * listing and creation endpoints. Use Seam\Seam for everything else. + * + * When no token is given, the SEAM_PERSONAL_ACCESS_TOKEN environment variable + * is used. */ class SeamWithoutWorkspace { @@ -36,25 +39,17 @@ public function __construct( ) { // A client carries its own endpoint and authorization, so the // authentication options are only read when one has to be built. - if ($client !== null) { - $this->client = $client; - } else { - if ($personal_access_token === null) { - throw new InvalidOptionsError( - "Must specify a personal_access_token", - ); - } - - $this->client = ClientFactory::create( + $this->client = + $client ?? + ClientFactory::create( Options::get_endpoint($endpoint), - Auth::get_auth_headers_for_personal_access_token_without_workspace( + Auth::get_auth_headers_without_workspace( $personal_access_token, ), $guzzle_options, $retries, $timeout, ); - } $this->workspaces = new WorkspacesProxy( new WorkspacesClient($this->client, [ diff --git a/tests/EnvTest.php b/tests/EnvTest.php index 3c0f92a7..79876cce 100644 --- a/tests/EnvTest.php +++ b/tests/EnvTest.php @@ -7,11 +7,18 @@ use Seam\InvalidOptionsError; use Seam\Options; use Seam\Seam; +use Seam\SeamWithoutWorkspace; use Tests\Support\FakeSeamConnectTestCase; final class EnvTest extends FakeSeamConnectTestCase { - private const VARIABLES = ["SEAM_API_KEY", "SEAM_ENDPOINT", "SEAM_API_URL"]; + private const VARIABLES = [ + "SEAM_API_KEY", + "SEAM_PERSONAL_ACCESS_TOKEN", + "SEAM_WORKSPACE_ID", + "SEAM_ENDPOINT", + "SEAM_API_URL", + ]; /** @var array */ private array $saved_env = []; @@ -118,10 +125,68 @@ public function testDeprecatedSeamApiUrlWarns(): void } } + public function testReadsThePersonalAccessTokenAndWorkspaceIdFromTheEnvironment(): void + { + putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); + putenv("SEAM_WORKSPACE_ID=" . $this->seed["seed_workspace_1"]); + + $seam = new Seam(endpoint: $this->endpoint); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testReadsOnlyTheWorkspaceIdFromTheEnvironment(): void + { + putenv("SEAM_WORKSPACE_ID=" . $this->seed["seed_workspace_1"]); + + $seam = new Seam( + personal_access_token: $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testWorkspaceIdOptionWinsOverTheEnvironment(): void + { + putenv("SEAM_WORKSPACE_ID=workspace-from-the-environment"); + + $seam = new Seam( + personal_access_token: $this->seed["seam_at1_token"], + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + /** + * Two credentials in the environment are ambiguous, so neither is picked. + */ + public function testFailsWhenBothCredentialEnvironmentVariablesAreSet(): void + { + putenv("SEAM_API_KEY=" . $this->seed["seam_apikey1_token"]); + putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); + putenv("SEAM_WORKSPACE_ID=" . $this->seed["seed_workspace_1"]); + + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage( + "Both SEAM_API_KEY and SEAM_PERSONAL_ACCESS_TOKEN", + ); + + new Seam(endpoint: $this->endpoint); + } + public function testFailsWhenNoCredentialsAreAvailable(): void { $this->expectException(InvalidOptionsError::class); - $this->expectExceptionMessage("SEAM_API_KEY is not set"); + $this->expectExceptionMessage("SEAM_API_KEY"); new Seam(endpoint: $this->endpoint); } @@ -140,4 +205,37 @@ public function testApiKeyEnvironmentVariableIsIgnoredForAPersonalAccessToken(): $this->assertSame($this->seed["august_device_1"], $device->device_id); } + + public function testPersonalAccessTokenEnvironmentVariableIsIgnoredForAnApiKey(): void + { + putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); + + $seam = new Seam( + api_key: $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testWithoutWorkspaceReadsThePersonalAccessTokenFromTheEnvironment(): void + { + putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); + + $seam = new SeamWithoutWorkspace(endpoint: $this->endpoint); + + $workspaces = $seam->workspaces->list(); + + $this->assertNotEmpty($workspaces); + } + + public function testWithoutWorkspaceFailsWhenNoTokenIsAvailable(): void + { + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage("SEAM_PERSONAL_ACCESS_TOKEN is not set"); + + new SeamWithoutWorkspace(endpoint: $this->endpoint); + } } From aba2adfabfaba9daecdbc31e9956acd4fd1135cf Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 03:55:53 +0000 Subject: [PATCH 12/80] fix: return a list of action attempts as a list Whether a method resolves its result as an action attempt was keyed on the return resource alone, so the list endpoint wrapped its entire action_attempts array in a single ActionAttempt and piped it through the resolver, which broke every call to it: an empty list decoded to null and a populated one polled an attempt with a null id. The array response now generates like any other list endpoint, which also restores the on_response hook the resolver branch skipped, so the paginator gets its pagination metadata. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- codegen/lib/layouts/route.ts | 9 +++++++-- src/Routes/ActionAttemptsClient.php | 14 +++++++------- tests/WaitForActionAttemptTest.php | 22 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 8dc8c110..8450dff8 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -74,7 +74,10 @@ const getMethodLayoutContext = ( ): MethodLayoutContext => { const { methodName, path, parameters, returnResource, returnPath } = method - const usesActionAttempt = returnResource === 'ActionAttempt' + // A method returning a list of action attempts is an ordinary list + // endpoint: only a single returned attempt is resolved. + const usesActionAttempt = + returnResource === 'ActionAttempt' && !method.isArrayResponse const usesOnResponse = parameters.some((p) => p.name === 'page_cursor') && methodName === 'list' const returnsVoid = returnResource === '' @@ -146,7 +149,9 @@ const getUseStatements = (client: PhpClient): string[] => { .filter((resourceName) => resourceName !== ''), ) - const usesActionAttempt = resourceNames.has('ActionAttempt') + const usesActionAttempt = client.methods.some( + (m) => m.returnResource === 'ActionAttempt' && !m.isArrayResponse, + ) // Void endpoints never read the response, so they do not decode it. const readsBody = client.methods.some((m) => m.returnResource !== '') diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 3f03a968..c1a656d4 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -61,7 +61,6 @@ public function get( * @param string $device_id ID of the device to filter action attempts by. * @param int $limit Maximum number of records to return per page. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. - * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ @@ -70,7 +69,6 @@ public function list( ?string $device_id = null, ?int $limit = null, ?string $page_cursor = null, - bool|array|null $wait_for_action_attempt = null, ?callable $on_response = null, ): array { $request_payload = []; @@ -94,11 +92,13 @@ public function list( ]), ); - return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempts), - $this->client, - $wait_for_action_attempt ?? - $this->defaults["wait_for_action_attempt"], + if ($on_response !== null) { + $on_response($res); + } + + return array_map( + fn($r) => ActionAttempt::from_json($r), + $res->action_attempts, ); } } diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index 4df89ea2..7f4696ed 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -26,6 +26,28 @@ private function pending_action_attempt(Seam $seam): ActionAttempt return $action_attempt; } + /** + * A list of action attempts is returned as is: only a single returned + * attempt is ever resolved, so listing must not poll pending attempts. + */ + public function testListReturnsActionAttemptsWithoutResolvingThem(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + $pending = $this->pending_action_attempt($seam); + + $attempts = $seam->action_attempts->list( + action_attempt_ids: [$pending->action_attempt_id], + ); + + $this->assertContainsOnlyInstancesOf(ActionAttempt::class, $attempts); + $this->assertCount(1, $attempts); + $this->assertSame( + $pending->action_attempt_id, + $attempts[0]->action_attempt_id, + ); + $this->assertSame("pending", $attempts[0]->status); + } + private function set_status( Seam $seam, ActionAttempt $action_attempt, From 0dfb88619c58db2f497624e59120bac65bab60ff Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 03:56:10 +0000 Subject: [PATCH 13/80] fix: keep a retried request from duplicating a write A timeout can fire while waiting on a response to a request the server received and is still processing, so retrying it can repeat a write, such as unlocking a door twice. Transport errors on a method that is not safe to repeat now go through a dedicated middleware that skips anything that looks like a timeout, while idempotent methods keep retrying every transport error. Connection resets keep retrying either way. The error mapping middleware also sat inside the redirect middleware, where raising on a 3xx made following redirects impossible even when asked for. It is unshifted to the outside of the stack, so a redirect is followed rather than raised and only an unfollowed one is an error. The handler stack a caller passes in is cloned rather than mutated, so building a second client from the same options no longer stacks the middleware twice and multiplies the retries. A bare handler, such as a MockHandler, is wrapped in a stack so the error mapping and retries apply to it instead of being silently dropped. And a response body that cannot seek, such as a streamed response, is read as is rather than failing on rewind. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- src/Http/Body.php | 9 +- src/Http/ClientFactory.php | 73 +++----------- src/Http/ErrorMiddleware.php | 6 +- src/Http/RetryMiddleware.php | 190 +++++++++++++++++++++++++++++++++++ tests/RedirectTest.php | 72 +++++++++++++ tests/RetryTest.php | 129 ++++++++++++++++++++++++ 6 files changed, 416 insertions(+), 63 deletions(-) create mode 100644 src/Http/RetryMiddleware.php create mode 100644 tests/RedirectTest.php diff --git a/src/Http/Body.php b/src/Http/Body.php index a63321a7..47b36eef 100644 --- a/src/Http/Body.php +++ b/src/Http/Body.php @@ -15,12 +15,17 @@ final class Body * * A PSR-7 body is a stream that can only be read once, so it is rewound * first: the middleware reads it to look for a Seam error before the - * caller ever sees the response. + * caller ever sees the response. A body that cannot seek, such as a + * streamed response, is simply read from where it stands. */ public static function decode(ResponseInterface $response): mixed { $body = $response->getBody(); - $body->rewind(); + + if ($body->isSeekable()) { + $body->rewind(); + } + $contents = $body->getContents(); if ($contents === "") { diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php index b51f3136..a479e305 100644 --- a/src/Http/ClientFactory.php +++ b/src/Http/ClientFactory.php @@ -4,9 +4,6 @@ use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; -use GuzzleHttp\Promise\PromiseInterface; -use GuzzleRetry\GuzzleRetryMiddleware; -use Psr\Http\Message\RequestInterface; use Seam\Utils\PackageVersion; /** @@ -28,22 +25,6 @@ final class ClientFactory */ public const DEFAULT_TIMEOUT = 30.0; - private const RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504]; - - /** - * Retrying a request the server may already have processed can duplicate a - * write, so a status code only triggers a retry for a method that is safe - * to repeat. A request that never reached the server is retried whatever - * the method, since it cannot have had an effect. - */ - private const IDEMPOTENT_METHODS = [ - "GET", - "HEAD", - "OPTIONS", - "PUT", - "DELETE", - ]; - /** * @param array $auth_headers * @param array $guzzle_options @@ -63,25 +44,22 @@ public static function create( $handler = $guzzle_options["handler"] ?? HandlerStack::create(); if ($handler instanceof HandlerStack) { - // Pushed first, so it wraps the retry middleware and only sees the - // response a request finally settled on. - $handler->push(ErrorMiddleware::create(), "seam_error"); - $handler->push( - self::retry_options_middleware(), - "seam_retry_options", - ); - $handler->push( - GuzzleRetryMiddleware::factory([ - "retry_enabled" => $retries > 0, - "max_retry_attempts" => $retries, - "retry_on_timeout" => true, - // Set per request by the middleware above. - "retry_on_status" => [], - ]), - "seam_retry", - ); + // Cloned so that building a client does not mutate a stack the + // caller may reuse, which would stack this middleware twice. + $handler = clone $handler; + } else { + // A bare handler, such as a MockHandler, is wrapped so the + // middleware below still applies to it. + $handler = HandlerStack::create($handler); } + // Unshifted so it sits outside every other middleware and only sees + // a response none of them could act on: a redirect is followed + // rather than raised, and a retried request is judged by the + // response it finally settled on. + $handler->unshift(ErrorMiddleware::create(), "seam_error"); + RetryMiddleware::add($handler, $retries); + $headers = array_merge( $auth_headers, $guzzle_options["headers"] ?? [], @@ -107,29 +85,6 @@ public static function create( ); } - /** - * Opts a request into status based retries when repeating it is safe. - */ - private static function retry_options_middleware(): callable - { - return static fn(callable $handler): callable => static function ( - RequestInterface $request, - array $options, - ) use ($handler): PromiseInterface { - if ( - in_array( - strtoupper($request->getMethod()), - self::IDEMPOTENT_METHODS, - true, - ) - ) { - $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; - } - - return $handler($request, $options); - }; - } - /** * @return array */ diff --git a/src/Http/ErrorMiddleware.php b/src/Http/ErrorMiddleware.php index a709af15..d4c4c506 100644 --- a/src/Http/ErrorMiddleware.php +++ b/src/Http/ErrorMiddleware.php @@ -14,8 +14,10 @@ * Guzzle middleware that turns a Seam error response into a Seam exception. * * The client is built with `http_errors` disabled so this runs instead, and it - * sits outside the retry middleware so it only sees the response a request - * finally settled on. + * sits outside every other middleware so it only sees a response none of them + * could act on: a redirect this deep has already been followed or was opted + * out of, and a retried request is judged by the response it finally settled + * on. * * A response that is not a Seam error envelope, such as a gateway returning * HTML, raises the transport error Guzzle would have raised on its own. diff --git a/src/Http/RetryMiddleware.php b/src/Http/RetryMiddleware.php new file mode 100644 index 00000000..6411a132 --- /dev/null +++ b/src/Http/RetryMiddleware.php @@ -0,0 +1,190 @@ +push(self::retry_options_middleware(), "seam_retry_options"); + $handler->push( + GuzzleRetryMiddleware::factory([ + "retry_enabled" => $retries > 0, + "max_retry_attempts" => $retries, + "retry_on_timeout" => true, + // Set per request by the middleware above. + "retry_on_status" => [], + ]), + "seam_retry", + ); + // Pushed last, so it sits inside the middleware above and its + // retries are invisible to it: each mechanism covers the failures + // the other does not, and their budgets never combine. + $handler->push( + self::transport_retry_middleware($retries), + "seam_transport_retry", + ); + } + + /** + * Configures the retry middleware per request: a method that is safe to + * repeat opts into status based retries, any other method opts out of + * its transport error handling, which would retry timeouts as well, and + * is covered by the transport retry middleware instead. + */ + private static function retry_options_middleware(): callable + { + return static fn(callable $handler): callable => static function ( + RequestInterface $request, + array $options, + ) use ($handler): PromiseInterface { + if (self::is_idempotent($request)) { + $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; + } else { + $options["retry_on_timeout"] = false; + } + + return $handler($request, $options); + }; + } + + /** + * Retries the transport errors for the methods that are not safe to + * repeat, skipping anything that looks like a timeout. + */ + private static function transport_retry_middleware(int $retries): callable + { + return static fn( + callable $handler, + ): callable => static fn(RequestInterface $request, array $options): PromiseInterface => self::dispatch($handler, $request, $options, $retries); + } + + private static function dispatch( + callable $handler, + RequestInterface $request, + array $options, + int $retries, + ): PromiseInterface { + return $handler($request, $options)->otherwise(static function ( + \Throwable $reason, + ) use ($handler, $request, $options, $retries): PromiseInterface { + $attempts = $options["seam_transport_retries"] ?? 0; + + if ( + !is_int($attempts) || + $attempts >= $retries || + self::is_idempotent($request) || + !self::is_retryable_transport_error($reason) + ) { + return Create::rejectionFor($reason); + } + + $options["seam_transport_retries"] = $attempts + 1; + $options["delay"] = + (int) (self::RETRY_DELAY_SECONDS * + (float) ($attempts + 1) * + 1000.0); + + return self::dispatch($handler, $request, $options, $retries); + }); + } + + private static function is_retryable_transport_error( + \Throwable $reason, + ): bool { + if ($reason instanceof ConnectException) { + return !self::is_timeout($reason); + } + + // A response was received, so this is a status error covered by the + // retry middleware, not a transport error. + if ($reason instanceof BadResponseException) { + return false; + } + + if ($reason instanceof RequestException) { + $errno = $reason->getHandlerContext()["errno"] ?? null; + + return $errno === self::ECONNRESET; + } + + return false; + } + + /** + * Whether a connection exception is a timeout. The curl errno is + * decisive when present; a handler without one, such as the stream + * handler, is judged by its message. + */ + private static function is_timeout(ConnectException $reason): bool + { + $errno = $reason->getHandlerContext()["errno"] ?? null; + + if ($errno !== null) { + return $errno === self::CURLE_OPERATION_TIMEDOUT; + } + + return preg_match("/timed?\s*out/i", $reason->getMessage()) === 1; + } + + private static function is_idempotent(RequestInterface $request): bool + { + return in_array( + strtoupper($request->getMethod()), + self::IDEMPOTENT_METHODS, + true, + ); + } +} diff --git a/tests/RedirectTest.php b/tests/RedirectTest.php new file mode 100644 index 00000000..0be0105d --- /dev/null +++ b/tests/RedirectTest.php @@ -0,0 +1,72 @@ +guzzle_options(), + $guzzle_options, + ), + ); + } + + public function testFollowsARedirectByDefault(): void + { + $recorder = new RecordingClient([ + new Response(302, [ + "location" => "https://example.com/devices/list_moved", + ]), + RecordingClient::json(200, ["devices" => []]), + ]); + + $devices = $this->seam($recorder)->devices->list(); + + $this->assertSame([], $devices); + $this->assertSame(2, $recorder->attempt_count()); + } + + /** + * With following disabled, a redirect is a response outside the success + * range like any other, and must not be silently decoded as a success. + */ + public function testARedirectIsAnErrorWhenFollowingIsDisabled(): void + { + $recorder = new RecordingClient([ + new Response(302, [ + "location" => "https://example.com/devices/list_moved", + ]), + ]); + + $seam = $this->seam($recorder, ["allow_redirects" => false]); + + try { + $seam->devices->list(); + $this->fail("Expected the redirect to surface as an error"); + } catch (RequestException $error) { + $this->assertSame(302, $error->getResponse()?->getStatusCode()); + } + + $this->assertSame(1, $recorder->attempt_count()); + } +} diff --git a/tests/RetryTest.php b/tests/RetryTest.php index a001e125..c033f905 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -5,6 +5,7 @@ namespace Tests; use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Request; use PHPUnit\Framework\TestCase; use Seam\Http\Body; @@ -82,6 +83,101 @@ public function testRetriesPostOnConnectionFailure(): void $this->assertSame(3, $recorder->attempt_count()); } + /** + * A timeout may have fired while waiting on a response to a request the + * server received and is still processing, so repeating the POST could + * duplicate a write. + */ + public function testDoesNotRetryPostOnTimeout(): void + { + $timeout = new ConnectException( + "cURL error 28: Operation timed out after 30001 milliseconds with 0 bytes received", + new Request("POST", "/devices/list"), + null, + ["errno" => 28], + ); + + $recorder = RecordingClient::repeating_throwable($timeout); + + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the timeout to surface"); + } catch (ConnectException) { + // Expected. + } + + $this->assertSame(1, $recorder->attempt_count()); + } + + /** + * A handler other than curl reports no errno, so the timeout is + * recognized by its message. + */ + public function testDoesNotRetryPostOnTimeoutWithoutAnErrno(): void + { + $timeout = new ConnectException( + "Connection timed out", + new Request("POST", "/devices/list"), + ); + + $recorder = RecordingClient::repeating_throwable($timeout); + + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the timeout to surface"); + } catch (ConnectException) { + // Expected. + } + + $this->assertSame(1, $recorder->attempt_count()); + } + + /** + * Repeating an idempotent request is safe even when the server may have + * received it, so a timeout does get retried there. + */ + public function testRetriesIdempotentRequestsOnTimeout(): void + { + $timeout = new ConnectException( + "cURL error 28: Operation timed out after 30001 milliseconds with 0 bytes received", + new Request("GET", "/devices/list"), + null, + ["errno" => 28], + ); + + $recorder = new RecordingClient([$timeout, $timeout, self::devices()]); + + $res = Body::decode( + $this->seam($recorder)->client->request("GET", "/devices/list"), + ); + + $this->assertSame([], $res->devices); + $this->assertSame(3, $recorder->attempt_count()); + } + + /** + * A connection reset by the peer surfaces as a request exception with + * errno 104 rather than a connection exception, but is a transport + * failure all the same. + */ + public function testRetriesPostOnConnectionReset(): void + { + $reset = new RequestException( + "Connection reset by peer", + new Request("POST", "/devices/list"), + null, + null, + ["errno" => 104], + ); + + $recorder = new RecordingClient([$reset, self::devices()]); + + $devices = $this->seam($recorder)->devices->list(); + + $this->assertSame([], $devices); + $this->assertSame(2, $recorder->attempt_count()); + } + public function testStopsRetryingOnceRetriesAreExhausted(): void { $connect_error = new ConnectException( @@ -119,6 +215,39 @@ public function testDoesNotRetryWhenRetriesAreDisabled(): void $this->assertSame(1, $recorder->attempt_count()); } + /** + * Building a client must not mutate a handler stack the caller may + * reuse: a second client built from the same options would otherwise + * stack the retry middleware twice and multiply the retries. + */ + public function testBuildingASecondClientDoesNotStackRetries(): void + { + $recorder = RecordingClient::repeating(self::service_unavailable()); + $options = $recorder->guzzle_options(); + + $first = new Seam( + api_key: self::API_KEY, + endpoint: "https://example.com", + guzzle_options: $options, + ); + $second = new Seam( + api_key: self::API_KEY, + endpoint: "https://example.com", + guzzle_options: $options, + ); + + $this->assertNotSame($first->client, $second->client); + + try { + Body::decode($second->client->request("GET", "/devices/list")); + $this->fail("Expected the 503 to surface"); + } catch (\Throwable) { + // The error mapping is covered in HttpErrorTest. + } + + $this->assertSame(3, $recorder->attempt_count()); + } + /** * The SDK itself only issues POSTs, but a caller reaching for the client * directly with an idempotent method does get status based retries. From c83b3edab02921dd9753c47ef27c347deee7cfc4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 03:56:23 +0000 Subject: [PATCH 14/80] fix: reject an option that a preconfigured client would silently discard A credential or endpoint passed beside a client was dropped without a sound, since the client carries its own, so requests went out with whatever authorization the client held. The combination now raises an InvalidOptionsError naming the offending option, on both Seam and SeamWithoutWorkspace; wait_for_action_attempt stays allowed since it does not configure the client. Also swept up along the way: - The paginator replaced a caller's on_response callback with its own instead of chaining the two, so the caller's silently never fired. - WorkspacesProxy forwarded to the generated create positionally, the exact silent mis-binding the README warns about, and now uses named arguments. - The reserved name check in codegen only knew PHP's type names, so a property named list or default would have generated a class that cannot parse; it now covers the reserved keywords too. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 3 +- codegen/layouts/seam-client.hbs | 16 +++++-- codegen/lib/resource-model.ts | 73 +++++++++++++++++++++++++++++-- src/Options.php | 25 +++++++++++ src/Paginator.php | 16 +++++-- src/Seam.php | 16 +++++-- src/SeamWithoutWorkspace.php | 12 +++++- src/WorkspacesProxy.php | 23 +++++----- tests/ClientTest.php | 76 +++++++++++++++++++++++++++++++++ tests/PaginatorTest.php | 21 +++++++++ 10 files changed, 256 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 71a5fc55..313ac7cb 100644 --- a/README.md +++ b/README.md @@ -374,7 +374,8 @@ $devices = Seam\Http\Body::decode($response)->devices; #### Overriding the client Pass an already configured Guzzle client. It carries its own endpoint and -authorization, so no other authentication option may be given alongside it. +authorization, so it cannot be combined with any option other than +`wait_for_action_attempt`. ```php $client = new GuzzleHttp\Client([ diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index 215622ad..2f0b0b92 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -49,7 +49,7 @@ class Seam * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. headers or proxy. * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. * @param float|null $timeout Request timeout in seconds, covering connecting and reading. Defaults to 30; pass 0 to disable. - * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so no other authentication option applies to it. + * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so it cannot be combined with any option other than wait_for_action_attempt. */ public function __construct( ?string $api_key = null, @@ -66,8 +66,18 @@ class Seam "wait_for_action_attempt" => $wait_for_action_attempt ?? true, ]; - // A client carries its own endpoint and authorization, so the - // authentication options are only read when one has to be built. + // A client carries its own endpoint and authorization, so no option + // that would configure one can be combined with it. + Options::check_client_options($client, [ + "api_key" => $api_key, + "personal_access_token" => $personal_access_token, + "workspace_id" => $workspace_id, + "endpoint" => $endpoint, + "guzzle_options" => $guzzle_options, + "retries" => $retries, + "timeout" => $timeout, + ]); + $this->client = $client ?? ClientFactory::create( Options::get_endpoint($endpoint), Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index 2d9bccd3..6c277f86 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -61,9 +61,10 @@ const rootNamespace = 'Seam\\Resources' // deep, so exceeding it means the blueprint is cyclic rather than nested. const maxDepth = 16 -// PHP reserves these as type names, so a class cannot be called any of them. -// Class names are case insensitive, so the check has to be too. +// PHP reserves these as type names or keywords, so a class cannot be called +// any of them. Class names are case insensitive, so the check has to be too. const reservedClassNames = new Set([ + // Type names. 'array', 'bool', 'callable', @@ -82,6 +83,72 @@ const reservedClassNames = new Set([ 'string', 'true', 'void', + // Keywords. + 'abstract', + 'and', + 'as', + 'break', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'echo', + 'else', + 'elseif', + 'empty', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'exit', + 'extends', + 'final', + 'finally', + 'fn', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'include', + 'include_once', + 'instanceof', + 'insteadof', + 'interface', + 'isset', + 'list', + 'match', + 'namespace', + 'new', + 'or', + 'print', + 'private', + 'protected', + 'public', + 'readonly', + 'require', + 'require_once', + 'return', + 'switch', + 'throw', + 'trait', + 'try', + 'unset', + 'use', + 'var', + 'while', + 'xor', + 'yield', ]) interface ClassDocs { @@ -209,7 +276,7 @@ const buildClass = ( if (reservedClassNames.has(nestedClassName.toLowerCase())) { throw new Error( - `Cannot generate ${nestedPath}: ${nestedClassName} is a reserved PHP type name`, + `Cannot generate ${nestedPath}: ${nestedClassName} is reserved in PHP`, ) } diff --git a/src/Options.php b/src/Options.php index e3d22aa3..287adfa0 100644 --- a/src/Options.php +++ b/src/Options.php @@ -79,6 +79,31 @@ public static function is_seam_options_with_personal_access_token( return true; } + /** + * A preconfigured client carries its own endpoint and authorization, so an + * option that would configure one is a mistake to combine with it, and is + * rejected rather than silently ignored. + * + * @param array $options The other options by name, where + * null (or, for an options array, empty) means the option was not given. + */ + public static function check_client_options( + ?object $client, + array $options, + ): void { + if ($client === null) { + return; + } + + foreach ($options as $name => $value) { + if ($value !== null && $value !== []) { + throw new InvalidOptionsError( + "The {$name} option cannot be used with the client option", + ); + } + } + } + /** * Reads an environment variable, treating an empty value as unset so that * an exported-but-blank variable does not override the default. diff --git a/src/Paginator.php b/src/Paginator.php index 5b86e6db..812012d8 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -55,10 +55,20 @@ private function fetchPage(string $cursor): array $params["page_cursor"] = $cursor; } - $params["on_response"] = fn($response) => $this->cachePagination( - $response, + // Chained rather than replaced, so a callback the caller passed in + // through the params still fires. + $on_response = $params["on_response"] ?? null; + + $params["on_response"] = function ($response) use ( $cursor, - ); + $on_response, + ): void { + $this->cachePagination($response, $cursor); + + if (is_callable($on_response)) { + $on_response($response); + } + }; $data = $request($params); diff --git a/src/Seam.php b/src/Seam.php index 855b1d4e..76dcda31 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -83,7 +83,7 @@ class Seam * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. headers or proxy. * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. * @param float|null $timeout Request timeout in seconds, covering connecting and reading. Defaults to 30; pass 0 to disable. - * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so no other authentication option applies to it. + * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so it cannot be combined with any option other than wait_for_action_attempt. */ public function __construct( ?string $api_key = null, @@ -100,8 +100,18 @@ public function __construct( "wait_for_action_attempt" => $wait_for_action_attempt ?? true, ]; - // A client carries its own endpoint and authorization, so the - // authentication options are only read when one has to be built. + // A client carries its own endpoint and authorization, so no option + // that would configure one can be combined with it. + Options::check_client_options($client, [ + "api_key" => $api_key, + "personal_access_token" => $personal_access_token, + "workspace_id" => $workspace_id, + "endpoint" => $endpoint, + "guzzle_options" => $guzzle_options, + "retries" => $retries, + "timeout" => $timeout, + ]); + $this->client = $client ?? ClientFactory::create( diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index 0423e5eb..91b9874a 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -37,8 +37,16 @@ public function __construct( ?float $timeout = null, ?ClientInterface $client = null, ) { - // A client carries its own endpoint and authorization, so the - // authentication options are only read when one has to be built. + // A client carries its own endpoint and authorization, so no option + // that would configure one can be combined with it. + Options::check_client_options($client, [ + "personal_access_token" => $personal_access_token, + "endpoint" => $endpoint, + "guzzle_options" => $guzzle_options, + "retries" => $retries, + "timeout" => $timeout, + ]); + $this->client = $client ?? ClientFactory::create( diff --git a/src/WorkspacesProxy.php b/src/WorkspacesProxy.php index 0c596e60..e1d64fb1 100644 --- a/src/WorkspacesProxy.php +++ b/src/WorkspacesProxy.php @@ -38,17 +38,20 @@ public function create( ?string $webview_primary_button_text_color = null, ?string $webview_success_message = null, ): Workspace { + // Forwarded by name: the generated parameter order follows the API + // definition, so a positional call could silently shift after a + // regeneration. return $this->workspaces->create( - $name, - $company_name, - $connect_partner_name, - $connect_webview_customization, - $is_sandbox, - $organization_id, - $webview_logo_shape, - $webview_primary_button_color, - $webview_primary_button_text_color, - $webview_success_message, + name: $name, + company_name: $company_name, + connect_partner_name: $connect_partner_name, + connect_webview_customization: $connect_webview_customization, + is_sandbox: $is_sandbox, + organization_id: $organization_id, + webview_logo_shape: $webview_logo_shape, + webview_primary_button_color: $webview_primary_button_color, + webview_primary_button_text_color: $webview_primary_button_text_color, + webview_success_message: $webview_success_message, ); } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 46c143c2..81e727a5 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -4,10 +4,15 @@ namespace Tests; +use GuzzleHttp\Handler\MockHandler; use Seam\Http\Body; use Seam\Http\ClientFactory; +use Seam\HttpApiError; +use Seam\InvalidOptionsError; use Seam\Seam; +use Seam\SeamWithoutWorkspace; use Tests\Support\FakeSeamConnectTestCase; +use Tests\Support\RecordingClient; final class ClientTest extends FakeSeamConnectTestCase { @@ -69,6 +74,77 @@ public function testClientOptionReusesAnotherInstancesClient(): void $this->assertSame($this->seed["august_device_1"], $device->device_id); } + /** + * A credential passed beside a client would be silently discarded, since + * the client carries its own authorization, so the mix-up is rejected. + */ + public function testClientOptionRejectsAnyOtherOption(): void + { + $client = $this->seam()->client; + + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage( + "The api_key option cannot be used with the client option", + ); + + new Seam(api_key: $this->seed["seam_apikey1_token"], client: $client); + } + + /** + * wait_for_action_attempt does not configure the client, so it is the + * one option a client can be combined with. + */ + public function testClientOptionStillTakesAWaitForActionAttemptDefault(): void + { + $seam = new Seam( + client: $this->seam()->client, + wait_for_action_attempt: false, + ); + + $this->assertFalse($seam->defaults["wait_for_action_attempt"]); + } + + public function testWithoutWorkspaceClientOptionRejectsAnyOtherOption(): void + { + $client = $this->seam()->client; + + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage( + "The endpoint option cannot be used with the client option", + ); + + new SeamWithoutWorkspace(endpoint: $this->endpoint, client: $client); + } + + /** + * A bare handler, such as a MockHandler, is wrapped in a handler stack + * so the error mapping and retries still apply to it. + */ + public function testABareHandlerStillGetsTheErrorMapping(): void + { + $mock = new MockHandler([ + RecordingClient::json(404, [ + "error" => [ + "type" => "device_not_found", + "message" => "Device not found", + ], + ]), + ]); + + $seam = new Seam( + api_key: $this->seed["seam_apikey1_token"], + endpoint: "https://example.com", + guzzle_options: ["handler" => $mock], + ); + + try { + $seam->devices->list(); + $this->fail("Expected the error to be mapped"); + } catch (HttpApiError $error) { + $this->assertSame("device_not_found", $error->getErrorCode()); + } + } + public function testGuzzleOptionsAreMergedIntoTheClient(): void { $seam = $this->seam( diff --git a/tests/PaginatorTest.php b/tests/PaginatorTest.php index 8442318a..4fbd2c39 100644 --- a/tests/PaginatorTest.php +++ b/tests/PaginatorTest.php @@ -56,6 +56,27 @@ public function testNextPageReturnsTheNextPage(): void $this->assertEmpty(array_intersect($first_ids, $second_ids)); } + /** + * The paginator reads the pagination metadata through on_response, but + * must not swallow a callback the caller passed in themselves. + */ + public function testOnResponseParamIsChainedNotReplaced(): void + { + $seen = 0; + + [, $pagination] = $this->paginator([ + "limit" => 2, + "on_response" => function ($response) use (&$seen): void { + $seen++; + $this->assertObjectHasProperty("pagination", $response); + }, + ])->firstPage(); + + $this->assertSame(1, $seen); + // The paginator's own callback has to keep working as well. + $this->assertTrue($pagination->has_next_page); + } + public function testNextPageRequiresACursor(): void { $this->expectException(\InvalidArgumentException::class); From c2602db9ab3490f5400131277b8d82723af2a7e5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 04:09:30 +0000 Subject: [PATCH 15/80] feat: remove the lts_version method It only echoed the LTS_VERSION constant, and its snake_case sat oddly beside createPaginator on the same class. Use Seam::LTS_VERSION. BREAKING CHANGE: lts_version() is removed from Seam and SeamWithoutWorkspace. Read the LTS_VERSION class constant instead. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- codegen/layouts/seam-client.hbs | 5 ----- src/Seam.php | 5 ----- src/SeamWithoutWorkspace.php | 5 ----- tests/ClientTest.php | 1 - 4 files changed, 16 deletions(-) diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index 2f0b0b92..24c40fbd 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -149,11 +149,6 @@ class Seam ); } - public function lts_version(): string - { - return self::LTS_VERSION; - } - /** * Creates a paginator for a list endpoint. * diff --git a/src/Seam.php b/src/Seam.php index 76dcda31..d6109e6b 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -242,11 +242,6 @@ public static function from_client( ); } - public function lts_version(): string - { - return self::LTS_VERSION; - } - /** * Creates a paginator for a list endpoint. * diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index 91b9874a..a75dd841 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -93,9 +93,4 @@ public static function from_client(ClientInterface $client): static { return new static(client: $client); } - - public function lts_version(): string - { - return self::LTS_VERSION; - } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 81e727a5..b289232a 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -244,6 +244,5 @@ public function testWaitForActionAttemptDefaultCanBeDisabled(): void public function testLtsVersionIsExposed(): void { $this->assertSame("1.0.0", Seam::LTS_VERSION); - $this->assertSame("1.0.0", $this->seam()->lts_version()); } } From de21c4d6d5896af0d19014b0e507162d3df89cec Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 04:17:19 +0000 Subject: [PATCH 16/80] test: specify that a 503 on a read should be retried Repeating a read is safe, but every SDK call currently goes over POST, where a status based retry never is, so the SDK's own reads get none. The test asserts today's behavior and marks itself incomplete; once the SDK issues GET for the endpoints that support it, planned for a followup PR, the incomplete branch stops matching and the real assertions take over. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- tests/RetryTest.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/RetryTest.php b/tests/RetryTest.php index c033f905..ba5ea9f8 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -215,6 +215,36 @@ public function testDoesNotRetryWhenRetriesAreDisabled(): void $this->assertSame(1, $recorder->attempt_count()); } + /** + * Repeating a read is safe, so a 503 on one should be retried. + * + * TODO: Every SDK call currently goes over POST, where a status based + * retry is never safe, so the SDK's own reads get none and this test + * marks itself incomplete. Once the SDK issues GET for the endpoints + * that support it, planned for a followup PR, the incomplete branch + * stops matching and the real assertions take over. + */ + public function testSdkReadsAreRetriedOnServiceUnavailable(): void + { + $recorder = new RecordingClient([ + self::service_unavailable(), + self::service_unavailable(), + self::devices(), + ]); + + try { + $devices = $this->seam($recorder)->devices->list(); + } catch (\Throwable) { + $this->assertSame(1, $recorder->attempt_count()); + $this->markTestIncomplete( + "The SDK does not use GET yet, so its reads get no status based retries.", + ); + } + + $this->assertSame([], $devices); + $this->assertSame(3, $recorder->attempt_count()); + } + /** * Building a client must not mutate a handler stack the caller may * reuse: a second client built from the same options would otherwise From 2c9220899692a448b48af9e39aeaa507e13c8f16 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 04:29:05 +0000 Subject: [PATCH 17/80] 4.0.0-beta.1 --- package-lock.json | 4 ++-- package.json | 2 +- src/Utils/PackageVersion.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35c7c127..86c0cac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "3.5.1", + "version": "4.0.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "3.5.1", + "version": "4.0.0-beta.1", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index c1f2a9ed..953c62f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "3.5.1", + "version": "4.0.0-beta.1", "type": "module", "private": true, "license": "MIT", diff --git a/src/Utils/PackageVersion.php b/src/Utils/PackageVersion.php index 66fbf871..fecc898a 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Utils/PackageVersion.php @@ -10,7 +10,7 @@ class PackageVersion * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "3.5.1"; + public const VERSION = "4.0.0-beta.1"; public static function get(): string { From 16d3b5cd3cd3d56654120f7e184922225097081e Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 21:50:30 -0700 Subject: [PATCH 18/80] Fix prune workflow job name --- .github/workflows/prune.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prune.yml b/.github/workflows/prune.yml index 562d16cc..1c484837 100644 --- a/.github/workflows/prune.yml +++ b/.github/workflows/prune.yml @@ -7,7 +7,7 @@ on: - cron: '0 15 * * 3' jobs: - tag: + branches: name: Prune Branches runs-on: 'ubuntu-latest' timeout-minutes: 30 From cecb150c00e80ad39579c8aa789f46c97b6ff871 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 22:38:43 -0700 Subject: [PATCH 19/80] Make retry behavior explicit --- README.md | 14 +-- src/Http/RetryMiddleware.php | 190 +++++++---------------------------- tests/RetryTest.php | 63 ++++++++---- 3 files changed, 84 insertions(+), 183 deletions(-) diff --git a/README.md b/README.md index 313ac7cb..5366cad2 100644 --- a/README.md +++ b/README.md @@ -341,12 +341,14 @@ $seam = new Seam\Seam(timeout: 60.0); #### Retries -Failed requests are retried twice by default, with exponential backoff. - -A request that never reached the server, such as a connection failure, is -always retried, since it cannot have had an effect. A request the server may -already have processed is retried only when repeating it is safe, so a retry -never risks duplicating a write. +By default, the SDK makes up to three attempts: the initial request and two +retries. Retries are limited to `GET`, `HEAD`, `OPTIONS`, `PUT`, and `DELETE` +requests that fail because of a transport error, timeout, HTTP 429 response, or +HTTP 5xx response. `POST` and `PATCH` requests are not retried. + +Retries use exponential backoff with jitter: approximately 200–240 ms before +the first retry and 400–480 ms before the second. A longer `Retry-After` header +is honored. The request timeout is reset for each attempt. ```php // Retry more times diff --git a/src/Http/RetryMiddleware.php b/src/Http/RetryMiddleware.php index 6411a132..792d887f 100644 --- a/src/Http/RetryMiddleware.php +++ b/src/Http/RetryMiddleware.php @@ -2,33 +2,18 @@ namespace Seam\Http; -use GuzzleHttp\Exception\BadResponseException; -use GuzzleHttp\Exception\ConnectException; -use GuzzleHttp\Exception\RequestException; use GuzzleHttp\HandlerStack; -use GuzzleHttp\Promise\Create; -use GuzzleHttp\Promise\PromiseInterface; use GuzzleRetry\GuzzleRetryMiddleware; use Psr\Http\Message\RequestInterface; /** - * Retries failed requests. + * Retries transient failures for idempotent requests. * - * Retrying a request the server may already have processed can duplicate a - * write, so what is retried depends on the method: - * - * - A method that is safe to repeat is retried on a retryable status code, - * and on any transport error. - * - Any other method is only retried on a transport error that occurred - * before the request could have reached the server, such as a failed - * connection. A timeout in particular is not retried, because it may have - * fired while waiting on a response to a request the server received and - * is still processing. + * Guzzle re-applies the request timeout to every retry, so the timeout covers + * each individual attempt rather than the complete sequence of attempts. */ final class RetryMiddleware { - private const RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504]; - private const IDEMPOTENT_METHODS = [ "GET", "HEAD", @@ -37,154 +22,47 @@ final class RetryMiddleware "DELETE", ]; - /** - * The linear backoff applied per attempt already made, matching the - * default of the retry middleware handling the idempotent methods. - */ - private const RETRY_DELAY_SECONDS = 1.5; + private const INITIAL_DELAY_SECONDS = 0.2; - /** - * The curl error for a request that hit a timeout. The timeout may have - * fired while waiting for the response, after the server received the - * request. - */ - private const CURLE_OPERATION_TIMEDOUT = 28; - - /** - * The errno for a connection reset by the peer, which the retry - * middleware below also treats as retryable. - */ - private const ECONNRESET = 104; + private const JITTER_MULTIPLIER = 1.2; public static function add(HandlerStack $handler, int $retries): void { - $handler->push(self::retry_options_middleware(), "seam_retry_options"); $handler->push( GuzzleRetryMiddleware::factory([ "retry_enabled" => $retries > 0, - "max_retry_attempts" => $retries, + "max_retry_attempts" => max(0, $retries), + "retry_on_methods" => self::IDEMPOTENT_METHODS, "retry_on_timeout" => true, - // Set per request by the middleware above. - "retry_on_status" => [], + "retry_on_status" => array_merge([429], range(500, 599)), + // Delay is applied by the callback below so Retry-After can + // be compared with, rather than replace, the jittered delay. + "default_retry_multiplier" => 0.0, + "on_retry_callback" => static function ( + int $retry_count, + float $retry_after, + RequestInterface &$request, + array &$options, + ): void { + $backoff = + self::INITIAL_DELAY_SECONDS * + 2.0 ** (float) ($retry_count - 1); + $jittered_backoff = random_int( + (int) ($backoff * 1000.0), + (int) ( + $backoff * + self::JITTER_MULTIPLIER * + 1000.0 + ), + ); + + $options["delay"] = max( + $jittered_backoff, + (int) ceil($retry_after * 1000.0), + ); + }, ]), "seam_retry", ); - // Pushed last, so it sits inside the middleware above and its - // retries are invisible to it: each mechanism covers the failures - // the other does not, and their budgets never combine. - $handler->push( - self::transport_retry_middleware($retries), - "seam_transport_retry", - ); - } - - /** - * Configures the retry middleware per request: a method that is safe to - * repeat opts into status based retries, any other method opts out of - * its transport error handling, which would retry timeouts as well, and - * is covered by the transport retry middleware instead. - */ - private static function retry_options_middleware(): callable - { - return static fn(callable $handler): callable => static function ( - RequestInterface $request, - array $options, - ) use ($handler): PromiseInterface { - if (self::is_idempotent($request)) { - $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; - } else { - $options["retry_on_timeout"] = false; - } - - return $handler($request, $options); - }; - } - - /** - * Retries the transport errors for the methods that are not safe to - * repeat, skipping anything that looks like a timeout. - */ - private static function transport_retry_middleware(int $retries): callable - { - return static fn( - callable $handler, - ): callable => static fn(RequestInterface $request, array $options): PromiseInterface => self::dispatch($handler, $request, $options, $retries); - } - - private static function dispatch( - callable $handler, - RequestInterface $request, - array $options, - int $retries, - ): PromiseInterface { - return $handler($request, $options)->otherwise(static function ( - \Throwable $reason, - ) use ($handler, $request, $options, $retries): PromiseInterface { - $attempts = $options["seam_transport_retries"] ?? 0; - - if ( - !is_int($attempts) || - $attempts >= $retries || - self::is_idempotent($request) || - !self::is_retryable_transport_error($reason) - ) { - return Create::rejectionFor($reason); - } - - $options["seam_transport_retries"] = $attempts + 1; - $options["delay"] = - (int) (self::RETRY_DELAY_SECONDS * - (float) ($attempts + 1) * - 1000.0); - - return self::dispatch($handler, $request, $options, $retries); - }); - } - - private static function is_retryable_transport_error( - \Throwable $reason, - ): bool { - if ($reason instanceof ConnectException) { - return !self::is_timeout($reason); - } - - // A response was received, so this is a status error covered by the - // retry middleware, not a transport error. - if ($reason instanceof BadResponseException) { - return false; - } - - if ($reason instanceof RequestException) { - $errno = $reason->getHandlerContext()["errno"] ?? null; - - return $errno === self::ECONNRESET; - } - - return false; - } - - /** - * Whether a connection exception is a timeout. The curl errno is - * decisive when present; a handler without one, such as the stream - * handler, is judged by its message. - */ - private static function is_timeout(ConnectException $reason): bool - { - $errno = $reason->getHandlerContext()["errno"] ?? null; - - if ($errno !== null) { - return $errno === self::CURLE_OPERATION_TIMEDOUT; - } - - return preg_match("/timed?\s*out/i", $reason->getMessage()) === 1; - } - - private static function is_idempotent(RequestInterface $request): bool - { - return in_array( - strtoupper($request->getMethod()), - self::IDEMPOTENT_METHODS, - true, - ); } } diff --git a/tests/RetryTest.php b/tests/RetryTest.php index ba5ea9f8..41f3cec0 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -61,26 +61,26 @@ public function testDoesNotRetryPostOnServiceUnavailable(): void } /** - * A connection failure never reached the server, so retrying it cannot - * duplicate anything. + * Non-idempotent methods are never retried, even when the failure is + * known to have happened before the request reached the server. */ - public function testRetriesPostOnConnectionFailure(): void + public function testDoesNotRetryPostOnConnectionFailure(): void { $connect_error = new ConnectException( "Could not resolve host", new Request("POST", "/devices/list"), ); - $recorder = new RecordingClient([ - $connect_error, - $connect_error, - self::devices(), - ]); + $recorder = RecordingClient::repeating_throwable($connect_error); - $devices = $this->seam($recorder)->devices->list(); + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the connection failure to surface"); + } catch (ConnectException) { + // Expected. + } - $this->assertSame([], $devices); - $this->assertSame(3, $recorder->attempt_count()); + $this->assertSame(1, $recorder->attempt_count()); } /** @@ -135,6 +135,8 @@ public function testDoesNotRetryPostOnTimeoutWithoutAnErrno(): void /** * Repeating an idempotent request is safe even when the server may have * received it, so a timeout does get retried there. + * + * TODO: Use the SDK route once generated reads use GET instead of POST. */ public function testRetriesIdempotentRequestsOnTimeout(): void { @@ -156,11 +158,10 @@ public function testRetriesIdempotentRequestsOnTimeout(): void } /** - * A connection reset by the peer surfaces as a request exception with - * errno 104 rather than a connection exception, but is a transport - * failure all the same. + * A connection reset is a transport error, but POST is still not safe to + * repeat. */ - public function testRetriesPostOnConnectionReset(): void + public function testDoesNotRetryPostOnConnectionReset(): void { $reset = new RequestException( "Connection reset by peer", @@ -170,14 +171,21 @@ public function testRetriesPostOnConnectionReset(): void ["errno" => 104], ); - $recorder = new RecordingClient([$reset, self::devices()]); + $recorder = RecordingClient::repeating_throwable($reset); - $devices = $this->seam($recorder)->devices->list(); + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the connection reset to surface"); + } catch (RequestException) { + // Expected. + } - $this->assertSame([], $devices); - $this->assertSame(2, $recorder->attempt_count()); + $this->assertSame(1, $recorder->attempt_count()); } + /** + * TODO: Use the SDK route once generated reads use GET instead of POST. + */ public function testStopsRetryingOnceRetriesAreExhausted(): void { $connect_error = new ConnectException( @@ -190,12 +198,18 @@ public function testStopsRetryingOnceRetriesAreExhausted(): void $this->expectException(ConnectException::class); try { - $this->seam($recorder, retries: 1)->devices->list(); + $this->seam($recorder, retries: 1)->client->request( + "GET", + "/devices/list", + ); } finally { $this->assertSame(2, $recorder->attempt_count()); } } + /** + * TODO: Use the SDK route once generated reads use GET instead of POST. + */ public function testDoesNotRetryWhenRetriesAreDisabled(): void { $connect_error = new ConnectException( @@ -206,7 +220,10 @@ public function testDoesNotRetryWhenRetriesAreDisabled(): void $recorder = RecordingClient::repeating_throwable($connect_error); try { - $this->seam($recorder, retries: 0)->devices->list(); + $this->seam($recorder, retries: 0)->client->request( + "GET", + "/devices/list", + ); $this->fail("Expected the connection failure to surface"); } catch (ConnectException) { // Expected. @@ -249,6 +266,8 @@ public function testSdkReadsAreRetriedOnServiceUnavailable(): void * Building a client must not mutate a handler stack the caller may * reuse: a second client built from the same options would otherwise * stack the retry middleware twice and multiply the retries. + * + * TODO: Use the SDK route once generated reads use GET instead of POST. */ public function testBuildingASecondClientDoesNotStackRetries(): void { @@ -281,6 +300,8 @@ public function testBuildingASecondClientDoesNotStackRetries(): void /** * The SDK itself only issues POSTs, but a caller reaching for the client * directly with an idempotent method does get status based retries. + * + * TODO: Use the SDK route once generated reads use GET instead of POST. */ public function testRetriesIdempotentRequestsOnServiceUnavailable(): void { From c410de55408359ba98a3ef7963e186de4554abf5 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 05:39:40 +0000 Subject: [PATCH 20/80] ci: Format code --- src/Http/RetryMiddleware.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Http/RetryMiddleware.php b/src/Http/RetryMiddleware.php index 792d887f..7d32f4b7 100644 --- a/src/Http/RetryMiddleware.php +++ b/src/Http/RetryMiddleware.php @@ -49,11 +49,7 @@ public static function add(HandlerStack $handler, int $retries): void 2.0 ** (float) ($retry_count - 1); $jittered_backoff = random_int( (int) ($backoff * 1000.0), - (int) ( - $backoff * - self::JITTER_MULTIPLIER * - 1000.0 - ), + (int) ($backoff * self::JITTER_MULTIPLIER * 1000.0), ); $options["delay"] = max( From 93490e0ffa20376ec2e59cb5011fee639269c204 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:19:46 -0700 Subject: [PATCH 21/80] Update blueprint and types --- package-lock.json | 22 +++++++++++----------- package.json | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 86c0cac8..da2a2607 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,10 @@ "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.2.0", + "@seamapi/blueprint": "^1.5.1", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.985.0", + "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", "execa": "^10.0.1", "prettier": "^3.9.6" @@ -806,9 +806,9 @@ "license": "MIT" }, "node_modules/@seamapi/blueprint": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.2.0.tgz", - "integrity": "sha512-+fomtVD/VGJavEpOtVZD+3ZxUYgTXAS4DreNqbsNl0R1qS0wiIdJ40AmE9dgLnY1cHCfPNBmf1z//FdvCqHhrw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.5.1.tgz", + "integrity": "sha512-rXHMNDGmaE7OK+tg3e64DnpMHQypN4BuSS+PkVqGUoYc9f+xBMONqPscsHAGi9iU/iyZOJZKEOKOXvMmWaLoLA==", "dev": true, "license": "MIT", "dependencies": { @@ -816,7 +816,7 @@ "zod": "^3.23.8" }, "engines": { - "node": ">=22.11.0", + "node": ">=22.12.0", "npm": ">=10.0.0" } }, @@ -890,14 +890,14 @@ } }, "node_modules/@seamapi/types": { - "version": "1.985.0", - "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.985.0.tgz", - "integrity": "sha512-3+aFZXav6zQ9mPsl6FLR0TuMBbu3u3kC16OoEjNHZbTASj1eOYTAjb2lUR8VjFAaR1iZi/rNcIPBRfX/xaWbfg==", + "version": "1.1001.0", + "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.1001.0.tgz", + "integrity": "sha512-pwIEqMYCdOLlIHUzzLlMq/4K6QwAM3kWXooSxNWOlPyCeWk21SvrbTN/joXwZtky504g3unLPKMbFad1mlQyfQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=22.11.0", - "npm": ">=10.9.4" + "node": ">=22.12.0", + "npm": ">=10.0.0" }, "peerDependencies": { "zod": "^3.24.0" diff --git a/package.json b/package.json index 953c62f4..2bce7fc9 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,10 @@ }, "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.2.0", + "@seamapi/blueprint": "^1.5.1", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.985.0", + "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", "execa": "^10.0.1", "prettier": "^3.9.6" From 97f655d4ae4a235517a0da5e3d3ea88e2f8ce607 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:20:30 +0000 Subject: [PATCH 22/80] ci: Generate code --- src/Resources/AcsCredential.php | 10 ++++ src/Resources/ActionAttempt.php | 20 ++++++++ src/Resources/Device.php | 66 +++++++++++++++++++++++++-- src/Resources/Event.php | 13 +++--- src/Resources/UnmanagedDevice.php | 5 ++ src/Routes/AccessCodesClient.php | 43 ----------------- src/Routes/DevicesUnmanagedClient.php | 20 -------- src/Routes/LocksClient.php | 56 ----------------------- src/Routes/NoiseSensorsClient.php | 56 ----------------------- src/Routes/ThermostatsClient.php | 56 ----------------------- 10 files changed, 104 insertions(+), 241 deletions(-) diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index f62fceda..a63289f1 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -367,6 +367,8 @@ public static function from_json(mixed $json): Warnings|null return new self( created_at: $json->created_at ?? null, message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, warning_code: $json->warning_code ?? null, ); } @@ -380,6 +382,14 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * The PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that could not be used. + */ + public string|null $original_code, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 2af52ae4..04af6d8c 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -762,8 +762,10 @@ public static function from_json(mixed $json): Warnings|null return new self( created_at: $json->created_at ?? null, message: $json->message ?? null, + new_code: $json->new_code ?? null, original_access_method_id: $json->original_access_method_id ?? null, + original_code: $json->original_code ?? null, warning_code: $json->warning_code ?? null, warning_message: $json->warning_message ?? null, ); @@ -778,10 +780,18 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * The PIN code that was assigned instead. + */ + public string|null $new_code, /** * ID of the original access method from which this backup access method was split, if applicable. */ public string|null $original_access_method_id, + /** + * The originally requested PIN code that could not be used. + */ + public string|null $original_code, public string|null $warning_code, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. @@ -1042,6 +1052,8 @@ public static function from_json(mixed $json): Warnings|null return new self( created_at: $json->created_at ?? null, message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, warning_code: $json->warning_code ?? null, ); } @@ -1055,6 +1067,14 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * The PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that could not be used. + */ + public string|null $original_code, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/Device.php b/src/Resources/Device.php index 9b01f398..e7d7964e 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -358,6 +358,7 @@ public static function from_json(mixed $json): Location|null } return new self( location_name: $json->location_name ?? null, + room_name: $json->room_name ?? null, time_zone: $json->time_zone ?? null, timezone: $json->timezone ?? null, ); @@ -368,6 +369,10 @@ public function __construct( * Name of the device location. */ public string|null $location_name, + /** + * Name of the room within the device location, when the provider reports one. + */ + public string|null $room_name, /** * Time zone of the device location. */ @@ -714,6 +719,9 @@ public static function from_json(mixed $json): Properties|null wyze_metadata: isset($json->wyze_metadata) ? Properties\WyzeMetadata::from_json($json->wyze_metadata) : null, + yacan_metadata: isset($json->yacan_metadata) + ? Properties\YacanMetadata::from_json($json->yacan_metadata) + : null, ); } @@ -1049,7 +1057,7 @@ public function __construct( /** * Metada for a Salto device. * - * @deprecated Use `salto_ks_metadata ` instead. + * @deprecated Use `salto_ks_metadata` instead. */ public Properties\SaltoMetadata|null $salto_metadata, /** @@ -1144,6 +1152,10 @@ public function __construct( * Metadata for a Wyze device. */ public Properties\WyzeMetadata|null $wyze_metadata, + /** + * Metadata for a Yacan device. + */ + public Properties\YacanMetadata|null $yacan_metadata, ) {} } @@ -1709,7 +1721,7 @@ public function __construct( /** * Device ID for a dormakaba Oracode device. */ - public mixed $device_id, + public string|null $device_id, /** * Door ID for a dormakaba Oracode device. */ @@ -2279,6 +2291,8 @@ public static function from_json(mixed $json): NestMetadata|null device_name: $json->device_name ?? null, display_name: $json->display_name ?? null, nest_device_id: $json->nest_device_id ?? null, + nest_structure_id: $json->nest_structure_id ?? null, + structure_name: $json->structure_name ?? null, ); } @@ -2299,6 +2313,14 @@ public function __construct( * Device ID for a Google Nest device. */ public string|null $nest_device_id, + /** + * ID of the Google Nest structure containing the device. + */ + public string|null $nest_structure_id, + /** + * Name of the Google Nest structure containing the device. The device owner sets this value. + */ + public string|null $structure_name, ) {} } @@ -2536,7 +2558,7 @@ public function __construct( /** * Metada for a Salto device. * - * @deprecated Use `salto_ks_metadata ` instead. + * @deprecated Use `salto_ks_metadata` instead. */ class SaltoMetadata { @@ -3026,6 +3048,44 @@ public function __construct( ) {} } + /** + * Metadata for a Yacan device. + */ + class YacanMetadata + { + public static function from_json(mixed $json): YacanMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + device_type: $json->device_type ?? null, + serial_number: $json->serial_number ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Yacan device. + */ + public string|null $device_id, + /** + * Device name for a Yacan device. + */ + public string|null $device_name, + /** + * Device type for a Yacan device. + */ + public string|null $device_type, + /** + * Serial number for a Yacan device. + */ + public string|null $serial_number, + ) {} + } + /** * 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. */ diff --git a/src/Resources/Event.php b/src/Resources/Event.php index f11c863e..73a16b05 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -61,7 +61,6 @@ public static function from_json(mixed $json): Event|null $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_type: $json->connected_account_type ?? null, connected_account_warnings: array_map( fn($c) => Event\ConnectedAccountWarnings::from_json($c), $json->connected_account_warnings ?? [], @@ -139,7 +138,6 @@ public static function from_json(mixed $json): Event|null to: isset($json->to) ? Event\To::from_json($json->to) : null, upper_limit_celsius: $json->upper_limit_celsius ?? null, upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, - user_identity_id: $json->user_identity_id ?? null, video_url: $json->video_url ?? null, workspace_id: $json->workspace_id ?? null, ); @@ -197,11 +195,17 @@ public function __construct( * Errors associated with the access control system. */ public array $acs_system_errors, + /** + * ID of the access system. + */ public string|null $acs_system_id, /** * Warnings associated with the access control system. */ public array $acs_system_warnings, + /** + * ID of the affected access system user. + */ public string|null $acs_user_id, public string|null $action_attempt_id, /** @@ -248,10 +252,6 @@ public function __construct( */ public array $connected_account_errors, public string|null $connected_account_id, - /** - * undocumented: Unreleased. - */ - public string|null $connected_account_type, /** * Warnings associated with the connected account. */ @@ -435,7 +435,6 @@ public function __construct( * Upper temperature limit, in °F, defined by the set threshold. */ public float|null $upper_limit_fahrenheit, - public string|null $user_identity_id, public string|null $video_url, /** * ID of the workspace associated with the event. diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 25d35b1e..87649de0 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -256,6 +256,7 @@ public static function from_json(mixed $json): Location|null } return new self( location_name: $json->location_name ?? null, + room_name: $json->room_name ?? null, time_zone: $json->time_zone ?? null, timezone: $json->timezone ?? null, ); @@ -266,6 +267,10 @@ public function __construct( * Name of the device location. */ public string|null $location_name, + /** + * Name of the room within the device location, when the provider reports one. + */ + public string|null $room_name, /** * Time zone of the device location. */ diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 3c446642..e0dc3632 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -495,9 +495,6 @@ public function report_device_constraints( * @param string $ends_at Date and time at which the validity of the new access code ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. * @param bool $is_external_modification_allowed Indicates whether [external modification](https://docs.seam.co/low-level-apis/smart-locks/access-codes#external-modification) of the code is allowed. Default: `false`. * @param bool $is_managed Indicates whether the access code is managed through Seam. Note that to convert an unmanaged access code into a managed access code, use `/access_codes/unmanaged/convert_to_managed`. - * @param bool $is_offline_access_code Indicates whether the access code is an [offline access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/offline-access-codes). - * @param bool $is_one_time_use Indicates whether the [offline access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/offline-access-codes) is a single-use access code. - * @param string $max_time_rounding Maximum rounding adjustment. To create a daily-bound [offline access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/offline-access-codes) for devices that support this feature, set this parameter to `1d`. * @param string $name Name of the new access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. @@ -505,12 +502,8 @@ public function report_device_constraints( To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). - * @param bool $prefer_native_scheduling Indicates whether [native scheduling](https://docs.seam.co/low-level-apis/smart-locks/access-codes#native-scheduling) should be used for time-bound codes when supported by the provider. Default: `true`. - * @param float $preferred_code_length Preferred code length. Only applicable if you do not specify a `code`. If the affected device does not support the preferred code length, Seam reverts to using the shortest supported code length. * @param string $starts_at Date and time at which the validity of the new access code starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @param string $type Type to which you want to convert the access code. To convert a time-bound access code to an ongoing access code, set `type` to `ongoing`. See also [Changing a time-bound access code to permanent access](https://docs.seam.co/low-level-apis/smart-locks/access-codes/modifying-access-codes#special-case-2-changing-a-time-bound-access-code-to-permanent-access). - * @param bool $use_backup_access_code_pool Indicates whether to use a [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) provided by Seam. If `true`, you can use [`/access_codes/pull_backup_access_code`](https://docs.seam.co/api/access_codes/pull_backup_access_code). - * @param bool $use_offline_access_code * @return void OK */ public function update( @@ -522,16 +515,9 @@ public function update( ?string $ends_at = null, ?bool $is_external_modification_allowed = null, ?bool $is_managed = null, - ?bool $is_offline_access_code = null, - ?bool $is_one_time_use = null, - ?string $max_time_rounding = null, ?string $name = null, - ?bool $prefer_native_scheduling = null, - ?float $preferred_code_length = null, ?string $starts_at = null, ?string $type = null, - ?bool $use_backup_access_code_pool = null, - ?bool $use_offline_access_code = null, ): void { $request_payload = []; @@ -563,44 +549,15 @@ public function update( if ($is_managed !== null) { $request_payload["is_managed"] = $is_managed; } - if ($is_offline_access_code !== null) { - $request_payload[ - "is_offline_access_code" - ] = $is_offline_access_code; - } - if ($is_one_time_use !== null) { - $request_payload["is_one_time_use"] = $is_one_time_use; - } - if ($max_time_rounding !== null) { - $request_payload["max_time_rounding"] = $max_time_rounding; - } if ($name !== null) { $request_payload["name"] = $name; } - if ($prefer_native_scheduling !== null) { - $request_payload[ - "prefer_native_scheduling" - ] = $prefer_native_scheduling; - } - if ($preferred_code_length !== null) { - $request_payload["preferred_code_length"] = $preferred_code_length; - } if ($starts_at !== null) { $request_payload["starts_at"] = $starts_at; } if ($type !== null) { $request_payload["type"] = $type; } - if ($use_backup_access_code_pool !== null) { - $request_payload[ - "use_backup_access_code_pool" - ] = $use_backup_access_code_pool; - } - if ($use_offline_access_code !== null) { - $request_payload[ - "use_offline_access_code" - ] = $use_offline_access_code; - } $this->client->request("POST", "/access_codes/update", [ "json" => (object) $request_payload, diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 9b2007f4..1d10eb6f 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -66,7 +66,6 @@ public function get( * @param string $connected_account_id ID of the connected account for which you want to list devices. * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type for which you want to list devices. @@ -75,9 +74,6 @@ public function get( * @param string $manufacturer Manufacturer for which you want to list devices. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. - * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id - * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ @@ -86,7 +82,6 @@ public function list( ?string $connected_account_id = null, ?array $connected_account_ids = null, ?string $created_before = null, - mixed $custom_metadata_has = null, ?string $customer_key = null, ?array $device_ids = null, ?string $device_type = null, @@ -95,9 +90,6 @@ public function list( ?string $manufacturer = null, ?string $page_cursor = null, ?string $search = null, - ?string $space_id = null, - ?string $unstable_location_id = null, - ?string $user_identifier_key = null, ?callable $on_response = null, ): array { $request_payload = []; @@ -114,9 +106,6 @@ public function list( if ($created_before !== null) { $request_payload["created_before"] = $created_before; } - if ($custom_metadata_has !== null) { - $request_payload["custom_metadata_has"] = $custom_metadata_has; - } if ($customer_key !== null) { $request_payload["customer_key"] = $customer_key; } @@ -141,15 +130,6 @@ public function list( if ($search !== null) { $request_payload["search"] = $search; } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } - if ($unstable_location_id !== null) { - $request_payload["unstable_location_id"] = $unstable_location_id; - } - if ($user_identifier_key !== null) { - $request_payload["user_identifier_key"] = $user_identifier_key; - } $res = Body::decode( $this->client->request("POST", "/devices/unmanaged/list", [ diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index c4b2c7b4..5f12b842 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -99,41 +99,19 @@ public function get(?string $device_id = null, ?string $name = null): Device * * @param string $connect_webview_id ID of the Connect Webview for which you want to list devices. * @param string $connected_account_id ID of the connected account for which you want to list devices. - * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. - * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. - * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type of the locks that you want to list. * @param array $device_types Device types of the locks that you want to list. - * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturer of the locks that you want to list. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. - * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. - * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id - * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. - * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( ?string $connect_webview_id = null, ?string $connected_account_id = null, - ?array $connected_account_ids = null, - ?string $created_before = null, - mixed $custom_metadata_has = null, ?string $customer_key = null, - ?array $device_ids = null, ?string $device_type = null, ?array $device_types = null, - ?float $limit = null, ?string $manufacturer = null, - ?string $page_cursor = null, - ?string $search = null, - ?string $space_id = null, - ?string $unstable_location_id = null, - ?string $user_identifier_key = null, - ?callable $on_response = null, ): array { $request_payload = []; @@ -143,48 +121,18 @@ public function list( if ($connected_account_id !== null) { $request_payload["connected_account_id"] = $connected_account_id; } - if ($connected_account_ids !== null) { - $request_payload["connected_account_ids"] = $connected_account_ids; - } - if ($created_before !== null) { - $request_payload["created_before"] = $created_before; - } - if ($custom_metadata_has !== null) { - $request_payload["custom_metadata_has"] = $custom_metadata_has; - } if ($customer_key !== null) { $request_payload["customer_key"] = $customer_key; } - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } if ($device_type !== null) { $request_payload["device_type"] = $device_type; } if ($device_types !== null) { $request_payload["device_types"] = $device_types; } - if ($limit !== null) { - $request_payload["limit"] = $limit; - } if ($manufacturer !== null) { $request_payload["manufacturer"] = $manufacturer; } - if ($page_cursor !== null) { - $request_payload["page_cursor"] = $page_cursor; - } - if ($search !== null) { - $request_payload["search"] = $search; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } - if ($unstable_location_id !== null) { - $request_payload["unstable_location_id"] = $unstable_location_id; - } - if ($user_identifier_key !== null) { - $request_payload["user_identifier_key"] = $user_identifier_key; - } $res = Body::decode( $this->client->request("POST", "/locks/list", [ @@ -192,10 +140,6 @@ public function list( ]), ); - if ($on_response !== null) { - $on_response($res); - } - return array_map(fn($r) => Device::from_json($r), $res->devices); } diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index 59025cba..b1450aea 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -35,41 +35,19 @@ public function __construct(ClientInterface $client, array $defaults) * * @param string $connect_webview_id ID of the Connect Webview for which you want to list devices. * @param string $connected_account_id ID of the connected account for which you want to list devices. - * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. - * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. - * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type of the noise sensors that you want to list. * @param array $device_types Device types of the noise sensors that you want to list. - * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturers of the noise sensors that you want to list. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. - * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. - * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id - * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. - * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( ?string $connect_webview_id = null, ?string $connected_account_id = null, - ?array $connected_account_ids = null, - ?string $created_before = null, - mixed $custom_metadata_has = null, ?string $customer_key = null, - ?array $device_ids = null, ?string $device_type = null, ?array $device_types = null, - ?float $limit = null, ?string $manufacturer = null, - ?string $page_cursor = null, - ?string $search = null, - ?string $space_id = null, - ?string $unstable_location_id = null, - ?string $user_identifier_key = null, - ?callable $on_response = null, ): array { $request_payload = []; @@ -79,48 +57,18 @@ public function list( if ($connected_account_id !== null) { $request_payload["connected_account_id"] = $connected_account_id; } - if ($connected_account_ids !== null) { - $request_payload["connected_account_ids"] = $connected_account_ids; - } - if ($created_before !== null) { - $request_payload["created_before"] = $created_before; - } - if ($custom_metadata_has !== null) { - $request_payload["custom_metadata_has"] = $custom_metadata_has; - } if ($customer_key !== null) { $request_payload["customer_key"] = $customer_key; } - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } if ($device_type !== null) { $request_payload["device_type"] = $device_type; } if ($device_types !== null) { $request_payload["device_types"] = $device_types; } - if ($limit !== null) { - $request_payload["limit"] = $limit; - } if ($manufacturer !== null) { $request_payload["manufacturer"] = $manufacturer; } - if ($page_cursor !== null) { - $request_payload["page_cursor"] = $page_cursor; - } - if ($search !== null) { - $request_payload["search"] = $search; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } - if ($unstable_location_id !== null) { - $request_payload["unstable_location_id"] = $unstable_location_id; - } - if ($user_identifier_key !== null) { - $request_payload["user_identifier_key"] = $user_identifier_key; - } $res = Body::decode( $this->client->request("POST", "/noise_sensors/list", [ @@ -128,10 +76,6 @@ public function list( ]), ); - if ($on_response !== null) { - $on_response($res); - } - return array_map(fn($r) => Device::from_json($r), $res->devices); } } diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index c73d00de..a1dfbc5d 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -318,41 +318,19 @@ public function heat_cool( * * @param string $connect_webview_id ID of the Connect Webview for which you want to list devices. * @param string $connected_account_id ID of the connected account for which you want to list devices. - * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. - * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. - * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type by which you want to filter thermostat devices. * @param array $device_types Array of device types by which you want to filter thermostat devices. - * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturer by which you want to filter thermostat devices. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. - * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. - * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id - * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. - * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( ?string $connect_webview_id = null, ?string $connected_account_id = null, - ?array $connected_account_ids = null, - ?string $created_before = null, - mixed $custom_metadata_has = null, ?string $customer_key = null, - ?array $device_ids = null, ?string $device_type = null, ?array $device_types = null, - ?float $limit = null, ?string $manufacturer = null, - ?string $page_cursor = null, - ?string $search = null, - ?string $space_id = null, - ?string $unstable_location_id = null, - ?string $user_identifier_key = null, - ?callable $on_response = null, ): array { $request_payload = []; @@ -362,48 +340,18 @@ public function list( if ($connected_account_id !== null) { $request_payload["connected_account_id"] = $connected_account_id; } - if ($connected_account_ids !== null) { - $request_payload["connected_account_ids"] = $connected_account_ids; - } - if ($created_before !== null) { - $request_payload["created_before"] = $created_before; - } - if ($custom_metadata_has !== null) { - $request_payload["custom_metadata_has"] = $custom_metadata_has; - } if ($customer_key !== null) { $request_payload["customer_key"] = $customer_key; } - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } if ($device_type !== null) { $request_payload["device_type"] = $device_type; } if ($device_types !== null) { $request_payload["device_types"] = $device_types; } - if ($limit !== null) { - $request_payload["limit"] = $limit; - } if ($manufacturer !== null) { $request_payload["manufacturer"] = $manufacturer; } - if ($page_cursor !== null) { - $request_payload["page_cursor"] = $page_cursor; - } - if ($search !== null) { - $request_payload["search"] = $search; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } - if ($unstable_location_id !== null) { - $request_payload["unstable_location_id"] = $unstable_location_id; - } - if ($user_identifier_key !== null) { - $request_payload["user_identifier_key"] = $user_identifier_key; - } $res = Body::decode( $this->client->request("POST", "/thermostats/list", [ @@ -411,10 +359,6 @@ public function list( ]), ); - if ($on_response !== null) { - $on_response($res); - } - return array_map(fn($r) => Device::from_json($r), $res->devices); } From b60e4c15cac2f969585e6ed43f317e1d9ccd74a7 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:23:49 -0700 Subject: [PATCH 23/80] Use blueprint preferred HTTP methods --- codegen/layouts/partials/route-method.hbs | 2 +- codegen/lib/class-model.ts | 1 + codegen/lib/layouts/route.ts | 2 ++ codegen/lib/routes.ts | 1 + src/Http/ResolveActionAttempt.php | 2 +- src/Routes/AccessCodesClient.php | 12 ++++---- src/Routes/AccessCodesUnmanagedClient.php | 10 +++---- src/Routes/AccessGrantsClient.php | 6 ++-- src/Routes/AccessGrantsUnmanagedClient.php | 6 ++-- src/Routes/AccessMethodsClient.php | 6 ++-- src/Routes/AccessMethodsUnmanagedClient.php | 4 +-- src/Routes/AcsAccessGroupsClient.php | 14 +++++----- src/Routes/AcsCredentialsClient.php | 14 +++++----- src/Routes/AcsEncodersClient.php | 2 +- src/Routes/AcsEntrancesClient.php | 2 +- src/Routes/AcsSystemsClient.php | 6 ++-- src/Routes/AcsUsersClient.php | 22 +++++++++------ src/Routes/ActionAttemptsClient.php | 2 +- src/Routes/ClientSessionsClient.php | 10 +++---- src/Routes/ConnectWebviewsClient.php | 4 +-- src/Routes/ConnectedAccountsClient.php | 6 ++-- src/Routes/DevicesClient.php | 6 ++-- src/Routes/DevicesUnmanagedClient.php | 4 +-- src/Routes/EventsClient.php | 2 +- src/Routes/InstantKeysClient.php | 6 ++-- src/Routes/LocksClient.php | 2 +- .../NoiseSensorsNoiseThresholdsClient.php | 8 +++--- src/Routes/PhonesClient.php | 6 ++-- src/Routes/SpacesClient.php | 16 +++++------ src/Routes/ThermostatsClient.php | 6 ++-- src/Routes/ThermostatsDailyProgramsClient.php | 4 +-- src/Routes/ThermostatsSchedulesClient.php | 8 +++--- src/Routes/UserIdentitiesClient.php | 28 +++++++++---------- src/Routes/UserIdentitiesUnmanagedClient.php | 6 ++-- src/Routes/WebhooksClient.php | 8 +++--- src/Routes/WorkspacesClient.php | 6 ++-- 36 files changed, 128 insertions(+), 122 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 95efcd7d..cf40c56b 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -15,7 +15,7 @@ {{/if}} {{#unless returnsVoid}}$res = Body::decode({{/unless}}$this->client->request( - "POST", + "{{httpMethod}}", "{{path}}", {{#if hasParams}} ["json" => (object) $request_payload], diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index 93356d5c..ab9f8022 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -11,6 +11,7 @@ export interface PhpClientMethodParameter { export interface PhpClientMethod { methodName: string + httpMethod: string path: string description: string responseDescription: string diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 8450dff8..de8c4693 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -15,6 +15,7 @@ const resourcesNamespace = 'Seam\\Resources' export interface MethodLayoutContext { methodName: string + httpMethod: string description: string responseDescription: string isDeprecated: boolean @@ -113,6 +114,7 @@ const getMethodLayoutContext = ( return { methodName, + httpMethod: method.httpMethod, description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index e49bb63b..60deb72a 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -121,6 +121,7 @@ const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { return { methodName: endpoint.name, + httpMethod: endpoint.request.preferredMethod, path: endpoint.path, description: endpoint.description, responseDescription: response.description, diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index b0bb60fd..3d51c6c1 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -78,7 +78,7 @@ private static function get_action_attempt( string $action_attempt_id, ): ActionAttempt { $res = Body::decode( - $client->request("POST", "/action_attempts/get", [ + $client->request("GET", "/action_attempts/get", [ "json" => (object) ["action_attempt_id" => $action_attempt_id], ]), ); diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index e0dc3632..31d04bc3 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -241,7 +241,7 @@ public function create_multiple( } $res = Body::decode( - $this->client->request("POST", "/access_codes/create_multiple", [ + $this->client->request("PUT", "/access_codes/create_multiple", [ "json" => (object) $request_payload, ]), ); @@ -270,7 +270,7 @@ public function delete( $request_payload["device_id"] = $device_id; } - $this->client->request("POST", "/access_codes/delete", [ + $this->client->request("DELETE", "/access_codes/delete", [ "json" => (object) $request_payload, ]); } @@ -288,7 +288,7 @@ public function generate_code(string $device_id): AccessCode $request_payload["device_id"] = $device_id; $res = Body::decode( - $this->client->request("POST", "/access_codes/generate_code", [ + $this->client->request("GET", "/access_codes/generate_code", [ "json" => (object) $request_payload, ]), ); @@ -324,7 +324,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/access_codes/get", [ + $this->client->request("GET", "/access_codes/get", [ "json" => (object) $request_payload, ]), ); @@ -559,7 +559,7 @@ public function update( $request_payload["type"] = $type; } - $this->client->request("POST", "/access_codes/update", [ + $this->client->request("PUT", "/access_codes/update", [ "json" => (object) $request_payload, ]); } @@ -602,7 +602,7 @@ public function update_multiple( $request_payload["starts_at"] = $starts_at; } - $this->client->request("POST", "/access_codes/update_multiple", [ + $this->client->request("PATCH", "/access_codes/update_multiple", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 75a59b15..e823dc0c 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -61,7 +61,7 @@ public function convert_to_managed( } $this->client->request( - "POST", + "PATCH", "/access_codes/unmanaged/convert_to_managed", ["json" => (object) $request_payload], ); @@ -79,7 +79,7 @@ public function delete(string $access_code_id): void $request_payload["access_code_id"] = $access_code_id; - $this->client->request("POST", "/access_codes/unmanaged/delete", [ + $this->client->request("DELETE", "/access_codes/unmanaged/delete", [ "json" => (object) $request_payload, ]); } @@ -112,7 +112,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/access_codes/unmanaged/get", [ + $this->client->request("GET", "/access_codes/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -156,7 +156,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_codes/unmanaged/list", [ + $this->client->request("GET", "/access_codes/unmanaged/list", [ "json" => (object) $request_payload, ]), ); @@ -206,7 +206,7 @@ public function update( ] = $is_external_modification_allowed; } - $this->client->request("POST", "/access_codes/unmanaged/update", [ + $this->client->request("PATCH", "/access_codes/unmanaged/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index e6a8a8b8..2ec4a198 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -134,7 +134,7 @@ public function delete(string $access_grant_id): void $request_payload["access_grant_id"] = $access_grant_id; - $this->client->request("POST", "/access_grants/delete", [ + $this->client->request("DELETE", "/access_grants/delete", [ "json" => (object) $request_payload, ]); } @@ -160,7 +160,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/access_grants/get", [ + $this->client->request("GET", "/access_grants/get", [ "json" => (object) $request_payload, ]), ); @@ -364,7 +364,7 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->client->request("POST", "/access_grants/update", [ + $this->client->request("PATCH", "/access_grants/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index dfe5df2a..06d2974c 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -37,7 +37,7 @@ public function get(string $access_grant_id): UnmanagedAccessGrant $request_payload["access_grant_id"] = $access_grant_id; $res = Body::decode( - $this->client->request("POST", "/access_grants/unmanaged/get", [ + $this->client->request("GET", "/access_grants/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -88,7 +88,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_grants/unmanaged/list", [ + $this->client->request("GET", "/access_grants/unmanaged/list", [ "json" => (object) $request_payload, ]), ); @@ -128,7 +128,7 @@ public function update( $request_payload["access_grant_key"] = $access_grant_key; } - $this->client->request("POST", "/access_grants/unmanaged/update", [ + $this->client->request("PATCH", "/access_grants/unmanaged/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index a038f4f9..e972d500 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -85,7 +85,7 @@ public function delete( $request_payload["reservation_key"] = $reservation_key; } - $this->client->request("POST", "/access_methods/delete", [ + $this->client->request("DELETE", "/access_methods/delete", [ "json" => (object) $request_payload, ]); } @@ -135,7 +135,7 @@ public function get(string $access_method_id): AccessMethod $request_payload["access_method_id"] = $access_method_id; $res = Body::decode( - $this->client->request("POST", "/access_methods/get", [ + $this->client->request("GET", "/access_methods/get", [ "json" => (object) $request_payload, ]), ); @@ -228,7 +228,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_methods/list", [ + $this->client->request("GET", "/access_methods/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 0aaa359c..3f958727 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -37,7 +37,7 @@ public function get(string $access_method_id): UnmanagedAccessMethod $request_payload["access_method_id"] = $access_method_id; $res = Body::decode( - $this->client->request("POST", "/access_methods/unmanaged/get", [ + $this->client->request("GET", "/access_methods/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -74,7 +74,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_methods/unmanaged/list", [ + $this->client->request("GET", "/access_methods/unmanaged/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 109f7141..900e7dc4 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -49,7 +49,7 @@ public function add_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/access_groups/add_user", [ + $this->client->request("PUT", "/acs/access_groups/add_user", [ "json" => (object) $request_payload, ]); } @@ -66,7 +66,7 @@ public function delete(string $acs_access_group_id): void $request_payload["acs_access_group_id"] = $acs_access_group_id; - $this->client->request("POST", "/acs/access_groups/delete", [ + $this->client->request("DELETE", "/acs/access_groups/delete", [ "json" => (object) $request_payload, ]); } @@ -84,7 +84,7 @@ public function get(string $acs_access_group_id): AcsAccessGroup $request_payload["acs_access_group_id"] = $acs_access_group_id; $res = Body::decode( - $this->client->request("POST", "/acs/access_groups/get", [ + $this->client->request("GET", "/acs/access_groups/get", [ "json" => (object) $request_payload, ]), ); @@ -123,7 +123,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/access_groups/list", [ + $this->client->request("GET", "/acs/access_groups/list", [ "json" => (object) $request_payload, ]), ); @@ -149,7 +149,7 @@ public function list_accessible_entrances( $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/access_groups/list_accessible_entrances", ["json" => (object) $request_payload], ), @@ -174,7 +174,7 @@ public function list_users(string $acs_access_group_id): array $request_payload["acs_access_group_id"] = $acs_access_group_id; $res = Body::decode( - $this->client->request("POST", "/acs/access_groups/list_users", [ + $this->client->request("GET", "/acs/access_groups/list_users", [ "json" => (object) $request_payload, ]), ); @@ -205,7 +205,7 @@ public function remove_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/access_groups/remove_user", [ + $this->client->request("DELETE", "/acs/access_groups/remove_user", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 4dc13187..2c74d537 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -48,7 +48,7 @@ public function assign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/credentials/assign", [ + $this->client->request("PATCH", "/acs/credentials/assign", [ "json" => (object) $request_payload, ]); } @@ -155,7 +155,7 @@ public function delete(string $acs_credential_id): void $request_payload["acs_credential_id"] = $acs_credential_id; - $this->client->request("POST", "/acs/credentials/delete", [ + $this->client->request("DELETE", "/acs/credentials/delete", [ "json" => (object) $request_payload, ]); } @@ -173,7 +173,7 @@ public function get(string $acs_credential_id): AcsCredential $request_payload["acs_credential_id"] = $acs_credential_id; $res = Body::decode( - $this->client->request("POST", "/acs/credentials/get", [ + $this->client->request("GET", "/acs/credentials/get", [ "json" => (object) $request_payload, ]), ); @@ -236,7 +236,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/credentials/list", [ + $this->client->request("GET", "/acs/credentials/list", [ "json" => (object) $request_payload, ]), ); @@ -265,7 +265,7 @@ public function list_accessible_entrances(string $acs_credential_id): array $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/credentials/list_accessible_entrances", ["json" => (object) $request_payload], ), @@ -300,7 +300,7 @@ public function unassign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/credentials/unassign", [ + $this->client->request("PATCH", "/acs/credentials/unassign", [ "json" => (object) $request_payload, ]); } @@ -328,7 +328,7 @@ public function update( $request_payload["ends_at"] = $ends_at; } - $this->client->request("POST", "/acs/credentials/update", [ + $this->client->request("PATCH", "/acs/credentials/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index 6383100f..3feb5c4a 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -79,7 +79,7 @@ public function get(string $acs_encoder_id): AcsEncoder $request_payload["acs_encoder_id"] = $acs_encoder_id; $res = Body::decode( - $this->client->request("POST", "/acs/encoders/get", [ + $this->client->request("GET", "/acs/encoders/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 0c6a65e5..c0d97a6e 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -40,7 +40,7 @@ public function get(string $acs_entrance_id): AcsEntrance $request_payload["acs_entrance_id"] = $acs_entrance_id; $res = Body::decode( - $this->client->request("POST", "/acs/entrances/get", [ + $this->client->request("GET", "/acs/entrances/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index 18863753..432e9a5c 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -37,7 +37,7 @@ public function get(string $acs_system_id): AcsSystem $request_payload["acs_system_id"] = $acs_system_id; $res = Body::decode( - $this->client->request("POST", "/acs/systems/get", [ + $this->client->request("GET", "/acs/systems/get", [ "json" => (object) $request_payload, ]), ); @@ -73,7 +73,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/systems/list", [ + $this->client->request("GET", "/acs/systems/list", [ "json" => (object) $request_payload, ]), ); @@ -98,7 +98,7 @@ public function list_compatible_credential_manager_acs_systems( $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/systems/list_compatible_credential_manager_acs_systems", ["json" => (object) $request_payload], ), diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 65f72306..b6d3340a 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -41,7 +41,7 @@ public function add_to_access_group( $request_payload["acs_access_group_id"] = $acs_access_group_id; $request_payload["acs_user_id"] = $acs_user_id; - $this->client->request("POST", "/acs/users/add_to_access_group", [ + $this->client->request("PUT", "/acs/users/add_to_access_group", [ "json" => (object) $request_payload, ]); } @@ -126,7 +126,7 @@ public function delete( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/users/delete", [ + $this->client->request("DELETE", "/acs/users/delete", [ "json" => (object) $request_payload, ]); } @@ -157,7 +157,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/acs/users/get", [ + $this->client->request("GET", "/acs/users/get", [ "json" => (object) $request_payload, ]), ); @@ -222,7 +222,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/users/list", [ + $this->client->request("GET", "/acs/users/list", [ "json" => (object) $request_payload, ]), ); @@ -261,7 +261,7 @@ public function list_accessible_entrances( $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/users/list_accessible_entrances", ["json" => (object) $request_payload], ), @@ -296,9 +296,13 @@ public function remove_from_access_group( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/users/remove_from_access_group", [ - "json" => (object) $request_payload, - ]); + $this->client->request( + "DELETE", + "/acs/users/remove_from_access_group", + [ + "json" => (object) $request_payload, + ], + ); } /** @@ -448,7 +452,7 @@ public function update( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/users/update", [ + $this->client->request("PATCH", "/acs/users/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index c1a656d4..74003d53 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -41,7 +41,7 @@ public function get( $request_payload["action_attempt_id"] = $action_attempt_id; $res = Body::decode( - $this->client->request("POST", "/action_attempts/get", [ + $this->client->request("GET", "/action_attempts/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 99d86e76..9f235068 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -75,7 +75,7 @@ public function create( } $res = Body::decode( - $this->client->request("POST", "/client_sessions/create", [ + $this->client->request("PUT", "/client_sessions/create", [ "json" => (object) $request_payload, ]), ); @@ -95,7 +95,7 @@ public function delete(string $client_session_id): void $request_payload["client_session_id"] = $client_session_id; - $this->client->request("POST", "/client_sessions/delete", [ + $this->client->request("DELETE", "/client_sessions/delete", [ "json" => (object) $request_payload, ]); } @@ -121,7 +121,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/client_sessions/get", [ + $this->client->request("GET", "/client_sessions/get", [ "json" => (object) $request_payload, ]), ); @@ -218,7 +218,7 @@ public function grant_access( $request_payload["user_identity_ids"] = $user_identity_ids; } - $this->client->request("POST", "/client_sessions/grant_access", [ + $this->client->request("PATCH", "/client_sessions/grant_access", [ "json" => (object) $request_payload, ]); } @@ -261,7 +261,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/client_sessions/list", [ + $this->client->request("GET", "/client_sessions/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 22549171..1a01bd70 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -119,7 +119,7 @@ public function delete(string $connect_webview_id): void $request_payload["connect_webview_id"] = $connect_webview_id; - $this->client->request("POST", "/connect_webviews/delete", [ + $this->client->request("DELETE", "/connect_webviews/delete", [ "json" => (object) $request_payload, ]); } @@ -139,7 +139,7 @@ public function get(string $connect_webview_id): ConnectWebview $request_payload["connect_webview_id"] = $connect_webview_id; $res = Body::decode( - $this->client->request("POST", "/connect_webviews/get", [ + $this->client->request("GET", "/connect_webviews/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 14b2b6f0..9525c04a 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -44,7 +44,7 @@ public function delete(string $connected_account_id): void $request_payload["connected_account_id"] = $connected_account_id; - $this->client->request("POST", "/connected_accounts/delete", [ + $this->client->request("DELETE", "/connected_accounts/delete", [ "json" => (object) $request_payload, ]); } @@ -70,7 +70,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/connected_accounts/get", [ + $this->client->request("GET", "/connected_accounts/get", [ "json" => (object) $request_payload, ]), ); @@ -198,7 +198,7 @@ public function update( $request_payload["display_name"] = $display_name; } - $this->client->request("POST", "/connected_accounts/update", [ + $this->client->request("PATCH", "/connected_accounts/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 32be9949..4ff3970d 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -49,7 +49,7 @@ public function get(?string $device_id = null, ?string $name = null): Device } $res = Body::decode( - $this->client->request("POST", "/devices/get", [ + $this->client->request("GET", "/devices/get", [ "json" => (object) $request_payload, ]), ); @@ -182,7 +182,7 @@ public function list_device_providers( } $res = Body::decode( - $this->client->request("POST", "/devices/list_device_providers", [ + $this->client->request("GET", "/devices/list_device_providers", [ "json" => (object) $request_payload, ]), ); @@ -252,7 +252,7 @@ public function update( $request_payload["properties"] = $properties; } - $this->client->request("POST", "/devices/update", [ + $this->client->request("PATCH", "/devices/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 1d10eb6f..ae1d1917 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -49,7 +49,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/devices/unmanaged/get", [ + $this->client->request("GET", "/devices/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -172,7 +172,7 @@ public function update( $request_payload["is_managed"] = $is_managed; } - $this->client->request("POST", "/devices/unmanaged/update", [ + $this->client->request("PATCH", "/devices/unmanaged/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index 0bf6a533..df5a0174 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -50,7 +50,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/events/get", [ + $this->client->request("GET", "/events/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index 895a9351..d4d383cd 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -36,7 +36,7 @@ public function delete(string $instant_key_id): void $request_payload["instant_key_id"] = $instant_key_id; - $this->client->request("POST", "/instant_keys/delete", [ + $this->client->request("DELETE", "/instant_keys/delete", [ "json" => (object) $request_payload, ]); } @@ -62,7 +62,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/instant_keys/get", [ + $this->client->request("GET", "/instant_keys/get", [ "json" => (object) $request_payload, ]), ); @@ -85,7 +85,7 @@ public function list(?string $user_identity_id = null): array } $res = Body::decode( - $this->client->request("POST", "/instant_keys/list", [ + $this->client->request("GET", "/instant_keys/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 5f12b842..c30b958b 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -86,7 +86,7 @@ public function get(?string $device_id = null, ?string $name = null): Device } $res = Body::decode( - $this->client->request("POST", "/locks/get", [ + $this->client->request("GET", "/locks/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 39723a23..810b839e 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -86,7 +86,7 @@ public function delete(string $device_id, string $noise_threshold_id): void $request_payload["noise_threshold_id"] = $noise_threshold_id; $this->client->request( - "POST", + "DELETE", "/noise_sensors/noise_thresholds/delete", ["json" => (object) $request_payload], ); @@ -106,7 +106,7 @@ public function get(string $noise_threshold_id): NoiseThreshold $res = Body::decode( $this->client->request( - "POST", + "GET", "/noise_sensors/noise_thresholds/get", ["json" => (object) $request_payload], ), @@ -129,7 +129,7 @@ public function list(string $device_id): array $res = Body::decode( $this->client->request( - "POST", + "GET", "/noise_sensors/noise_thresholds/list", ["json" => (object) $request_payload], ), @@ -185,7 +185,7 @@ public function update( } $this->client->request( - "POST", + "PUT", "/noise_sensors/noise_thresholds/update", ["json" => (object) $request_payload], ); diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index b710c78c..7b49612d 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -37,7 +37,7 @@ public function deactivate(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request("POST", "/phones/deactivate", [ + $this->client->request("DELETE", "/phones/deactivate", [ "json" => (object) $request_payload, ]); } @@ -55,7 +55,7 @@ public function get(string $device_id): Phone $request_payload["device_id"] = $device_id; $res = Body::decode( - $this->client->request("POST", "/phones/get", [ + $this->client->request("GET", "/phones/get", [ "json" => (object) $request_payload, ]), ); @@ -86,7 +86,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/phones/list", [ + $this->client->request("GET", "/phones/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 633e115a..1a5dbec1 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -41,7 +41,7 @@ public function add_acs_entrances( $request_payload["acs_entrance_ids"] = $acs_entrance_ids; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/add_acs_entrances", [ + $this->client->request("PUT", "/spaces/add_acs_entrances", [ "json" => (object) $request_payload, ]); } @@ -62,7 +62,7 @@ public function add_connected_account( $request_payload["connected_account_id"] = $connected_account_id; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/add_connected_account", [ + $this->client->request("PUT", "/spaces/add_connected_account", [ "json" => (object) $request_payload, ]); } @@ -81,7 +81,7 @@ public function add_devices(array $device_ids, string $space_id): void $request_payload["device_ids"] = $device_ids; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/add_devices", [ + $this->client->request("PUT", "/spaces/add_devices", [ "json" => (object) $request_payload, ]); } @@ -150,7 +150,7 @@ public function delete(string $space_id): void $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/delete", [ + $this->client->request("DELETE", "/spaces/delete", [ "json" => (object) $request_payload, ]); } @@ -176,7 +176,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/spaces/get", [ + $this->client->request("GET", "/spaces/get", [ "json" => (object) $request_payload, ]), ); @@ -261,7 +261,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/spaces/list", [ + $this->client->request("GET", "/spaces/list", [ "json" => (object) $request_payload, ]), ); @@ -310,7 +310,7 @@ public function remove_connected_account( $request_payload["connected_account_id"] = $connected_account_id; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/remove_connected_account", [ + $this->client->request("DELETE", "/spaces/remove_connected_account", [ "json" => (object) $request_payload, ]); } @@ -375,7 +375,7 @@ public function update( } $res = Body::decode( - $this->client->request("POST", "/spaces/update", [ + $this->client->request("PATCH", "/spaces/update", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index a1dfbc5d..7d41d63c 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -208,7 +208,7 @@ public function delete_climate_preset( $request_payload["climate_preset_key"] = $climate_preset_key; $request_payload["device_id"] = $device_id; - $this->client->request("POST", "/thermostats/delete_climate_preset", [ + $this->client->request("DELETE", "/thermostats/delete_climate_preset", [ "json" => (object) $request_payload, ]); } @@ -551,7 +551,7 @@ public function set_temperature_threshold( } $this->client->request( - "POST", + "PATCH", "/thermostats/set_temperature_threshold", ["json" => (object) $request_payload], ); @@ -633,7 +633,7 @@ public function update_climate_preset( $request_payload["name"] = $name; } - $this->client->request("POST", "/thermostats/update_climate_preset", [ + $this->client->request("PATCH", "/thermostats/update_climate_preset", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 9fc26e01..c16100fb 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -72,7 +72,7 @@ public function delete(string $thermostat_daily_program_id): void "thermostat_daily_program_id" ] = $thermostat_daily_program_id; - $this->client->request("POST", "/thermostats/daily_programs/delete", [ + $this->client->request("DELETE", "/thermostats/daily_programs/delete", [ "json" => (object) $request_payload, ]); } @@ -102,7 +102,7 @@ public function update( $res = Body::decode( $this->client->request( - "POST", + "PATCH", "/thermostats/daily_programs/update", ["json" => (object) $request_payload], ), diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index 4e857221..a5faaa6c 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -84,7 +84,7 @@ public function delete(string $thermostat_schedule_id): void $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $this->client->request("POST", "/thermostats/schedules/delete", [ + $this->client->request("DELETE", "/thermostats/schedules/delete", [ "json" => (object) $request_payload, ]); } @@ -102,7 +102,7 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; $res = Body::decode( - $this->client->request("POST", "/thermostats/schedules/get", [ + $this->client->request("GET", "/thermostats/schedules/get", [ "json" => (object) $request_payload, ]), ); @@ -129,7 +129,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/thermostats/schedules/list", [ + $this->client->request("GET", "/thermostats/schedules/list", [ "json" => (object) $request_payload, ]), ); @@ -185,7 +185,7 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->client->request("POST", "/thermostats/schedules/update", [ + $this->client->request("PATCH", "/thermostats/schedules/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 3260b177..b37dbd5d 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -60,7 +60,7 @@ public function add_acs_user( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request("POST", "/user_identities/add_acs_user", [ + $this->client->request("PUT", "/user_identities/add_acs_user", [ "json" => (object) $request_payload, ]); } @@ -121,7 +121,7 @@ public function delete(string $user_identity_id): void $request_payload["user_identity_id"] = $user_identity_id; - $this->client->request("POST", "/user_identities/delete", [ + $this->client->request("DELETE", "/user_identities/delete", [ "json" => (object) $request_payload, ]); } @@ -183,7 +183,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/user_identities/get", [ + $this->client->request("GET", "/user_identities/get", [ "json" => (object) $request_payload, ]), ); @@ -208,7 +208,7 @@ public function grant_access_to_device( $request_payload["user_identity_id"] = $user_identity_id; $this->client->request( - "POST", + "PUT", "/user_identities/grant_access_to_device", ["json" => (object) $request_payload], ); @@ -288,7 +288,7 @@ public function list_accessible_devices(string $user_identity_id): array $res = Body::decode( $this->client->request( - "POST", + "GET", "/user_identities/list_accessible_devices", ["json" => (object) $request_payload], ), @@ -311,7 +311,7 @@ public function list_accessible_entrances(string $user_identity_id): array $res = Body::decode( $this->client->request( - "POST", + "GET", "/user_identities/list_accessible_entrances", ["json" => (object) $request_payload], ), @@ -336,11 +336,9 @@ public function list_acs_systems(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; $res = Body::decode( - $this->client->request( - "POST", - "/user_identities/list_acs_systems", - ["json" => (object) $request_payload], - ), + $this->client->request("GET", "/user_identities/list_acs_systems", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); @@ -359,7 +357,7 @@ public function list_acs_users(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; $res = Body::decode( - $this->client->request("POST", "/user_identities/list_acs_users", [ + $this->client->request("GET", "/user_identities/list_acs_users", [ "json" => (object) $request_payload, ]), ); @@ -383,7 +381,7 @@ public function remove_acs_user( $request_payload["acs_user_id"] = $acs_user_id; $request_payload["user_identity_id"] = $user_identity_id; - $this->client->request("POST", "/user_identities/remove_acs_user", [ + $this->client->request("DELETE", "/user_identities/remove_acs_user", [ "json" => (object) $request_payload, ]); } @@ -405,7 +403,7 @@ public function revoke_access_to_device( $request_payload["user_identity_id"] = $user_identity_id; $this->client->request( - "POST", + "DELETE", "/user_identities/revoke_access_to_device", ["json" => (object) $request_payload], ); @@ -444,7 +442,7 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request("POST", "/user_identities/update", [ + $this->client->request("PATCH", "/user_identities/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 4190e614..6cea5c93 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -37,7 +37,7 @@ public function get(string $user_identity_id): UnmanagedUserIdentity $request_payload["user_identity_id"] = $user_identity_id; $res = Body::decode( - $this->client->request("POST", "/user_identities/unmanaged/get", [ + $this->client->request("GET", "/user_identities/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -78,7 +78,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/user_identities/unmanaged/list", [ + $this->client->request("GET", "/user_identities/unmanaged/list", [ "json" => (object) $request_payload, ]), ); @@ -116,7 +116,7 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request("POST", "/user_identities/unmanaged/update", [ + $this->client->request("PATCH", "/user_identities/unmanaged/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index efe061fa..d34a9660 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -61,7 +61,7 @@ public function delete(string $webhook_id): void $request_payload["webhook_id"] = $webhook_id; - $this->client->request("POST", "/webhooks/delete", [ + $this->client->request("DELETE", "/webhooks/delete", [ "json" => (object) $request_payload, ]); } @@ -79,7 +79,7 @@ public function get(string $webhook_id): Webhook $request_payload["webhook_id"] = $webhook_id; $res = Body::decode( - $this->client->request("POST", "/webhooks/get", [ + $this->client->request("GET", "/webhooks/get", [ "json" => (object) $request_payload, ]), ); @@ -94,7 +94,7 @@ public function get(string $webhook_id): Webhook */ public function list(): array { - $res = Body::decode($this->client->request("POST", "/webhooks/list")); + $res = Body::decode($this->client->request("GET", "/webhooks/list")); return array_map(fn($r) => Webhook::from_json($r), $res->webhooks); } @@ -113,7 +113,7 @@ public function update(array $event_types, string $webhook_id): void $request_payload["event_types"] = $event_types; $request_payload["webhook_id"] = $webhook_id; - $this->client->request("POST", "/webhooks/update", [ + $this->client->request("PUT", "/webhooks/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 84462b8a..752b182b 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -108,7 +108,7 @@ public function create( */ public function get(): Workspace { - $res = Body::decode($this->client->request("POST", "/workspaces/get")); + $res = Body::decode($this->client->request("GET", "/workspaces/get")); return Workspace::from_json($res->workspace); } @@ -120,7 +120,7 @@ public function get(): Workspace */ public function list(): array { - $res = Body::decode($this->client->request("POST", "/workspaces/list")); + $res = Body::decode($this->client->request("GET", "/workspaces/list")); return array_map(fn($r) => Workspace::from_json($r), $res->workspaces); } @@ -190,7 +190,7 @@ public function update( $request_payload["organization_id"] = $organization_id; } - $this->client->request("POST", "/workspaces/update", [ + $this->client->request("PATCH", "/workspaces/update", [ "json" => (object) $request_payload, ]); } From f3f9616960b99d70e214c01dafe5b8609a652260 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:29:58 +0000 Subject: [PATCH 24/80] ci: Generate code --- src/Routes/AcsUsersClient.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index b6d3340a..e26c38f8 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -299,9 +299,7 @@ public function remove_from_access_group( $this->client->request( "DELETE", "/acs/users/remove_from_access_group", - [ - "json" => (object) $request_payload, - ], + ["json" => (object) $request_payload], ); } From 045751eb8908d67d6db8d24bbfd3504f0ddc240b Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:40:28 -0700 Subject: [PATCH 25/80] Use params for GET / DELETE --- codegen/layouts/partials/route-method.hbs | 2 +- codegen/lib/layouts/route.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index cf40c56b..06c547d5 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -18,7 +18,7 @@ "{{httpMethod}}", "{{path}}", {{#if hasParams}} - ["json" => (object) $request_payload], + ["{{#if usesQueryParams}}query{{else}}json{{/if}}" => {{#unless usesQueryParams}}(object) {{/unless}}$request_payload], {{/if}} ){{#unless returnsVoid}}){{/unless}}; {{#if usesActionAttempt}} diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index de8c4693..6104d390 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -16,6 +16,7 @@ const resourcesNamespace = 'Seam\\Resources' export interface MethodLayoutContext { methodName: string httpMethod: string + usesQueryParams: boolean description: string responseDescription: string isDeprecated: boolean @@ -115,6 +116,7 @@ const getMethodLayoutContext = ( return { methodName, httpMethod: method.httpMethod, + usesQueryParams: ['GET', 'DELETE'].includes(method.httpMethod), description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, From 1478dd9609d12b2b52f66b094c115752a16d2ec9 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:41:09 +0000 Subject: [PATCH 26/80] ci: Generate code --- src/Routes/AccessCodesClient.php | 6 +++--- src/Routes/AccessCodesUnmanagedClient.php | 6 +++--- src/Routes/AccessGrantsClient.php | 4 ++-- src/Routes/AccessGrantsUnmanagedClient.php | 4 ++-- src/Routes/AccessMethodsClient.php | 6 +++--- src/Routes/AccessMethodsUnmanagedClient.php | 4 ++-- src/Routes/AcsAccessGroupsClient.php | 12 ++++++------ src/Routes/AcsCredentialsClient.php | 8 ++++---- src/Routes/AcsEncodersClient.php | 2 +- src/Routes/AcsEntrancesClient.php | 2 +- src/Routes/AcsSystemsClient.php | 6 +++--- src/Routes/AcsUsersClient.php | 10 +++++----- src/Routes/ActionAttemptsClient.php | 2 +- src/Routes/ClientSessionsClient.php | 6 +++--- src/Routes/ConnectWebviewsClient.php | 4 ++-- src/Routes/ConnectedAccountsClient.php | 4 ++-- src/Routes/DevicesClient.php | 4 ++-- src/Routes/DevicesUnmanagedClient.php | 2 +- src/Routes/EventsClient.php | 2 +- src/Routes/InstantKeysClient.php | 6 +++--- src/Routes/LocksClient.php | 2 +- src/Routes/NoiseSensorsNoiseThresholdsClient.php | 6 +++--- src/Routes/PhonesClient.php | 6 +++--- src/Routes/SpacesClient.php | 8 ++++---- src/Routes/ThermostatsClient.php | 2 +- src/Routes/ThermostatsDailyProgramsClient.php | 2 +- src/Routes/ThermostatsSchedulesClient.php | 6 +++--- src/Routes/UserIdentitiesClient.php | 16 ++++++++-------- src/Routes/UserIdentitiesUnmanagedClient.php | 4 ++-- src/Routes/WebhooksClient.php | 4 ++-- 30 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 31d04bc3..a648f967 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -271,7 +271,7 @@ public function delete( } $this->client->request("DELETE", "/access_codes/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -289,7 +289,7 @@ public function generate_code(string $device_id): AccessCode $res = Body::decode( $this->client->request("GET", "/access_codes/generate_code", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -325,7 +325,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/access_codes/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index e823dc0c..8662ce2f 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -80,7 +80,7 @@ public function delete(string $access_code_id): void $request_payload["access_code_id"] = $access_code_id; $this->client->request("DELETE", "/access_codes/unmanaged/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -113,7 +113,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/access_codes/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -157,7 +157,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/access_codes/unmanaged/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 2ec4a198..a8c23f9c 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -135,7 +135,7 @@ public function delete(string $access_grant_id): void $request_payload["access_grant_id"] = $access_grant_id; $this->client->request("DELETE", "/access_grants/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -161,7 +161,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/access_grants/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 06d2974c..3b3d6658 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -38,7 +38,7 @@ public function get(string $access_grant_id): UnmanagedAccessGrant $res = Body::decode( $this->client->request("GET", "/access_grants/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -89,7 +89,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/access_grants/unmanaged/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index e972d500..c8bb36c9 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -86,7 +86,7 @@ public function delete( } $this->client->request("DELETE", "/access_methods/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -136,7 +136,7 @@ public function get(string $access_method_id): AccessMethod $res = Body::decode( $this->client->request("GET", "/access_methods/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -229,7 +229,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/access_methods/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 3f958727..9bc0a43a 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -38,7 +38,7 @@ public function get(string $access_method_id): UnmanagedAccessMethod $res = Body::decode( $this->client->request("GET", "/access_methods/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -75,7 +75,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/access_methods/unmanaged/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 900e7dc4..74cb4208 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -67,7 +67,7 @@ public function delete(string $acs_access_group_id): void $request_payload["acs_access_group_id"] = $acs_access_group_id; $this->client->request("DELETE", "/acs/access_groups/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -85,7 +85,7 @@ public function get(string $acs_access_group_id): AcsAccessGroup $res = Body::decode( $this->client->request("GET", "/acs/access_groups/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -124,7 +124,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/acs/access_groups/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -151,7 +151,7 @@ public function list_accessible_entrances( $this->client->request( "GET", "/acs/access_groups/list_accessible_entrances", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -175,7 +175,7 @@ public function list_users(string $acs_access_group_id): array $res = Body::decode( $this->client->request("GET", "/acs/access_groups/list_users", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -206,7 +206,7 @@ public function remove_user( } $this->client->request("DELETE", "/acs/access_groups/remove_user", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } } diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 2c74d537..86a884c8 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -156,7 +156,7 @@ public function delete(string $acs_credential_id): void $request_payload["acs_credential_id"] = $acs_credential_id; $this->client->request("DELETE", "/acs/credentials/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -174,7 +174,7 @@ public function get(string $acs_credential_id): AcsCredential $res = Body::decode( $this->client->request("GET", "/acs/credentials/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -237,7 +237,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/acs/credentials/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -267,7 +267,7 @@ public function list_accessible_entrances(string $acs_credential_id): array $this->client->request( "GET", "/acs/credentials/list_accessible_entrances", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index 3feb5c4a..b7cd6c15 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -80,7 +80,7 @@ public function get(string $acs_encoder_id): AcsEncoder $res = Body::decode( $this->client->request("GET", "/acs/encoders/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index c0d97a6e..1cd2c1c1 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -41,7 +41,7 @@ public function get(string $acs_entrance_id): AcsEntrance $res = Body::decode( $this->client->request("GET", "/acs/entrances/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index 432e9a5c..af1d681d 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -38,7 +38,7 @@ public function get(string $acs_system_id): AcsSystem $res = Body::decode( $this->client->request("GET", "/acs/systems/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -74,7 +74,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/acs/systems/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -100,7 +100,7 @@ public function list_compatible_credential_manager_acs_systems( $this->client->request( "GET", "/acs/systems/list_compatible_credential_manager_acs_systems", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index e26c38f8..5b72d157 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -127,7 +127,7 @@ public function delete( } $this->client->request("DELETE", "/acs/users/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -158,7 +158,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/acs/users/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -223,7 +223,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/acs/users/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -263,7 +263,7 @@ public function list_accessible_entrances( $this->client->request( "GET", "/acs/users/list_accessible_entrances", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -299,7 +299,7 @@ public function remove_from_access_group( $this->client->request( "DELETE", "/acs/users/remove_from_access_group", - ["json" => (object) $request_payload], + ["query" => $request_payload], ); } diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 74003d53..11454f3d 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -42,7 +42,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/action_attempts/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 9f235068..313d2dd6 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -96,7 +96,7 @@ public function delete(string $client_session_id): void $request_payload["client_session_id"] = $client_session_id; $this->client->request("DELETE", "/client_sessions/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -122,7 +122,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/client_sessions/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -262,7 +262,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/client_sessions/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 1a01bd70..b5f171b2 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -120,7 +120,7 @@ public function delete(string $connect_webview_id): void $request_payload["connect_webview_id"] = $connect_webview_id; $this->client->request("DELETE", "/connect_webviews/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -140,7 +140,7 @@ public function get(string $connect_webview_id): ConnectWebview $res = Body::decode( $this->client->request("GET", "/connect_webviews/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 9525c04a..abbb5914 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -45,7 +45,7 @@ public function delete(string $connected_account_id): void $request_payload["connected_account_id"] = $connected_account_id; $this->client->request("DELETE", "/connected_accounts/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -71,7 +71,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/connected_accounts/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 4ff3970d..f7807686 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -50,7 +50,7 @@ public function get(?string $device_id = null, ?string $name = null): Device $res = Body::decode( $this->client->request("GET", "/devices/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -183,7 +183,7 @@ public function list_device_providers( $res = Body::decode( $this->client->request("GET", "/devices/list_device_providers", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index ae1d1917..e6c222ea 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -50,7 +50,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/devices/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index df5a0174..f787e91b 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -51,7 +51,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/events/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index d4d383cd..b04a987c 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -37,7 +37,7 @@ public function delete(string $instant_key_id): void $request_payload["instant_key_id"] = $instant_key_id; $this->client->request("DELETE", "/instant_keys/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -63,7 +63,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/instant_keys/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -86,7 +86,7 @@ public function list(?string $user_identity_id = null): array $res = Body::decode( $this->client->request("GET", "/instant_keys/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index c30b958b..313a499f 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -87,7 +87,7 @@ public function get(?string $device_id = null, ?string $name = null): Device $res = Body::decode( $this->client->request("GET", "/locks/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 810b839e..88558830 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -88,7 +88,7 @@ public function delete(string $device_id, string $noise_threshold_id): void $this->client->request( "DELETE", "/noise_sensors/noise_thresholds/delete", - ["json" => (object) $request_payload], + ["query" => $request_payload], ); } @@ -108,7 +108,7 @@ public function get(string $noise_threshold_id): NoiseThreshold $this->client->request( "GET", "/noise_sensors/noise_thresholds/get", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -131,7 +131,7 @@ public function list(string $device_id): array $this->client->request( "GET", "/noise_sensors/noise_thresholds/list", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index 7b49612d..f56953e8 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -38,7 +38,7 @@ public function deactivate(string $device_id): void $request_payload["device_id"] = $device_id; $this->client->request("DELETE", "/phones/deactivate", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -56,7 +56,7 @@ public function get(string $device_id): Phone $res = Body::decode( $this->client->request("GET", "/phones/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -87,7 +87,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/phones/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 1a5dbec1..88d64988 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -151,7 +151,7 @@ public function delete(string $space_id): void $request_payload["space_id"] = $space_id; $this->client->request("DELETE", "/spaces/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -177,7 +177,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/spaces/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -262,7 +262,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/spaces/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -311,7 +311,7 @@ public function remove_connected_account( $request_payload["space_id"] = $space_id; $this->client->request("DELETE", "/spaces/remove_connected_account", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 7d41d63c..af0f5e09 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -209,7 +209,7 @@ public function delete_climate_preset( $request_payload["device_id"] = $device_id; $this->client->request("DELETE", "/thermostats/delete_climate_preset", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index c16100fb..3d64dcbc 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -73,7 +73,7 @@ public function delete(string $thermostat_daily_program_id): void ] = $thermostat_daily_program_id; $this->client->request("DELETE", "/thermostats/daily_programs/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index a5faaa6c..38ad4f07 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -85,7 +85,7 @@ public function delete(string $thermostat_schedule_id): void $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; $this->client->request("DELETE", "/thermostats/schedules/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -103,7 +103,7 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule $res = Body::decode( $this->client->request("GET", "/thermostats/schedules/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -130,7 +130,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/thermostats/schedules/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index b37dbd5d..c30cf586 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -122,7 +122,7 @@ public function delete(string $user_identity_id): void $request_payload["user_identity_id"] = $user_identity_id; $this->client->request("DELETE", "/user_identities/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -184,7 +184,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/user_identities/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -290,7 +290,7 @@ public function list_accessible_devices(string $user_identity_id): array $this->client->request( "GET", "/user_identities/list_accessible_devices", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -313,7 +313,7 @@ public function list_accessible_entrances(string $user_identity_id): array $this->client->request( "GET", "/user_identities/list_accessible_entrances", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -337,7 +337,7 @@ public function list_acs_systems(string $user_identity_id): array $res = Body::decode( $this->client->request("GET", "/user_identities/list_acs_systems", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -358,7 +358,7 @@ public function list_acs_users(string $user_identity_id): array $res = Body::decode( $this->client->request("GET", "/user_identities/list_acs_users", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -382,7 +382,7 @@ public function remove_acs_user( $request_payload["user_identity_id"] = $user_identity_id; $this->client->request("DELETE", "/user_identities/remove_acs_user", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -405,7 +405,7 @@ public function revoke_access_to_device( $this->client->request( "DELETE", "/user_identities/revoke_access_to_device", - ["json" => (object) $request_payload], + ["query" => $request_payload], ); } diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 6cea5c93..92a77c06 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -38,7 +38,7 @@ public function get(string $user_identity_id): UnmanagedUserIdentity $res = Body::decode( $this->client->request("GET", "/user_identities/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -79,7 +79,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/user_identities/unmanaged/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index d34a9660..29cbb648 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -62,7 +62,7 @@ public function delete(string $webhook_id): void $request_payload["webhook_id"] = $webhook_id; $this->client->request("DELETE", "/webhooks/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -80,7 +80,7 @@ public function get(string $webhook_id): Webhook $res = Body::decode( $this->client->request("GET", "/webhooks/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); From 8bdc5fb34e028c3e8438d13ba6b528c2634dc5b9 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:49:41 -0700 Subject: [PATCH 27/80] Impliment isOptional / isNullable --- codegen/lib/class-model.ts | 5 +++-- codegen/lib/layouts/resource.ts | 11 ++++++++--- codegen/lib/layouts/route.ts | 10 +++++++--- codegen/lib/resource-model.ts | 4 ++++ codegen/lib/routes.ts | 3 ++- tests/HeadersTest.php | 2 +- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index ab9f8022..c335114e 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -5,7 +5,8 @@ export interface PhpClientMethodParameter { name: string type: string description: string - required?: boolean | undefined + isOptional: boolean + isNullable: boolean position?: number | undefined } @@ -44,4 +45,4 @@ export const sortPhpClientMethodParameters = ( [...parameters].sort((a, b) => getParameterRank(a) - getParameterRank(b)) const getParameterRank = (parameter: PhpClientMethodParameter): number => - parameter.position ?? ((parameter.required ?? false) ? 1000 : 9999) + parameter.position ?? (parameter.isOptional ? 9999 : 1000) diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index 6ebd0ee6..72b424d1 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -57,19 +57,24 @@ const generateConstructorParam = ( property: ResourceClassProperty, ): ConstructorParamLayoutContext => { let declaration: string + // Resource decoding remains tolerant of sparse response envelopes. Optional + // properties can also be omitted when constructing a resource directly; + // nullable properties retain the same null-safe representation. + const defaultValue = property.isOptional ? ' = null' : '' + switch (property.kind) { case 'objectReference': - declaration = `public ${property.referenceName}|null $${property.name},` + declaration = `public ${property.referenceName}|null $${property.name}${defaultValue},` break case 'listReference': - declaration = `public array $${property.name},` + declaration = `public array${property.isOptional ? '|null' : ''} $${property.name}${defaultValue},` break case 'value': { const { phpType } = property const nullSuffix = phpType === 'mixed' ? '' : '|null' - declaration = `public ${phpType}${nullSuffix} $${property.name},` + declaration = `public ${phpType}${nullSuffix} $${property.name}${defaultValue},` break } } diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 6104d390..fbe11da4 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -26,6 +26,8 @@ export interface MethodLayoutContext { type: string description: string required: boolean + isOptional: boolean + isNullable: boolean }> documentedParameters: Array<{ name: string @@ -94,7 +96,7 @@ const getMethodLayoutContext = ( const signatureParams = sortedParameters .map( (p) => - `${!(p.required ?? false) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${(p.required ?? false) ? '' : ' = null'}`, + `${(p.isNullable || p.isOptional) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional ? ' = null' : ''}`, ) .concat( usesActionAttempt @@ -105,11 +107,13 @@ const getMethodLayoutContext = ( .join(', ') const documentedEndpointParameters = sortedParameters.map( - ({ name, type, description, required }) => ({ + ({ name, type, description, isOptional, isNullable }) => ({ name, type, description, - required: required ?? false, + required: !isOptional, + isOptional, + isNullable, }), ) diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index 6c277f86..c09688d7 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -32,6 +32,8 @@ export type ResourceClassProperty = interface ResourceClassPropertyMetadata { name: string description: string + isOptional: boolean + isNullable: boolean isDeprecated: boolean deprecationMessage: string } @@ -258,6 +260,8 @@ const buildClass = ( const metadata = { name: property.name, description: property.description, + isOptional: property.isOptional, + isNullable: property.isNullable, isDeprecated: property.isDeprecated, deprecationMessage: property.deprecationMessage, } diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 60deb72a..d81cb9f7 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -131,7 +131,8 @@ const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { name: parameter.name, type: getPhpType(parameter), description: parameter.description, - required: parameter.isRequired, + isOptional: !parameter.isRequired, + isNullable: parameter.isNullable, // The primary identifier of a get endpoint always sorts first in the // method signature. position: diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index d752ad4f..6afecd90 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -35,7 +35,7 @@ public function testSendsDefaultHeaders(): void $request = $recorder->request(); $this->assertSame("/devices/get", $request->getUri()->getPath()); - $this->assertEquals((object) ["device_id" => "d1"], $recorder->body()); + $this->assertSame("device_id=d1", $request->getUri()->getQuery()); $this->assertSame( "Bearer seam_apikey_token", From 3c2a719ccaf31851e929ce6a936bac2991f070ff Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:50:26 +0000 Subject: [PATCH 28/80] ci: Generate code --- src/Resources/AccessCode.php | 48 +- src/Resources/AccessGrant.php | 20 +- src/Resources/AccessMethod.php | 18 +- src/Resources/AcsAccessGroup.php | 6 +- src/Resources/AcsCredential.php | 68 +- src/Resources/AcsEntrance.php | 132 ++-- src/Resources/AcsSystem.php | 26 +- src/Resources/AcsUser.php | 52 +- src/Resources/ActionAttempt.php | 188 ++--- src/Resources/Batch.php | 48 +- src/Resources/ClientSession.php | 4 +- src/Resources/ConnectWebview.php | 2 +- src/Resources/ConnectedAccount.php | 54 +- src/Resources/Device.php | 868 ++++++++++++------------ src/Resources/DeviceProvider.php | 40 +- src/Resources/Event.php | 76 +-- src/Resources/InstantKey.php | 10 +- src/Resources/NoiseThreshold.php | 2 +- src/Resources/Phone.php | 16 +- src/Resources/Space.php | 16 +- src/Resources/ThermostatSchedule.php | 4 +- src/Resources/UnmanagedAccessCode.php | 44 +- src/Resources/UnmanagedAccessGrant.php | 14 +- src/Resources/UnmanagedAccessMethod.php | 12 +- src/Resources/UnmanagedDevice.php | 80 +-- src/Resources/Webhook.php | 4 +- src/Resources/Workspace.php | 12 +- 27 files changed, 932 insertions(+), 932 deletions(-) diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index d2c1a118..ad39cb12 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -90,11 +90,11 @@ public function __construct( /** * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. */ - public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Date and time after which the time-bound access code becomes inactive. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). */ @@ -102,7 +102,7 @@ public function __construct( /** * Indicates whether the access code is a backup code. */ - public bool|null $is_backup, + public bool|null $is_backup = null, /** * Indicates whether a backup access code is available for use if the primary access code is lost or compromised. */ @@ -126,11 +126,11 @@ public function __construct( /** * Indicates whether the code is set on the device according to a preconfigured schedule. */ - public bool|null $is_scheduled_on_device, + public bool|null $is_scheduled_on_device = null, /** * Indicates whether the access code is waiting for a code assignment. */ - public bool|null $is_waiting_for_code_assignment, + public bool|null $is_waiting_for_code_assignment = null, /** * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). */ @@ -142,11 +142,11 @@ public function __construct( /** * Identifier of the pulled backup access code. Used to associate the pulled backup access code with the original access code. */ - public string|null $pulled_backup_access_code_id, + public string|null $pulled_backup_access_code_id = null, /** * Date and time at which the time-bound access code becomes active. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * Current status of the access code within the operational lifecycle. Values are `setting`, a transitional phase that indicates that the code is being configured or activated; `set`, which indicates that the code is active and operational; `unset`, which indicates a deactivated or unused state, either before activation or after deliberate deactivation; `removing`, which indicates a transitional period in which the code is being deleted or made inactive; and `unknown`, which indicates an indeterminate state, due to reasons such as system errors or incomplete data, that highlights a potential need for system review or troubleshooting. See also [Lifecycle of Access Codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes). */ @@ -195,35 +195,35 @@ public function __construct( /** * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. */ - public bool|null $is_cancellable, + public bool|null $is_cancellable = null, /** * Indicates whether early check-in is available for this stay. */ - public bool|null $is_early_checkin_able, + public bool|null $is_early_checkin_able = null, /** * Indicates whether the stay can be extended via the Dormakaba Oracode API. */ - public bool|null $is_extendable, + public bool|null $is_extendable = null, /** * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. */ - public bool|null $is_overridable, + public bool|null $is_overridable = null, /** * Dormakaba Oracode site name associated with this access code. */ - public string|null $site_name, + public string|null $site_name = null, /** * Dormakaba Oracode stay ID associated with this access code. */ - public float|null $stay_id, + public float|null $stay_id = null, /** * Dormakaba Oracode user level ID associated with this access code. */ - public string|null $user_level_id, + public string|null $user_level_id = null, /** * Dormakaba Oracode user level name associated with this access code. */ - public string|null $user_level_name, + public string|null $user_level_name = null, ) {} } @@ -261,11 +261,11 @@ public function __construct( /** * 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. */ - public string|null $change_type, + public string|null $change_type = null, /** * Date and time at which Seam created the error. */ - public string|null $created_at, + public string|null $created_at = null, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ @@ -277,13 +277,13 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. */ - public string|null $managed_access_code_id, + public string|null $managed_access_code_id = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -291,11 +291,11 @@ public function __construct( /** * List of fields that were changed externally, with their previous and new values. */ - public array $modified_fields, + public array|null $modified_fields = null, /** * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. */ - public string|null $unmanaged_access_code_id, + public string|null $unmanaged_access_code_id = null, ) {} } @@ -368,11 +368,11 @@ public function __construct( /** * 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. */ - public string|null $change_type, + public string|null $change_type = null, /** * Date and time at which Seam created the warning. */ - public string|null $created_at, + public string|null $created_at = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -380,7 +380,7 @@ public function __construct( /** * List of fields that were changed externally, with their previous and new values. */ - public array $modified_fields, + public array|null $modified_fields = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index 6df3876a..9d2d9448 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -56,7 +56,7 @@ public function __construct( /** * Unique key for the access grant within the workspace. */ - public string|null $access_grant_key, + public string|null $access_grant_key = null, /** * IDs of the access methods created for the Access Grant. */ @@ -64,7 +64,7 @@ public function __construct( /** * Client Session Token. Only returned if the Access Grant has a mobile_key access method. */ - public string|null $client_session_token, + public string|null $client_session_token = null, /** * Date and time at which the Access Grant was created. */ @@ -72,7 +72,7 @@ public function __construct( /** * ID of the customization profile associated with the Access Grant. */ - public string|null $customization_profile_id, + public string|null $customization_profile_id = null, /** * Display name of the Access Grant. */ @@ -88,7 +88,7 @@ public function __construct( /** * Instant Key URL. Only returned if the Access Grant has a single mobile_key access_method. */ - public string|null $instant_key_url, + public string|null $instant_key_url = null, /** * @deprecated Use `space_ids`. */ @@ -108,7 +108,7 @@ public function __construct( /** * Reservation key for the access grant. */ - public string|null $reservation_key, + public string|null $reservation_key = null, /** * IDs of the spaces to which the Access Grant gives access. */ @@ -168,7 +168,7 @@ public function __construct( /** * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. */ - public array|null $missing_device_ids, + public array|null $missing_device_ids = null, ) {} } @@ -242,7 +242,7 @@ public function __construct( /** * Specific PIN code to use for this access method. Only applicable when mode is 'code'. */ - public string|null $code, + public string|null $code = null, /** * IDs of the access methods created for the requested access method. */ @@ -258,7 +258,7 @@ public function __construct( /** * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. */ - public int|null $instant_key_max_use_count, + public int|null $instant_key_max_use_count = null, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ @@ -305,7 +305,7 @@ public function __construct( /** * 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). */ - public array $failed_devices, + public array|null $failed_devices = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -380,7 +380,7 @@ public function __construct( /** * Common code key to ensure PIN code reuse across devices. */ - public string|null $common_code_key, + public string|null $common_code_key = null, /** * New device IDs where access codes should be created. */ diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index 7745b2f6..f7946c5b 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -51,11 +51,11 @@ public function __construct( /** * Token of the client session associated with the access method. */ - public string|null $client_session_token, + public string|null $client_session_token = null, /** * The actual PIN code for code access methods. */ - public string|null $code, + public string|null $code = null, /** * Date and time at which the access method was created. */ @@ -63,7 +63,7 @@ public function __construct( /** * ID of the customization profile associated with the access method. */ - public string|null $customization_profile_id, + public string|null $customization_profile_id = null, /** * Display name of the access method. */ @@ -75,15 +75,15 @@ public function __construct( /** * URL of the Instant Key for mobile key access methods. */ - public string|null $instant_key_url, + public string|null $instant_key_url = null, /** * 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. */ - public bool|null $is_assignment_required, + public bool|null $is_assignment_required = null, /** * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. */ - public bool|null $is_encoding_required, + public bool|null $is_encoding_required = null, /** * Indicates whether the access method has been issued. */ @@ -91,11 +91,11 @@ public function __construct( /** * 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. */ - public bool|null $is_ready_for_assignment, + public bool|null $is_ready_for_assignment = null, /** * 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. */ - public bool|null $is_ready_for_encoding, + public bool|null $is_ready_for_encoding = null, /** * Date and time at which the access method was issued. */ @@ -223,7 +223,7 @@ public function __construct( /** * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $original_access_method_id, + public string|null $original_access_method_id = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index b51f042d..387acf79 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -62,7 +62,7 @@ public function __construct( /** * `starts_at` and `ends_at` timestamps for the access group's access. */ - public AcsAccessGroup\AccessSchedule|null $access_schedule, + public AcsAccessGroup\AccessSchedule|null $access_schedule = null, /** * ID of the access group. */ @@ -296,7 +296,7 @@ public function __construct( /** * Name of the access group. */ - public string|null $name, + public string|null $name = null, /** * Starting time for the access schedule. */ @@ -336,7 +336,7 @@ public function __construct( /** * Name of the access group. */ - public string|null $name, + public string|null $name = null, /** * Starting time for the access schedule. */ diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index a63289f1..51f87ee3 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -87,7 +87,7 @@ public function __construct( /** * ID of the credential pool to which the credential belongs. */ - public string|null $acs_credential_pool_id, + public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -95,23 +95,23 @@ public function __construct( /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $acs_user_id, + public string|null $acs_user_id = null, /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\AkilesMetadata|null $akiles_metadata, + public AcsCredential\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public AcsCredential\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_number, + public string|null $card_number = null, /** * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $code, + public string|null $code = null, /** * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -127,7 +127,7 @@ public function __construct( /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -135,19 +135,19 @@ public function __construct( /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. */ - public bool|null $is_issued, + public bool|null $is_issued = null, /** * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ - public bool|null $is_latest_desired_state_synced_with_provider, + public bool|null $is_latest_desired_state_synced_with_provider = null, /** * Indicates whether Seam manages the credential. */ @@ -155,35 +155,35 @@ public function __construct( /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ - public bool|null $is_multi_phone_sync_credential, + public bool|null $is_multi_phone_sync_credential = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. */ - public bool|null $is_one_time_use, + public bool|null $is_one_time_use = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. */ - public string|null $issued_at, + public string|null $issued_at = null, /** * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. */ - public string|null $latest_desired_state_synced_with_provider_at, + public string|null $latest_desired_state_synced_with_provider_at = null, /** * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $parent_acs_credential_id, + public string|null $parent_acs_credential_id = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\VisionlineMetadata|null $visionline_metadata, + public AcsCredential\VisionlineMetadata|null $visionline_metadata = null, /** * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -214,7 +214,7 @@ public function __construct( /** * ID of the Akiles member PIN. */ - public string|null $member_pin_id, + public string|null $member_pin_id = null, ) {} } @@ -244,27 +244,27 @@ public function __construct( /** * 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. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Names of the doors to which to grant access in the Vostio access system. */ - public array|null $door_names, + public array|null $door_names = null, /** * Endpoint ID in the Vostio access system. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Key ID in the Vostio access system. */ - public string|null $key_id, + public string|null $key_id = null, /** * Key issuing request ID in the Vostio access system. */ - public string|null $key_issuing_request_id, + public string|null $key_issuing_request_id = null, /** * IDs of the guest entrances to override in the Vostio access system. */ - public array|null $override_guest_acs_entrance_ids, + public array|null $override_guest_acs_entrance_ids = null, ) {} } @@ -322,35 +322,35 @@ public function __construct( /** * 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. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Card function type in the Visionline access system. */ - public string|null $card_function_type, + public string|null $card_function_type = null, /** * ID of the card in the Visionline access system. */ - public string|null $card_id, + public string|null $card_id = null, /** * Common entrance IDs in the Visionline access system. */ - public array|null $common_acs_entrance_ids, + public array|null $common_acs_entrance_ids = null, /** * ID of the credential in the Visionline access system. */ - public string|null $credential_id, + public string|null $credential_id = null, /** * Guest entrance IDs in the Visionline access system. */ - public array|null $guest_acs_entrance_ids, + public array|null $guest_acs_entrance_ids = null, /** * Indicates whether the credential is valid. */ - public bool|null $is_valid, + public bool|null $is_valid = null, /** * IDs of the credentials to which you want to join. */ - public array|null $joiner_acs_credential_ids, + public array|null $joiner_acs_credential_ids = null, ) {} } diff --git a/src/Resources/AcsEntrance.php b/src/Resources/AcsEntrance.php index 59fdad0d..9c20b698 100644 --- a/src/Resources/AcsEntrance.php +++ b/src/Resources/AcsEntrance.php @@ -113,39 +113,39 @@ public function __construct( /** * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AkilesMetadata|null $akiles_metadata, + public AcsEntrance\AkilesMetadata|null $akiles_metadata = null, /** * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public AcsEntrance\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AvigilonAltaMetadata|null $avigilon_alta_metadata, + public AcsEntrance\AvigilonAltaMetadata|null $avigilon_alta_metadata = null, /** * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\BrivoMetadata|null $brivo_metadata, + public AcsEntrance\BrivoMetadata|null $brivo_metadata = null, /** * Indicates whether the ACS entrance can belong to a reservation via an access_grant.reservation_key. */ - public bool|null $can_belong_to_reservation, + public bool|null $can_belong_to_reservation = null, /** * Indicates whether the ACS entrance can be unlocked with card credentials. */ - public bool|null $can_unlock_with_card, + public bool|null $can_unlock_with_card = null, /** * Indicates whether the ACS entrance can be unlocked with cloud key credentials. */ - public bool|null $can_unlock_with_cloud_key, + public bool|null $can_unlock_with_cloud_key = null, /** * Indicates whether the ACS entrance can be unlocked with pin codes. */ - public bool|null $can_unlock_with_code, + public bool|null $can_unlock_with_code = null, /** * Indicates whether the ACS entrance can be unlocked with mobile key credentials. */ - public bool|null $can_unlock_with_mobile_key, + public bool|null $can_unlock_with_mobile_key = null, /** * ID of the [connected account](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -161,11 +161,11 @@ public function __construct( /** * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\DormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata, + public AcsEntrance\DormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata = null, /** * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata, + public AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata = null, /** * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -173,23 +173,23 @@ public function __construct( /** * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\HotekMetadata|null $hotek_metadata, + public AcsEntrance\HotekMetadata|null $hotek_metadata = null, /** * Indicates whether the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) is currently locked. */ - public bool|null $is_locked, + public bool|null $is_locked = null, /** * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\LatchMetadata|null $latch_metadata, + public AcsEntrance\LatchMetadata|null $latch_metadata = null, /** * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\SaltoKsMetadata|null $salto_ks_metadata, + public AcsEntrance\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata, + public AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata = null, /** * IDs of the spaces that the entrance is in. */ @@ -197,7 +197,7 @@ public function __construct( /** * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\VisionlineMetadata|null $visionline_metadata, + public AcsEntrance\VisionlineMetadata|null $visionline_metadata = null, /** * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -232,19 +232,19 @@ public function __construct( /** * Actions the gadget exposes (for example, open). */ - public array $actions, + public array|null $actions = null, /** * ID of the Akiles gadget. */ - public string|null $gadget_id, + public string|null $gadget_id = null, /** * ID of the Akiles site the gadget belongs to. */ - public string|null $site_id, + public string|null $site_id = null, /** * Name of the Akiles site the gadget belongs to. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -272,23 +272,23 @@ public function __construct( /** * Name of the door in the Vostio access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Number of the door in the Vostio access system. */ - public float|null $door_number, + public float|null $door_number = null, /** * Type of the door in the Vostio access system. */ - public string|null $door_type, + public string|null $door_type = null, /** * PMS ID of the door in the Vostio access system. */ - public string|null $pms_id, + public string|null $pms_id = null, /** * Indicates whether keys are allowed to set the door in stand open mode in the Vostio access system. */ - public bool|null $stand_open, + public bool|null $stand_open = null, ) {} } @@ -318,31 +318,31 @@ public function __construct( /** * Entry name for an Avigilon Alta system. */ - public string|null $entry_name, + public string|null $entry_name = null, /** * Total count of entry relays for an Avigilon Alta system. */ - public float|null $entry_relays_total_count, + public float|null $entry_relays_total_count = null, /** * Organization name for an Avigilon Alta system. */ - public string|null $org_name, + public string|null $org_name = null, /** * Site ID for an Avigilon Alta system. */ - public float|null $site_id, + public float|null $site_id = null, /** * Site name for an Avigilon Alta system. */ - public string|null $site_name, + public string|null $site_name = null, /** * Zone ID for an Avigilon Alta system. */ - public float|null $zone_id, + public float|null $zone_id = null, /** * Zone name for an Avigilon Alta system. */ - public string|null $zone_name, + public string|null $zone_name = null, ) {} } @@ -367,15 +367,15 @@ public function __construct( /** * ID of the access point in the Brivo access system. */ - public string|null $access_point_id, + public string|null $access_point_id = null, /** * ID of the site that the access point belongs to. */ - public float|null $site_id, + public float|null $site_id = null, /** * Name of the site that the access point belongs to. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -399,7 +399,7 @@ public function __construct( /** * Name of the access point in the dormakaba Ambiance access system. */ - public string|null $access_point_name, + public string|null $access_point_name = null, ) {} } @@ -423,7 +423,7 @@ public function __construct( /** * Type of access point profile in the dormakaba Community access system. */ - public string|null $access_point_profile, + public string|null $access_point_profile = null, ) {} } @@ -481,15 +481,15 @@ public function __construct( /** * Display name of the entrance. */ - public string|null $common_area_name, + public string|null $common_area_name = null, /** * Display name of the entrance. */ - public string|null $common_area_number, + public string|null $common_area_number = null, /** * Room number of the entrance. */ - public string|null $room_number, + public string|null $room_number = null, ) {} } @@ -515,19 +515,19 @@ public function __construct( /** * Accessibility type in the Latch access system. */ - public string|null $accessibility_type, + public string|null $accessibility_type = null, /** * Name of the door in the Latch access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Type of the door in the Latch access system. */ - public string|null $door_type, + public string|null $door_type = null, /** * Indicates whether the entrance is connected. */ - public bool|null $is_connected, + public bool|null $is_connected = null, ) {} } @@ -557,35 +557,35 @@ public function __construct( /** * Battery level of the door access device. */ - public string|null $battery_level, + public string|null $battery_level = null, /** * Name of the door in the Salto KS access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Indicates whether an intrusion alarm is active on the door. */ - public bool|null $intrusion_alarm, + public bool|null $intrusion_alarm = null, /** * Indicates whether the door is left open. */ - public bool|null $left_open_alarm, + public bool|null $left_open_alarm = null, /** * Type of the lock in the Salto KS access system. */ - public string|null $lock_type, + public string|null $lock_type = null, /** * Locked state of the door in the Salto KS access system. */ - public string|null $locked_state, + public string|null $locked_state = null, /** * Indicates whether the door access device is online. */ - public bool|null $online, + public bool|null $online = null, /** * Indicates whether privacy mode is enabled for the lock. */ - public bool|null $privacy_mode, + public bool|null $privacy_mode = null, ) {} } @@ -613,27 +613,27 @@ public function __construct( /** * Indicates whether AuditOnKeys is enabled for the door in the Salto Space access system. */ - public bool|null $audit_on_keys, + public bool|null $audit_on_keys = null, /** * Description of the door in the Salto Space access system. */ - public string|null $door_description, + public string|null $door_description = null, /** * Door ID in the Salto Space access system. */ - public string|null $door_id, + public string|null $door_id = null, /** * Name of the door in the Salto Space access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Description of the room in the Salto Space access system. */ - public string|null $room_description, + public string|null $room_description = null, /** * Name of the room in the Salto Space access system. */ - public string|null $room_name, + public string|null $room_name = null, ) {} } @@ -661,15 +661,15 @@ public function __construct( /** * Category of the door in the Visionline access system. */ - public string|null $door_category, + public string|null $door_category = null, /** * Name of the door in the Visionline access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Profile for the door in the Visionline access system. */ - public array $profiles, + public array|null $profiles = null, ) {} } @@ -725,11 +725,11 @@ public function __construct( /** * ID of the gadget action. */ - public string|null $id, + public string|null $id = null, /** * Name of the gadget action. */ - public string|null $name, + public string|null $name = null, ) {} } } @@ -757,11 +757,11 @@ public function __construct( /** * Door profile ID in the Visionline access system. */ - public string|null $visionline_door_profile_id, + public string|null $visionline_door_profile_id = null, /** * Door profile type in the Visionline access system. */ - public string|null $visionline_door_profile_type, + public string|null $visionline_door_profile_type = null, ) {} } } diff --git a/src/Resources/AcsSystem.php b/src/Resources/AcsSystem.php index b00ec137..642d242d 100644 --- a/src/Resources/AcsSystem.php +++ b/src/Resources/AcsSystem.php @@ -58,7 +58,7 @@ public function __construct( /** * Number of access groups in the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public float|null $acs_access_group_count, + public float|null $acs_access_group_count = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -66,7 +66,7 @@ public function __construct( /** * Number of users in the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public float|null $acs_user_count, + public float|null $acs_user_count = null, /** * ID of the connected account associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -84,7 +84,7 @@ public function __construct( /** * ID of the default credential manager `acs_system` for this [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public string|null $default_credential_manager_acs_system_id, + public string|null $default_credential_manager_acs_system_id = null, /** * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -92,11 +92,11 @@ public function __construct( /** * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * Alternative text for the [access control system](https://docs.seam.co/low-level-apis/access-systems) image. */ @@ -120,15 +120,15 @@ public function __construct( /** * @deprecated Use `external_type`. */ - public string|null $system_type, + public string|null $system_type = null, /** * @deprecated Use `external_type_display_name`. */ - public string|null $system_type_display_name, + public string|null $system_type_display_name = null, /** * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public AcsSystem\VisionlineMetadata|null $visionline_metadata, + public AcsSystem\VisionlineMetadata|null $visionline_metadata = null, /** * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -172,7 +172,7 @@ public function __construct( /** * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -222,15 +222,15 @@ public function __construct( /** * IP address or hostname of the main Visionline server relative to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge) on the local network. */ - public string|null $lan_address, + public string|null $lan_address = null, /** * Keyset loaded into a reader. Mobile keys and reader administration tools securely authenticate only with readers programmed with a matching keyset. */ - public string|null $mobile_access_uuid, + public string|null $mobile_access_uuid = null, /** * Unique ID assigned by the ASSA ABLOY licensing team that identifies each hotel in your credential manager. */ - public string|null $system_id, + public string|null $system_id = null, ) {} } @@ -265,7 +265,7 @@ public function __construct( /** * @deprecated this field is deprecated. */ - public array|null $misconfigured_acs_entrance_ids, + public array|null $misconfigured_acs_entrance_ids = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index e2bc4fbf..880d5331 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -70,7 +70,7 @@ public function __construct( /** * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. */ - public AcsUser\AccessSchedule|null $access_schedule, + public AcsUser\AccessSchedule|null $access_schedule = null, /** * ID of the [access system](https://docs.seam.co/low-level-apis/access-systems) that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ @@ -94,11 +94,11 @@ public function __construct( /** * @deprecated use email_address. */ - public string|null $email, + public string|null $email = null, /** * Email address of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $email_address, + public string|null $email_address = null, /** * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ @@ -106,19 +106,19 @@ public function __construct( /** * Brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * Full name of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $full_name, + public string|null $full_name = null, /** * ID of the HID access control system associated with the user. */ - public string|null $hid_acs_system_id, + public string|null $hid_acs_system_id = null, /** * Indicates whether Seam manages the access system user. */ @@ -126,39 +126,39 @@ public function __construct( /** * Indicates whether the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is currently [suspended](https://docs.seam.co/low-level-apis/access-systems/user-management/suspending-and-unsuspending-users). */ - public bool|null $is_suspended, + public bool|null $is_suspended = null, /** * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. */ - public array $pending_mutations, + public array|null $pending_mutations = null, /** * Phone number of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). */ - public string|null $phone_number, + public string|null $phone_number = null, /** * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public AcsUser\SaltoKsMetadata|null $salto_ks_metadata, + public AcsUser\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public AcsUser\SaltoSpaceMetadata|null $salto_space_metadata, + public AcsUser\SaltoSpaceMetadata|null $salto_space_metadata = null, /** * Email address of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $user_identity_email_address, + public string|null $user_identity_email_address = null, /** * Full name of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $user_identity_full_name, + public string|null $user_identity_full_name = null, /** * ID of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Phone number of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). */ - public string|null $user_identity_phone_number, + public string|null $user_identity_phone_number = null, /** * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ @@ -274,7 +274,7 @@ public function __construct( /** * Optional: When the user creation is scheduled to occur. */ - public string|null $scheduled_at, + public string|null $scheduled_at = null, public PendingMutations\To|null $to, /** * Whether the user is scheduled to be added to or removed from the access group. @@ -300,7 +300,7 @@ public function __construct( /** * Indicates whether the user holds an active subscription slot on the Salto KS site. Only subscribed users can unlock doors and count against the site's user-subscription limit. A user may not be subscribed because their access schedule has not started or has ended, the site has reached its subscription limit, or they were manually unsubscribed. This is distinct from `is_suspended`, which reflects whether the user has been explicitly blocked. */ - public bool|null $is_subscribed, + public bool|null $is_subscribed = null, ) {} } @@ -324,11 +324,11 @@ public function __construct( /** * Indicates whether AuditOpenings is enabled for the user in the Salto Space access system. */ - public bool|null $audit_openings, + public bool|null $audit_openings = null, /** * User ID in the Salto Space access system. */ - public string|null $user_id, + public string|null $user_id = null, ) {} } @@ -395,7 +395,7 @@ public function __construct( /** * Email address of the access system user. */ - public string|null $email_address, + public string|null $email_address = null, /** * Starting time for the access schedule. */ @@ -403,12 +403,12 @@ public function __construct( /** * Full name of the access system user. */ - public string|null $full_name, + public string|null $full_name = null, public bool|null $is_suspended, /** * Phone number of the access system user. */ - public string|null $phone_number, + public string|null $phone_number = null, /** * Starting time for the access schedule. */ @@ -447,7 +447,7 @@ public function __construct( /** * Email address of the access system user. */ - public string|null $email_address, + public string|null $email_address = null, /** * Starting time for the access schedule. */ @@ -455,12 +455,12 @@ public function __construct( /** * Full name of the access system user. */ - public string|null $full_name, + public string|null $full_name = null, public bool|null $is_suspended, /** * Phone number of the access system user. */ - public string|null $phone_number, + public string|null $phone_number = null, /** * Starting time for the access schedule. */ diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 04af6d8c..009c6529 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -183,7 +183,7 @@ public function __construct( /** * ID of the credential pool to which the credential belongs. */ - public string|null $acs_credential_pool_id, + public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -191,24 +191,24 @@ public function __construct( /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $acs_user_id, + public string|null $acs_user_id = null, /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public Result\AkilesMetadata|null $akiles_metadata, + public Result\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_number, + public string|null $card_number = null, /** * Token of the client session associated with the access method. */ - public string|null $client_session_token, - public string|null $code, + public string|null $client_session_token = null, + public string|null $code = null, /** * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -217,38 +217,38 @@ public function __construct( /** * ID of the customization profile associated with the access method. */ - public string|null $customization_profile_id, + public string|null $customization_profile_id = null, public string|null $display_name, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ - public string|null $ends_at, + public string|null $ends_at = null, public array $errors, /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * URL of the Instant Key for mobile key access methods. */ - public string|null $instant_key_url, + public string|null $instant_key_url = null, /** * 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. */ - public bool|null $is_assignment_required, + public bool|null $is_assignment_required = null, /** * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. */ - public bool|null $is_encoding_required, - public bool|null $is_issued, + public bool|null $is_encoding_required = null, + public bool|null $is_issued = null, /** * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ - public bool|null $is_latest_desired_state_synced_with_provider, + public bool|null $is_latest_desired_state_synced_with_provider = null, /** * Indicates whether Seam manages the credential. */ @@ -256,24 +256,24 @@ public function __construct( /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ - public bool|null $is_multi_phone_sync_credential, + public bool|null $is_multi_phone_sync_credential = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. */ - public bool|null $is_one_time_use, + public bool|null $is_one_time_use = null, /** * 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. */ - public bool|null $is_ready_for_assignment, + public bool|null $is_ready_for_assignment = null, /** * 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. */ - public bool|null $is_ready_for_encoding, - public string|null $issued_at, + public bool|null $is_ready_for_encoding = null, + public string|null $issued_at = null, /** * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. */ - public string|null $latest_desired_state_synced_with_provider_at, + public string|null $latest_desired_state_synced_with_provider_at = null, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ @@ -282,7 +282,7 @@ public function __construct( /** * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $parent_acs_credential_id, + public string|null $parent_acs_credential_id = null, /** * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. */ @@ -290,17 +290,17 @@ public function __construct( /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public Result\VisionlineMetadata|null $visionline_metadata, + public Result\VisionlineMetadata|null $visionline_metadata = null, public array $warnings, - public bool|null $was_confirmed_by_device, + public bool|null $was_confirmed_by_device = null, public string|null $workspace_id, ) {} } @@ -356,7 +356,7 @@ public function __construct( /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnEncoder\VisionlineMetadata|null $visionline_metadata, + public AcsCredentialOnEncoder\VisionlineMetadata|null $visionline_metadata = null, ) {} } @@ -440,7 +440,7 @@ public function __construct( /** * ID of the credential pool to which the credential belongs. */ - public string|null $acs_credential_pool_id, + public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -448,23 +448,23 @@ public function __construct( /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $acs_user_id, + public string|null $acs_user_id = null, /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\AkilesMetadata|null $akiles_metadata, + public AcsCredentialOnSeam\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public AcsCredentialOnSeam\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_number, + public string|null $card_number = null, /** * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $code, + public string|null $code = null, /** * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -480,7 +480,7 @@ public function __construct( /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -488,52 +488,52 @@ public function __construct( /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. */ - public bool|null $is_issued, + public bool|null $is_issued = null, /** * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ - public bool|null $is_latest_desired_state_synced_with_provider, + public bool|null $is_latest_desired_state_synced_with_provider = null, public bool|null $is_managed, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ - public bool|null $is_multi_phone_sync_credential, + public bool|null $is_multi_phone_sync_credential = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. */ - public bool|null $is_one_time_use, + public bool|null $is_one_time_use = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. */ - public string|null $issued_at, + public string|null $issued_at = null, /** * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. */ - public string|null $latest_desired_state_synced_with_provider_at, + public string|null $latest_desired_state_synced_with_provider_at = null, /** * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $parent_acs_credential_id, + public string|null $parent_acs_credential_id = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata, + public AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata = null, /** * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -562,7 +562,7 @@ public function __construct( /** * ID of the Akiles member PIN. */ - public string|null $member_pin_id, + public string|null $member_pin_id = null, ) {} } @@ -592,27 +592,27 @@ public function __construct( /** * 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. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Names of the doors to which to grant access in the Vostio access system. */ - public array|null $door_names, + public array|null $door_names = null, /** * Endpoint ID in the Vostio access system. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Key ID in the Vostio access system. */ - public string|null $key_id, + public string|null $key_id = null, /** * Key issuing request ID in the Vostio access system. */ - public string|null $key_issuing_request_id, + public string|null $key_issuing_request_id = null, /** * IDs of the guest entrances to override in the Vostio access system. */ - public array|null $override_guest_acs_entrance_ids, + public array|null $override_guest_acs_entrance_ids = null, ) {} } @@ -720,35 +720,35 @@ public function __construct( /** * 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. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Card function type in the Visionline access system. */ - public string|null $card_function_type, + public string|null $card_function_type = null, /** * ID of the card in the Visionline access system. */ - public string|null $card_id, + public string|null $card_id = null, /** * Common entrance IDs in the Visionline access system. */ - public array|null $common_acs_entrance_ids, + public array|null $common_acs_entrance_ids = null, /** * ID of the credential in the Visionline access system. */ - public string|null $credential_id, + public string|null $credential_id = null, /** * Guest entrance IDs in the Visionline access system. */ - public array|null $guest_acs_entrance_ids, + public array|null $guest_acs_entrance_ids = null, /** * Indicates whether the credential is valid. */ - public bool|null $is_valid, + public bool|null $is_valid = null, /** * IDs of the credentials to which you want to join. */ - public array|null $joiner_acs_credential_ids, + public array|null $joiner_acs_credential_ids = null, ) {} } @@ -783,15 +783,15 @@ public function __construct( /** * The PIN code that was assigned instead. */ - public string|null $new_code, + public string|null $new_code = null, /** * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $original_access_method_id, + public string|null $original_access_method_id = null, /** * The originally requested PIN code that could not be used. */ - public string|null $original_code, + public string|null $original_code = null, public string|null $warning_code, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. @@ -832,51 +832,51 @@ public function __construct( /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is cancelled. */ - public bool|null $cancelled, + public bool|null $cancelled = null, /** * Format of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_format, + public string|null $card_format = null, /** * Holder of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_holder, + public string|null $card_holder = null, /** * Card ID for the Visionline card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_id, + public string|null $card_id = null, /** * IDs of the common [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public array|null $common_acs_entrance_ids, + public array|null $common_acs_entrance_ids = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is discarded. */ - public bool|null $discarded, + public bool|null $discarded = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is expired. */ - public bool|null $expired, + public bool|null $expired = null, /** * IDs of the guest [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public array|null $guest_acs_entrance_ids, + public array|null $guest_acs_entrance_ids = null, /** * Number of issued cards associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public float|null $number_of_issued_cards, + public float|null $number_of_issued_cards = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overridden. */ - public bool|null $overridden, + public bool|null $overridden = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overwritten. */ - public bool|null $overwritten, + public bool|null $overwritten = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is pending auto-update. */ - public bool|null $pending_auto_update, + public bool|null $pending_auto_update = null, ) {} } } @@ -899,7 +899,7 @@ public function __construct( /** * ID of the Akiles member PIN. */ - public string|null $member_pin_id, + public string|null $member_pin_id = null, ) {} } @@ -929,27 +929,27 @@ public function __construct( /** * 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. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Names of the doors to which to grant access in the Vostio access system. */ - public array|null $door_names, + public array|null $door_names = null, /** * Endpoint ID in the Vostio access system. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Key ID in the Vostio access system. */ - public string|null $key_id, + public string|null $key_id = null, /** * Key issuing request ID in the Vostio access system. */ - public string|null $key_issuing_request_id, + public string|null $key_issuing_request_id = null, /** * IDs of the guest entrances to override in the Vostio access system. */ - public array|null $override_guest_acs_entrance_ids, + public array|null $override_guest_acs_entrance_ids = null, ) {} } @@ -1007,35 +1007,35 @@ public function __construct( /** * 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. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Card function type in the Visionline access system. */ - public string|null $card_function_type, + public string|null $card_function_type = null, /** * ID of the card in the Visionline access system. */ - public string|null $card_id, + public string|null $card_id = null, /** * Common entrance IDs in the Visionline access system. */ - public array|null $common_acs_entrance_ids, + public array|null $common_acs_entrance_ids = null, /** * ID of the credential in the Visionline access system. */ - public string|null $credential_id, + public string|null $credential_id = null, /** * Guest entrance IDs in the Visionline access system. */ - public array|null $guest_acs_entrance_ids, + public array|null $guest_acs_entrance_ids = null, /** * Indicates whether the credential is valid. */ - public bool|null $is_valid, + public bool|null $is_valid = null, /** * IDs of the credentials to which you want to join. */ - public array|null $joiner_acs_credential_ids, + public array|null $joiner_acs_credential_ids = null, ) {} } @@ -1070,11 +1070,11 @@ public function __construct( /** * The PIN code that was assigned instead. */ - public string|null $new_code, + public string|null $new_code = null, /** * The originally requested PIN code that could not be used. */ - public string|null $original_code, + public string|null $original_code = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/Batch.php b/src/Resources/Batch.php index 29a7b2da..7f5c92a0 100644 --- a/src/Resources/Batch.php +++ b/src/Resources/Batch.php @@ -52,15 +52,15 @@ public function __construct( * * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach and work across both standalone smart locks and access systems. Use the lower-level Access Codes API directly only when you specifically need to manage individual PIN codes. */ - public mixed $access_codes, + public mixed $access_codes = null, /** * 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. */ - public mixed $access_grants, + public mixed $access_grants = null, /** * 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. */ - public mixed $access_methods, + public mixed $access_methods = null, /** * 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. * @@ -68,7 +68,7 @@ public function __construct( * * To learn whether your access control system supports access groups, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). */ - public mixed $acs_access_groups, + public mixed $acs_access_groups = null, /** * Means by which an [access control system user](https://docs.seam.co/low-level-apis/access-systems/user-management) gains access at an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). The `acs_credential` object represents a [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) that provides an ACS user access within an [access control system](https://docs.seam.co/low-level-apis/access-systems). * @@ -78,7 +78,7 @@ public function __construct( * * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach. Use the lower-level ACS credential API directly only when you specifically need to manage individual credentials. */ - public mixed $acs_credentials, + public mixed $acs_credentials = null, /** * Represents a hardware device that encodes [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) data onto physical cards within an [access control system](https://docs.seam.co/low-level-apis/access-systems). * @@ -95,13 +95,13 @@ public function __construct( * * To verify if your access control system requires a card encoder, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). */ - public mixed $acs_encoders, + public mixed $acs_encoders = null, /** * Represents an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) within an [access control system](https://docs.seam.co/low-level-apis/access-systems). * * In an access control system, an entrance is a secured door, gate, zone, or other method of entry. You can list details for all the `acs_entrance` resources in your workspace or get these details for a specific `acs_entrance`. You can also list all entrances associated with a specific credential, and you can list all credentials associated with a specific entrance. */ - public mixed $acs_entrances, + public mixed $acs_entrances = null, /** * Represents an [access control system](https://docs.seam.co/low-level-apis/access-systems). * @@ -109,7 +109,7 @@ public function __construct( * * For details about the resources associated with an access control system, see the [access control systems namespace](https://docs.seam.co/api/acs). */ - public mixed $acs_systems, + public mixed $acs_systems = null, /** * Represents a [user](https://docs.seam.co/low-level-apis/access-systems/user-management) in an [access system](https://docs.seam.co/low-level-apis/access-systems). * @@ -117,7 +117,7 @@ public function __construct( * * For details about how to configure users in your access system, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). */ - public mixed $acs_users, + public mixed $acs_users = null, /** * Represents an action attempt that enables you to keep track of the progress of your action that affects a physical device or system.actions against a device. Action attempts are useful because the physical world is intrinsically asynchronous. * @@ -125,7 +125,7 @@ public function __construct( * * See also [Action Attempts](https://docs.seam.co/core-concepts/action-attempts). */ - public mixed $action_attempts, + public mixed $action_attempts = null, /** * Represents a [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). If you want to restrict your users' access to their own devices, use client sessions. * @@ -137,7 +137,7 @@ public function __construct( * * See also [Get Started with React](https://docs.seam.co/ui-components/overview/getting-started-with-seam-components/get-started-with-react-components-and-client-session-tokens). */ - public mixed $client_sessions, + public mixed $client_sessions = null, /** * Represents a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews). * @@ -151,43 +151,43 @@ public function __construct( * * To list all providers within a category, use `/devices/list_device_providers` with the desired `provider_category` filter. To list all provider keys, use `/devices/list_device_providers` with no filters. */ - public mixed $connect_webviews, + public mixed $connect_webviews = null, /** * Represents a [connected account](https://docs.seam.co/core-concepts/connected-accounts). 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. */ - public mixed $connected_accounts, + public mixed $connected_accounts = null, /** * Represents a [device](https://docs.seam.co/core-concepts/devices) that has been connected to Seam. */ - public mixed $devices, + public mixed $devices = null, /** * Represents an event. Events let you know when something interesting happens in your workspace. For example, when a lock is unlocked, Seam creates a `lock.unlocked` event. When a device's battery level is low, Seam creates a `device.battery_low` event. * * As with other API resources, you can retrieve an individual event or a list of events. Seam also provides a separate webhook system for sending the event objects directly to an endpoint on your sever. Manage webhooks through [Seam Console](https://console.seam.co). You can also use the webhooks sandbox in Seam Console to see the different payloads for each event and test them against your own endpoints. */ - public mixed $events, + public mixed $events = null, /** * Represents a Seam Instant Key. For issuing Bluetooth mobile keys, Instant Keys are the fastest way to share access. With a single API call, you can create a mobile key and send it through text or email or embed it in your own app. * * There’s no app to install, nor account to create. Your user just taps a link and gets a lightweight, native-feeling experience using iOS App Clip or Instant Apps on Android. Further, Instant Keys work offline, so even in areas with poor cellular or Wi-Fi, like elevator banks or concrete-walled hallways, the Instant Keys still work. */ - public mixed $instant_keys, + public mixed $instant_keys = null, /** * Represents a [noise threshold](https://docs.seam.co/capability-guides/noise-sensors/configure-noise-threshold-settings) for a [noise sensor](https://docs.seam.co/capability-guides/noise-sensors). Thresholds represent the limits of noise tolerated at a property, which can be customized for each hour of the day. Each device has its own default thresholds, but you can use the Seam API to modify them. */ - public mixed $noise_thresholds, + public mixed $noise_thresholds = null, /** * Represents a space that is a logical grouping of devices and entrances. You can assign access to an entire space, thereby making granting access more efficient. */ - public mixed $spaces, + public mixed $spaces = null, /** * Represents a thermostat daily program, consisting of a set of periods, each of which has a starting time and the key that identifies the climate preset to apply at the starting time. */ - public mixed $thermostat_daily_programs, + public mixed $thermostat_daily_programs = null, /** * Represents a [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) that activates a configured [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) on a [thermostat](https://docs.seam.co/capability-guides/thermostats) at a specified starting time and deactivates the climate preset at a specified ending time. */ - public mixed $thermostat_schedules, + public mixed $thermostat_schedules = null, /** * Represents an [unmanaged smart lock access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes). * @@ -201,19 +201,19 @@ public function __construct( * * - [Kwikset](https://docs.seam.co/device-and-system-integration-guides/kwikset-locks) */ - public mixed $unmanaged_access_codes, + public mixed $unmanaged_access_codes = null, /** * Represents an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). 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](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) on an unmanaged device are unmanaged. To control an unmanaged device with Seam, [convert it to a managed device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices#convert-an-unmanaged-device-to-managed). */ - public mixed $unmanaged_devices, + public mixed $unmanaged_devices = null, /** * Represents a [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with an application user account. */ - public mixed $user_identities, + public mixed $user_identities = null, /** * Represents a Seam [workspace](https://docs.seam.co/core-concepts/workspaces). A workspace is a top-level entity that encompasses all other resources below it, such as devices, connected accounts, and Connect Webviews. Seam provides two types of workspaces. A [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is a special type of workspace designed for testing code. Sandbox workspaces offer test device accounts and virtual devices that you can connect and control. This ability to work with virtual devices is quite handy because it removes the need to own physical devices from multiple brands. To connect real devices and systems to Seam, use a [production workspace](https://docs.seam.co/core-concepts/workspaces#production-workspaces). */ - public mixed $workspaces, + public mixed $workspaces = null, ) {} } } diff --git a/src/Resources/ClientSession.php b/src/Resources/ClientSession.php index 53ec9f7e..88083915 100644 --- a/src/Resources/ClientSession.php +++ b/src/Resources/ClientSession.php @@ -55,7 +55,7 @@ public function __construct( /** * Customer key associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). */ - public string|null $customer_key, + public string|null $customer_key = null, /** * Number of devices associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). */ @@ -75,7 +75,7 @@ public function __construct( /** * ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. * diff --git a/src/Resources/ConnectWebview.php b/src/Resources/ConnectWebview.php index 224eb740..c857baa0 100644 --- a/src/Resources/ConnectWebview.php +++ b/src/Resources/ConnectWebview.php @@ -95,7 +95,7 @@ public function __construct( /** * The customer key associated with this webview, if any. */ - public string|null $customer_key, + public string|null $customer_key = null, /** * Device selection mode of the Connect Webview. Supported values: `none`, `single`, `multiple`. */ diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index 3f6e237b..0bd1b645 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -53,7 +53,7 @@ public function __construct( /** * Type of connected account. */ - public string|null $account_type, + public string|null $account_type = null, /** * Display name for the connected account type. */ @@ -69,7 +69,7 @@ public function __construct( /** * Date and time at which the connected account was created. */ - public string|null $created_at, + public string|null $created_at = null, /** * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. */ @@ -77,15 +77,15 @@ public function __construct( /** * Your unique key for the customer associated with this connected account. */ - public string|null $customer_key, + public string|null $customer_key = null, /** * Default reservation check-in time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration — set during the connect_webview for providers like Lodgify whose API does not expose check-in times. */ - public string|null $default_checkin_time, + public string|null $default_checkin_time = null, /** * Default reservation check-out time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration. */ - public string|null $default_checkout_time, + public string|null $default_checkout_time = null, /** * Display name for the connected account. */ @@ -97,25 +97,25 @@ public function __construct( /** * For iCal connected accounts, the platform that produced the feed (for example, `airbnb`, `vrbo`, or `booking`), or `unknown` when it could not be determined. Intended for rendering the source platform's logo. */ - public string|null $ical_feed_origin, + public string|null $ical_feed_origin = null, /** * For iCal connected accounts, the feed URL for the connection. Sourced from the connector configuration. */ - public string|null $ical_url, + public string|null $ical_url = null, /** * Logo URL for the connected account provider. */ - public string|null $image_url, + public string|null $image_url = null, /** * IANA time zone (e.g. America/Los_Angeles) for this connected account. Sourced from the connector configuration. */ - public string|null $time_zone, + public string|null $time_zone = null, /** * User identifier associated with the connected account. * * @deprecated Use `display_name` instead. */ - public ConnectedAccount\UserIdentifier|null $user_identifier, + public ConnectedAccount\UserIdentifier|null $user_identifier = null, /** * Warnings associated with the connected account. */ @@ -162,11 +162,11 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, /** * Indicates whether the error is related specifically to the connected account. */ - public bool|null $is_connected_account_error, + public bool|null $is_connected_account_error = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -203,23 +203,23 @@ public function __construct( /** * API URL for the user identifier associated with the connected account. */ - public string|null $api_url, + public string|null $api_url = null, /** * Email address of the user identifier associated with the connected account. */ - public string|null $email, + public string|null $email = null, /** * Indicates whether the user identifier associated with the connected account is exclusive. */ - public bool|null $exclusive, + public bool|null $exclusive = null, /** * Phone number of the user identifier associated with the connected account. */ - public string|null $phone, + public string|null $phone = null, /** * Username of the user identifier associated with the connected account. */ - public string|null $username, + public string|null $username = null, ) {} } @@ -289,7 +289,7 @@ public function __construct( /** * Salto sites associated with the connected account that has an error. */ - public array $sites, + public array|null $sites = null, ) {} } } @@ -319,19 +319,19 @@ public function __construct( /** * ID of a Salto site associated with the connected account that has an error. */ - public string|null $site_id, + public string|null $site_id = null, /** * Name of a Salto site associated with the connected account that has an error. */ - public string|null $site_name, + public string|null $site_name = null, /** * Subscription limit of site users for a Salto site associated with the connected account that has an error. */ - public int|null $site_user_subscription_limit, + public int|null $site_user_subscription_limit = null, /** * Count of subscribed site users for a Salto site associated with the connected account that has an error. */ - public int|null $subscribed_site_user_count, + public int|null $subscribed_site_user_count = null, ) {} } } @@ -359,7 +359,7 @@ public function __construct( /** * Salto sites associated with the connected account that has a warning. */ - public array $sites, + public array|null $sites = null, ) {} } } @@ -389,19 +389,19 @@ public function __construct( /** * ID of a Salto site associated with the connected account that has a warning. */ - public string|null $site_id, + public string|null $site_id = null, /** * Name of a Salto site associated with the connected account that has a warning. */ - public string|null $site_name, + public string|null $site_name = null, /** * Subscription limit of site users for a Salto site associated with the connected account that has a warning. */ - public int|null $site_user_subscription_limit, + public int|null $site_user_subscription_limit = null, /** * Count of subscribed site users for a Salto site associated with the connected account that has a warning. */ - public int|null $subscribed_site_user_count, + public int|null $subscribed_site_user_count = null, ) {} } } diff --git a/src/Resources/Device.php b/src/Resources/Device.php index e7d7964e..4e960104 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -82,83 +82,83 @@ public function __construct( /** * Indicates whether the lock supports configuring automatic locking. */ - public bool|null $can_configure_auto_lock, + public bool|null $can_configure_auto_lock = null, /** * Indicates whether the thermostat supports cooling. */ - public bool|null $can_hvac_cool, + public bool|null $can_hvac_cool = null, /** * Indicates whether the thermostat supports heating. */ - public bool|null $can_hvac_heat, + public bool|null $can_hvac_heat = null, /** * Indicates whether the thermostat supports simultaneous heating and cooling. */ - public bool|null $can_hvac_heat_cool, + public bool|null $can_hvac_heat_cool = null, /** * Indicates whether the device supports programming offline access codes. */ - public bool|null $can_program_offline_access_codes, + public bool|null $can_program_offline_access_codes = null, /** * Indicates whether the device supports programming online access codes. */ - public bool|null $can_program_online_access_codes, + public bool|null $can_program_online_access_codes = null, /** * Indicates whether the thermostat supports different climate programs for each day of the week. */ - public bool|null $can_program_thermostat_programs_as_different_each_day, + public bool|null $can_program_thermostat_programs_as_different_each_day = null, /** * Indicates whether the thermostat supports a single climate program applied to every day. */ - public bool|null $can_program_thermostat_programs_as_same_each_day, + public bool|null $can_program_thermostat_programs_as_same_each_day = null, /** * Indicates whether the thermostat supports weekday/weekend climate programs. */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, + public bool|null $can_program_thermostat_programs_as_weekday_weekend = null, /** * Indicates whether the device supports remote locking. */ - public bool|null $can_remotely_lock, + public bool|null $can_remotely_lock = null, /** * Indicates whether the device supports remote unlocking. */ - public bool|null $can_remotely_unlock, + public bool|null $can_remotely_unlock = null, /** * Indicates whether the thermostat supports running climate programs. */ - public bool|null $can_run_thermostat_programs, + public bool|null $can_run_thermostat_programs = null, /** * Indicates whether the device supports simulating connection in a sandbox. */ - public bool|null $can_simulate_connection, + public bool|null $can_simulate_connection = null, /** * Indicates whether the device supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_disconnection, + public bool|null $can_simulate_disconnection = null, /** * Indicates whether the hub supports simulating connection in a sandbox. */ - public bool|null $can_simulate_hub_connection, + public bool|null $can_simulate_hub_connection = null, /** * Indicates whether the hub supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_hub_disconnection, + public bool|null $can_simulate_hub_disconnection = null, /** * Indicates whether the device supports simulating a paid subscription in a sandbox. */ - public bool|null $can_simulate_paid_subscription, + public bool|null $can_simulate_paid_subscription = null, /** * Indicates whether the device supports simulating removal in a sandbox. */ - public bool|null $can_simulate_removal, + public bool|null $can_simulate_removal = null, /** * Indicates whether the thermostat can be turned off. */ - public bool|null $can_turn_off_hvac, + public bool|null $can_turn_off_hvac = null, /** * Indicates whether the lock supports unlocking with an access code. */ - public bool|null $can_unlock_with_code, + public bool|null $can_unlock_with_code = null, /** * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). */ @@ -182,11 +182,11 @@ public function __construct( /** * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. */ - public Device\DeviceManufacturer|null $device_manufacturer, + public Device\DeviceManufacturer|null $device_manufacturer = null, /** * Provider of the device. Represents the third-party service through which the device is controlled. */ - public Device\DeviceProvider|null $device_provider, + public Device\DeviceProvider|null $device_provider = null, /** * Type of the device. */ @@ -206,11 +206,11 @@ public function __construct( /** * Location information for the device. */ - public Device\Location|null $location, + public Device\Location|null $location = null, /** * Optional nickname to describe the device, settable through Seam. */ - public string|null $nickname, + public string|null $nickname = null, /** * Properties of the device. */ @@ -257,7 +257,7 @@ public function __construct( /** * Image URL for the manufacturer logo. */ - public string|null $image_url, + public string|null $image_url = null, /** * Manufacturer identifier, such as `august`, `yale`, `salto`, and so on. */ @@ -295,7 +295,7 @@ public function __construct( /** * Image URL for the device provider. */ - public string|null $image_url, + public string|null $image_url = null, /** * Provider category. Indicates the third-party provider type, such as `stable`, for stable integrations, or `internal`, for internal integrations. */ @@ -336,7 +336,7 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** @@ -368,21 +368,21 @@ public function __construct( /** * Name of the device location. */ - public string|null $location_name, + public string|null $location_name = null, /** * Name of the room within the device location, when the provider reports one. */ - public string|null $room_name, + public string|null $room_name = null, /** * Time zone of the device location. */ - public string|null $time_zone, + public string|null $time_zone = null, /** * Time zone of the device location. * * @deprecated Use `time_zone` instead. */ - public string|null $timezone, + public string|null $timezone = null, ) {} } @@ -729,21 +729,21 @@ public function __construct( /** * Accessory keypad properties and state. */ - public Properties\AccessoryKeypad|null $accessory_keypad, + public Properties\AccessoryKeypad|null $accessory_keypad = null, /** * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). * * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. */ - public Properties\ActiveThermostatSchedule|null $active_thermostat_schedule, + public Properties\ActiveThermostatSchedule|null $active_thermostat_schedule = null, /** * ID of the active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ - public string|null $active_thermostat_schedule_id, + public string|null $active_thermostat_schedule_id = null, /** * Metadata for an Akiles device. */ - public Properties\AkilesMetadata|null $akiles_metadata, + public Properties\AkilesMetadata|null $akiles_metadata = null, /** * Appearance-related properties, as reported by the device. */ @@ -751,243 +751,243 @@ public function __construct( /** * Metadata for an Aqara device. */ - public Properties\AqaraMetadata|null $aqara_metadata, + public Properties\AqaraMetadata|null $aqara_metadata = null, /** * ASSA ABLOY Credential Service metadata for the phone. */ - public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, + public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata = null, /** * Metadata for an ASSA ABLOY Vostio system. */ - public Properties\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public Properties\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Metadata for an August device. */ - public Properties\AugustMetadata|null $august_metadata, + public Properties\AugustMetadata|null $august_metadata = null, /** * The delay in seconds before the lock automatically locks after being unlocked. */ - public float|null $auto_lock_delay_seconds, + public float|null $auto_lock_delay_seconds = null, /** * Indicates whether automatic locking is enabled. */ - public bool|null $auto_lock_enabled, + public bool|null $auto_lock_enabled = null, /** * Climate preset modes that the thermostat supports, such as "home", "away", "wake", "sleep", "occupied", and "unoccupied". */ - public array|null $available_climate_preset_modes, + public array|null $available_climate_preset_modes = null, /** * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. */ - public array $available_climate_presets, + public array|null $available_climate_presets = null, /** * Fan mode settings that the thermostat supports. */ - public array|null $available_fan_mode_settings, + public array|null $available_fan_mode_settings = null, /** * HVAC mode settings that the thermostat supports. */ - public array|null $available_hvac_mode_settings, + public array|null $available_hvac_mode_settings = null, /** * Metadata for an Avigilon Alta system. */ - public Properties\AvigilonAltaMetadata|null $avigilon_alta_metadata, + public Properties\AvigilonAltaMetadata|null $avigilon_alta_metadata = null, /** * Indicates whether the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is currently enabled for the device. To disable it, set this to `false` using [/devices/update](https://docs.seam.co/api/devices/update). */ - public bool|null $backup_access_code_pool_enabled, + public bool|null $backup_access_code_pool_enabled = null, /** * Represents the current status of the battery charge level. */ - public Properties\Battery|null $battery, + public Properties\Battery|null $battery = null, /** * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. */ - public float|null $battery_level, + public float|null $battery_level = null, /** * Metadata for a Brivo device. */ - public Properties\BrivoMetadata|null $brivo_metadata, + public Properties\BrivoMetadata|null $brivo_metadata = null, /** * 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. */ - public array $code_constraints, + public array|null $code_constraints = null, /** * Metadata for a ControlByWeb device. */ - public Properties\ControlbywebMetadata|null $controlbyweb_metadata, + public Properties\ControlbywebMetadata|null $controlbyweb_metadata = null, /** * Current climate setting. */ - public Properties\CurrentClimateSetting|null $current_climate_setting, + public Properties\CurrentClimateSetting|null $current_climate_setting = null, /** * Array of noise threshold IDs that are currently triggering. */ - public array|null $currently_triggering_noise_threshold_ids, + public array|null $currently_triggering_noise_threshold_ids = null, /** * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. */ - public Properties\DefaultClimateSetting|null $default_climate_setting, + public Properties\DefaultClimateSetting|null $default_climate_setting = null, /** * Indicates whether the door is open. */ - public bool|null $door_open, + public bool|null $door_open = null, /** * Metadata for a dormakaba Oracode device. */ - public Properties\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + public Properties\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Metadata for an ecobee device. */ - public Properties\EcobeeMetadata|null $ecobee_metadata, + public Properties\EcobeeMetadata|null $ecobee_metadata = null, /** * Key of the [fallback climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets/setting-the-fallback-climate-preset) for the thermostat. */ - public string|null $fallback_climate_preset_key, + public string|null $fallback_climate_preset_key = null, /** * @deprecated Use `current_climate_setting.fan_mode_setting` instead. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, /** * Metadata for a 4SUITES device. */ - public Properties\FourSuitesMetadata|null $four_suites_metadata, + public Properties\FourSuitesMetadata|null $four_suites_metadata = null, /** * Metadata for a Genie device. */ - public Properties\GenieMetadata|null $genie_metadata, + public Properties\GenieMetadata|null $genie_metadata = null, /** * Indicates whether the device has direct power. */ - public bool|null $has_direct_power, + public bool|null $has_direct_power = null, /** * Indicates whether the device supports native entry events. */ - public bool|null $has_native_entry_events, + public bool|null $has_native_entry_events = null, /** * Metadata for a Honeywell Resideo device. */ - public Properties\HoneywellResideoMetadata|null $honeywell_resideo_metadata, + public Properties\HoneywellResideoMetadata|null $honeywell_resideo_metadata = null, /** * Metadata for an igloo device. */ - public Properties\IglooMetadata|null $igloo_metadata, + public Properties\IglooMetadata|null $igloo_metadata = null, /** * Metadata for an igloohome device. */ - public Properties\IgloohomeMetadata|null $igloohome_metadata, + public Properties\IgloohomeMetadata|null $igloohome_metadata = null, /** * Alt text for the device image. */ - public string|null $image_alt_text, + public string|null $image_alt_text = null, /** * Image URL for the device. */ - public string|null $image_url, + public string|null $image_url = null, /** * Indicates whether the connected HVAC system is currently cooling, as reported by the thermostat. */ - public bool|null $is_cooling, + public bool|null $is_cooling = null, /** * Indicates whether the fan in the connected HVAC system is currently running, as reported by the thermostat. */ - public bool|null $is_fan_running, + public bool|null $is_fan_running = null, /** * Indicates whether the connected HVAC system is currently heating, as reported by the thermostat. */ - public bool|null $is_heating, + public bool|null $is_heating = null, /** * Indicates whether the current thermostat settings differ from the most recent active program or schedule that Seam activated. For this condition to occur, `current_climate_setting.manual_override_allowed` must also be `true`. */ - public bool|null $is_temporary_manual_override_active, + public bool|null $is_temporary_manual_override_active = null, /** * Metadata for a KeyNest device. */ - public Properties\KeynestMetadata|null $keynest_metadata, + public Properties\KeynestMetadata|null $keynest_metadata = null, /** * Keypad battery status. */ - public Properties\KeypadBattery|null $keypad_battery, + public Properties\KeypadBattery|null $keypad_battery = null, /** * Metadata for a Kisi device. */ - public Properties\KisiMetadata|null $kisi_metadata, + public Properties\KisiMetadata|null $kisi_metadata = null, /** * Metadata for a Korelock device. */ - public Properties\KorelockMetadata|null $korelock_metadata, + public Properties\KorelockMetadata|null $korelock_metadata = null, /** * Metadata for a Kwikset device. */ - public Properties\KwiksetMetadata|null $kwikset_metadata, + public Properties\KwiksetMetadata|null $kwikset_metadata = null, /** * Indicates whether the lock is locked. */ - public bool|null $locked, + public bool|null $locked = null, /** * Metadata for a Lockly device. */ - public Properties\LocklyMetadata|null $lockly_metadata, + public Properties\LocklyMetadata|null $lockly_metadata = null, /** * 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. */ - public string|null $manufacturer, + public string|null $manufacturer = null, /** * Maximum number of active access codes that the device supports. */ - public float|null $max_active_codes_supported, + public float|null $max_active_codes_supported = null, /** * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. */ - public float|null $max_cooling_set_point_celsius, + public float|null $max_cooling_set_point_celsius = null, /** * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. */ - public float|null $max_cooling_set_point_fahrenheit, + public float|null $max_cooling_set_point_fahrenheit = null, /** * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. */ - public float|null $max_heating_set_point_celsius, + public float|null $max_heating_set_point_celsius = null, /** * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. */ - public float|null $max_heating_set_point_fahrenheit, + public float|null $max_heating_set_point_fahrenheit = null, /** * Maximum number of periods that the thermostat can support per day. For example, if the thermostat supports 4 periods per day, this value is 4. */ - public float|null $max_thermostat_daily_program_periods_per_day, + public float|null $max_thermostat_daily_program_periods_per_day = null, /** * Maximum number of climate presets that the thermostat can support for weekly programming. */ - public float|null $max_unique_climate_presets_per_thermostat_weekly_program, + public float|null $max_unique_climate_presets_per_thermostat_weekly_program = null, /** * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. */ - public float|null $min_cooling_set_point_celsius, + public float|null $min_cooling_set_point_celsius = null, /** * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. */ - public float|null $min_cooling_set_point_fahrenheit, + public float|null $min_cooling_set_point_fahrenheit = null, /** * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °C between the cooling and heating set points when in heat-cool (auto) mode. */ - public float|null $min_heating_cooling_delta_celsius, + public float|null $min_heating_cooling_delta_celsius = null, /** * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °F between the cooling and heating set points when in heat-cool (auto) mode. */ - public float|null $min_heating_cooling_delta_fahrenheit, + public float|null $min_heating_cooling_delta_fahrenheit = null, /** * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. */ - public float|null $min_heating_set_point_celsius, + public float|null $min_heating_set_point_celsius = null, /** * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. */ - public float|null $min_heating_set_point_fahrenheit, + public float|null $min_heating_set_point_fahrenheit = null, /** * Metadata for a Minut device. */ - public Properties\MinutMetadata|null $minut_metadata, + public Properties\MinutMetadata|null $minut_metadata = null, /** * Device model-related properties. */ @@ -1001,33 +1001,33 @@ public function __construct( /** * Metadata for a Google Nest device. */ - public Properties\NestMetadata|null $nest_metadata, + public Properties\NestMetadata|null $nest_metadata = null, /** * Indicates current noise level in decibels, if the device supports noise detection. */ - public float|null $noise_level_decibels, + public float|null $noise_level_decibels = null, /** * Metadata for a NoiseAware device. */ - public Properties\NoiseawareMetadata|null $noiseaware_metadata, + public Properties\NoiseawareMetadata|null $noiseaware_metadata = null, /** * Metadata for a Nuki device. */ - public Properties\NukiMetadata|null $nuki_metadata, + public Properties\NukiMetadata|null $nuki_metadata = null, /** * Indicates whether it is currently possible to use offline access codes for the device. * * @deprecated use device.can_program_offline_access_codes */ - public bool|null $offline_access_codes_enabled, + public bool|null $offline_access_codes_enabled = null, /** * 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. */ - public array $offline_time_frame_options, + public array|null $offline_time_frame_options = null, /** * Metadata for an Omnitec device. */ - public Properties\OmnitecMetadata|null $omnitec_metadata, + public Properties\OmnitecMetadata|null $omnitec_metadata = null, /** * Indicates whether the device is online. */ @@ -1037,125 +1037,125 @@ public function __construct( * * @deprecated use device.can_program_online_access_codes */ - public bool|null $online_access_codes_enabled, + public bool|null $online_access_codes_enabled = null, /** * 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. */ - public array $online_time_frame_options, + public array|null $online_time_frame_options = null, /** * Reported relative humidity, as a value between 0 and 1, inclusive. */ - public float|null $relative_humidity, + public float|null $relative_humidity = null, /** * Metadata for a Ring device. */ - public Properties\RingMetadata|null $ring_metadata, + public Properties\RingMetadata|null $ring_metadata = null, /** * Metadata for a Salto KS device. */ - public Properties\SaltoKsMetadata|null $salto_ks_metadata, + public Properties\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Metada for a Salto device. * * @deprecated Use `salto_ks_metadata` instead. */ - public Properties\SaltoMetadata|null $salto_metadata, + public Properties\SaltoMetadata|null $salto_metadata = null, /** * Salto Space credential service metadata for the phone. */ - public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, + public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata = null, /** * Metadata for a Schlage device. */ - public Properties\SchlageMetadata|null $schlage_metadata, + public Properties\SchlageMetadata|null $schlage_metadata = null, /** * Metadata for Seam Bridge. */ - public Properties\SeamBridgeMetadata|null $seam_bridge_metadata, + public Properties\SeamBridgeMetadata|null $seam_bridge_metadata = null, /** * Metadata for a Sensi device. */ - public Properties\SensiMetadata|null $sensi_metadata, + public Properties\SensiMetadata|null $sensi_metadata = null, /** * Serial number of the device. */ - public string|null $serial_number, + public string|null $serial_number = null, /** * Metadata for a SmartThings device. */ - public Properties\SmartthingsMetadata|null $smartthings_metadata, + public Properties\SmartthingsMetadata|null $smartthings_metadata = null, /** * Supported code lengths for access codes. */ - public array|null $supported_code_lengths, + public array|null $supported_code_lengths = null, /** * @deprecated use device.properties.model.can_connect_accessory_keypad */ - public bool|null $supports_accessory_keypad, + public bool|null $supports_accessory_keypad = null, /** * Indicates whether the device supports a [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes). */ - public bool|null $supports_backup_access_code_pool, + public bool|null $supports_backup_access_code_pool = null, /** * @deprecated use offline_access_codes_enabled */ - public bool|null $supports_offline_access_codes, + public bool|null $supports_offline_access_codes = null, /** * Metadata for a tado° device. */ - public Properties\TadoMetadata|null $tado_metadata, + public Properties\TadoMetadata|null $tado_metadata = null, /** * Metadata for a Tedee device. */ - public Properties\TedeeMetadata|null $tedee_metadata, + public Properties\TedeeMetadata|null $tedee_metadata = null, /** * Reported temperature in °C. */ - public float|null $temperature_celsius, + public float|null $temperature_celsius = null, /** * Reported temperature in °F. */ - public float|null $temperature_fahrenheit, + public float|null $temperature_fahrenheit = null, /** * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. */ - public Properties\TemperatureThreshold|null $temperature_threshold, + public Properties\TemperatureThreshold|null $temperature_threshold = null, /** * Precision of the thermostat's period in minutes. For example, if the thermostat supports 15-minute periods, this value is 15. All values are relative to the top of the hour, so for 15 minutes, the periods would be 0, 15, 30, and 45 minutes past the hour. */ - public float|null $thermostat_daily_program_period_precision_minutes, + public float|null $thermostat_daily_program_period_precision_minutes = null, /** * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. */ - public array $thermostat_daily_programs, + public array|null $thermostat_daily_programs = null, /** * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. */ - public Properties\ThermostatWeeklyProgram|null $thermostat_weekly_program, + public Properties\ThermostatWeeklyProgram|null $thermostat_weekly_program = null, /** * Metadata for a TTLock device. */ - public Properties\TtlockMetadata|null $ttlock_metadata, + public Properties\TtlockMetadata|null $ttlock_metadata = null, /** * Metadata for a 2N device. */ - public Properties\TwoNMetadata|null $two_n_metadata, + public Properties\TwoNMetadata|null $two_n_metadata = null, /** * Metadata for an Ultraloq device. */ - public Properties\UltraloqMetadata|null $ultraloq_metadata, + public Properties\UltraloqMetadata|null $ultraloq_metadata = null, /** * Metadata for an ASSA ABLOY Visionline system. */ - public Properties\VisionlineMetadata|null $visionline_metadata, + public Properties\VisionlineMetadata|null $visionline_metadata = null, /** * Metadata for a Wyze device. */ - public Properties\WyzeMetadata|null $wyze_metadata, + public Properties\WyzeMetadata|null $wyze_metadata = null, /** * Metadata for a Yacan device. */ - public Properties\YacanMetadata|null $yacan_metadata, + public Properties\YacanMetadata|null $yacan_metadata = null, ) {} } @@ -1228,7 +1228,7 @@ public function __construct( /** * Keypad battery properties. */ - public AccessoryKeypad\Battery|null $battery, + public AccessoryKeypad\Battery|null $battery = null, /** * Indicates if an accessory keypad is connected to the device. */ @@ -1315,11 +1315,11 @@ public function __construct( /** * @deprecated use device.properties.model.can_connect_accessory_keypad */ - public bool|null $accessory_keypad_supported, + public bool|null $accessory_keypad_supported = null, /** * Indicates whether the device can connect a accessory keypad. */ - public bool|null $can_connect_accessory_keypad, + public bool|null $can_connect_accessory_keypad = null, /** * Display name of the device model. */ @@ -1327,7 +1327,7 @@ public function __construct( /** * Indicates whether the device has a built in accessory keypad. */ - public bool|null $has_built_in_keypad, + public bool|null $has_built_in_keypad = null, /** * Display name that corresponds to the manufacturer-specific terminology for the device. */ @@ -1335,11 +1335,11 @@ public function __construct( /** * @deprecated use device.can_program_offline_access_codes. */ - public bool|null $offline_access_codes_supported, + public bool|null $offline_access_codes_supported = null, /** * @deprecated use device.can_program_online_access_codes. */ - public bool|null $online_access_codes_supported, + public bool|null $online_access_codes_supported = null, ) {} } @@ -1371,11 +1371,11 @@ public function __construct( /** * Endpoints associated with the phone. */ - public array $endpoints, + public array|null $endpoints = null, /** * Indicates whether the credential service has active endpoints associated with the phone. */ - public bool|null $has_active_endpoint, + public bool|null $has_active_endpoint = null, ) {} } @@ -1397,7 +1397,7 @@ public function __construct( /** * Indicates whether the credential service has an active associated phone. */ - public bool|null $has_active_phone, + public bool|null $has_active_phone = null, ) {} } @@ -1423,19 +1423,19 @@ public function __construct( /** * Group ID to which to add users for an Akiles device. */ - public string|null $_member_group_id, + public string|null $_member_group_id = null, /** * Gadget ID for an Akiles device. */ - public string|null $gadget_id, + public string|null $gadget_id = null, /** * Gadget name for an Akiles device. */ - public string|null $gadget_name, + public string|null $gadget_name = null, /** * Product name for an Akiles device. */ - public string|null $product_name, + public string|null $product_name = null, ) {} } @@ -1465,35 +1465,35 @@ public function __construct( /** * Device name for an Aqara device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device ID (did) for an Aqara device. */ - public string|null $did, + public string|null $did = null, /** * Firmware version for an Aqara device. */ - public string|null $firmware_version, + public string|null $firmware_version = null, /** * Model identifier for an Aqara device. */ - public string|null $model, + public string|null $model = null, /** * Model type for an Aqara device. */ - public float|null $model_type, + public float|null $model_type = null, /** * Parent gateway device ID for an Aqara device. */ - public string|null $parent_did, + public string|null $parent_did = null, /** * Position (room) ID for an Aqara device. */ - public string|null $position_id, + public string|null $position_id = null, /** * Time zone reported for an Aqara device (e.g. GMT-07:00). */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } @@ -1515,7 +1515,7 @@ public function __construct( /** * Encoder name for an ASSA ABLOY Vostio system. */ - public string|null $encoder_name, + public string|null $encoder_name = null, ) {} } @@ -1544,31 +1544,31 @@ public function __construct( /** * Indicates whether an August device has a keypad. */ - public bool|null $has_keypad, + public bool|null $has_keypad = null, /** * House ID for an August device. */ - public string|null $house_id, + public string|null $house_id = null, /** * House name for an August device. */ - public string|null $house_name, + public string|null $house_name = null, /** * Keypad battery level for an August device. */ - public string|null $keypad_battery_level, + public string|null $keypad_battery_level = null, /** * Lock ID for an August device. */ - public string|null $lock_id, + public string|null $lock_id = null, /** * Lock name for an August device. */ - public string|null $lock_name, + public string|null $lock_name = null, /** * Model for an August device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -1598,31 +1598,31 @@ public function __construct( /** * Entry name for an Avigilon Alta system. */ - public string|null $entry_name, + public string|null $entry_name = null, /** * Total count of entry relays for an Avigilon Alta system. */ - public float|null $entry_relays_total_count, + public float|null $entry_relays_total_count = null, /** * Organization name for an Avigilon Alta system. */ - public string|null $org_name, + public string|null $org_name = null, /** * Site ID for an Avigilon Alta system. */ - public float|null $site_id, + public float|null $site_id = null, /** * Site name for an Avigilon Alta system. */ - public string|null $site_name, + public string|null $site_name = null, /** * Zone ID for an Avigilon Alta system. */ - public float|null $zone_id, + public float|null $zone_id = null, /** * Zone name for an Avigilon Alta system. */ - public string|null $zone_name, + public string|null $zone_name = null, ) {} } @@ -1646,11 +1646,11 @@ public function __construct( /** * Indicates whether the Brivo access point has activation (remote unlock) enabled. */ - public bool|null $activation_enabled, + public bool|null $activation_enabled = null, /** * Device name for a Brivo device. */ - public string|null $device_name, + public string|null $device_name = null, ) {} } @@ -1675,15 +1675,15 @@ public function __construct( /** * Device ID for a ControlByWeb device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a ControlByWeb device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Relay name for a ControlByWeb device. */ - public string|null $relay_name, + public string|null $relay_name = null, ) {} } @@ -1721,37 +1721,37 @@ public function __construct( /** * Device ID for a dormakaba Oracode device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Door ID for a dormakaba Oracode device. */ - public float|null $door_id, + public float|null $door_id = null, /** * Indicates whether a door is wireless for a dormakaba Oracode device. */ - public bool|null $door_is_wireless, + public bool|null $door_is_wireless = null, /** * Door name for a dormakaba Oracode device. */ - public string|null $door_name, + public string|null $door_name = null, /** * IANA time zone for a dormakaba Oracode device. */ - public string|null $iana_timezone, + public string|null $iana_timezone = null, /** * Predefined time slots for a dormakaba Oracode device. */ - public array $predefined_time_slots, + public array|null $predefined_time_slots = null, /** * Site ID for a dormakaba Oracode device. * * @deprecated Previously marked as "@DEPRECATED." */ - public float|null $site_id, + public float|null $site_id = null, /** * Site name for a dormakaba Oracode device. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -1775,11 +1775,11 @@ public function __construct( /** * Device name for an ecobee device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device ID for an ecobee device. */ - public string|null $ecobee_device_id, + public string|null $ecobee_device_id = null, ) {} } @@ -1805,15 +1805,15 @@ public function __construct( /** * Device ID for a 4SUITES device. */ - public float|null $device_id, + public float|null $device_id = null, /** * Device name for a 4SUITES device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Reclose delay, in seconds, for a 4SUITES device. */ - public float|null $reclose_delay_in_seconds, + public float|null $reclose_delay_in_seconds = null, ) {} } @@ -1837,11 +1837,11 @@ public function __construct( /** * Lock name for a Genie device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Door name for a Genie device. */ - public string|null $door_name, + public string|null $door_name = null, ) {} } @@ -1867,11 +1867,11 @@ public function __construct( /** * Device name for a Honeywell Resideo device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device ID for a Honeywell Resideo device. */ - public string|null $honeywell_resideo_device_id, + public string|null $honeywell_resideo_device_id = null, ) {} } @@ -1896,15 +1896,15 @@ public function __construct( /** * Bridge ID for an igloo device. */ - public string|null $bridge_id, + public string|null $bridge_id = null, /** * Device ID for an igloo device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Model for an igloo device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -1933,27 +1933,27 @@ public function __construct( /** * Bridge ID for an igloohome device. */ - public string|null $bridge_id, + public string|null $bridge_id = null, /** * Bridge name for an igloohome device. */ - public string|null $bridge_name, + public string|null $bridge_name = null, /** * Device ID for an igloohome device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for an igloohome device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Indicates whether a keypad is linked to a bridge for an igloohome device. */ - public bool|null $is_accessory_keypad_linked_to_bridge, + public bool|null $is_accessory_keypad_linked_to_bridge = null, /** * Keypad ID for an igloohome device. */ - public string|null $keypad_id, + public string|null $keypad_id = null, ) {} } @@ -1998,87 +1998,87 @@ public function __construct( /** * Address for a KeyNest device. */ - public string|null $address, + public string|null $address = null, /** * Current or last store ID for a KeyNest device. */ - public float|null $current_or_last_store_id, + public float|null $current_or_last_store_id = null, /** * Current status for a KeyNest device. */ - public string|null $current_status, + public string|null $current_status = null, /** * Current user company for a KeyNest device. */ - public string|null $current_user_company, + public string|null $current_user_company = null, /** * Current user email for a KeyNest device. */ - public string|null $current_user_email, + public string|null $current_user_email = null, /** * Current user name for a KeyNest device. */ - public string|null $current_user_name, + public string|null $current_user_name = null, /** * Current user phone number for a KeyNest device. */ - public string|null $current_user_phone_number, + public string|null $current_user_phone_number = null, /** * Default office ID for a KeyNest device. */ - public float|null $default_office_id, + public float|null $default_office_id = null, /** * Device name for a KeyNest device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Fob ID for a KeyNest device. */ - public float|null $fob_id, + public float|null $fob_id = null, /** * Handover method for a KeyNest device. */ - public string|null $handover_method, + public string|null $handover_method = null, /** * Whether the KeyNest device has a photo. */ - public bool|null $has_photo, + public bool|null $has_photo = null, /** * Whether the key is in a locker that does not support the access codes API. */ - public bool|null $is_quadient_locker, + public bool|null $is_quadient_locker = null, /** * Key ID for a KeyNest device. */ - public string|null $key_id, + public string|null $key_id = null, /** * Key notes for a KeyNest device. */ - public string|null $key_notes, + public string|null $key_notes = null, /** * KeyNest app user for a KeyNest device. */ - public string|null $keynest_app_user, + public string|null $keynest_app_user = null, /** * Last movement timestamp for a KeyNest device. */ - public string|null $last_movement, + public string|null $last_movement = null, /** * Property ID for a KeyNest device. */ - public string|null $property_id, + public string|null $property_id = null, /** * Property postcode for a KeyNest device. */ - public string|null $property_postcode, + public string|null $property_postcode = null, /** * Status type for a KeyNest device. */ - public string|null $status_type, + public string|null $status_type = null, /** * Subscription plan for a KeyNest device. */ - public string|null $subscription_plan, + public string|null $subscription_plan = null, ) {} } @@ -2104,19 +2104,19 @@ public function __construct( /** * Description for a Kisi device. */ - public string|null $description, + public string|null $description = null, /** * Lock ID for a Kisi device. */ - public float|null $lock_id, + public float|null $lock_id = null, /** * Lock name for a Kisi device. */ - public string|null $lock_name, + public string|null $lock_name = null, /** * Place name for a Kisi device. */ - public string|null $place_name, + public string|null $place_name = null, ) {} } @@ -2145,31 +2145,31 @@ public function __construct( /** * Device ID for a Korelock device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Korelock device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Firmware version for a Korelock device. */ - public string|null $firmware_version, + public string|null $firmware_version = null, /** * Location ID for a Korelock device. Required for timebound access codes. */ - public string|null $location_id, + public string|null $location_id = null, /** * Model code for a Korelock device. */ - public string|null $model_code, + public string|null $model_code = null, /** * Serial number for a Korelock device. */ - public string|null $serial_number, + public string|null $serial_number = null, /** * WiFi signal strength (0-1) for a Korelock device. */ - public float|null $wifi_signal_strength, + public float|null $wifi_signal_strength = null, ) {} } @@ -2194,15 +2194,15 @@ public function __construct( /** * Device ID for a Kwikset device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Kwikset device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Model number for a Kwikset device. */ - public string|null $model_number, + public string|null $model_number = null, ) {} } @@ -2227,15 +2227,15 @@ public function __construct( /** * Device ID for a Lockly device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Lockly device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Model for a Lockly device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -2264,15 +2264,15 @@ public function __construct( /** * Device ID for a Minut device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Minut device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Latest sensor values for a Minut device. */ - public MinutMetadata\LatestSensorValues|null $latest_sensor_values, + public MinutMetadata\LatestSensorValues|null $latest_sensor_values = null, ) {} } @@ -2300,27 +2300,27 @@ public function __construct( /** * Custom device name for a Google Nest device. The device owner sets this value. */ - public string|null $device_custom_name, + public string|null $device_custom_name = null, /** * Device name for a Google Nest device. Google sets this value. */ - public string|null $device_name, + public string|null $device_name = null, /** * Display name for a Google Nest device. */ - public string|null $display_name, + public string|null $display_name = null, /** * Device ID for a Google Nest device. */ - public string|null $nest_device_id, + public string|null $nest_device_id = null, /** * ID of the Google Nest structure containing the device. */ - public string|null $nest_structure_id, + public string|null $nest_structure_id = null, /** * Name of the Google Nest structure containing the device. The device owner sets this value. */ - public string|null $structure_name, + public string|null $structure_name = null, ) {} } @@ -2347,23 +2347,23 @@ public function __construct( /** * Device ID for a NoiseAware device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device model for a NoiseAware device. */ - public string|null $device_model, + public string|null $device_model = null, /** * Device name for a NoiseAware device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Noise level, in decibels, for a NoiseAware device. */ - public float|null $noise_level_decibel, + public float|null $noise_level_decibel = null, /** * Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. */ - public float|null $noise_level_nrs, + public float|null $noise_level_nrs = null, ) {} } @@ -2390,23 +2390,23 @@ public function __construct( /** * Device ID for a Nuki device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Nuki device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Indicates whether keypad 2 is paired for a Nuki device. */ - public bool|null $keypad_2_paired, + public bool|null $keypad_2_paired = null, /** * Indicates whether the keypad battery is in a critical state for a Nuki device. */ - public bool|null $keypad_battery_critical, + public bool|null $keypad_battery_critical = null, /** * Indicates whether the keypad is paired for a Nuki device. */ - public bool|null $keypad_paired, + public bool|null $keypad_paired = null, ) {} } @@ -2435,31 +2435,31 @@ public function __construct( /** * Whether the Omnitec lock has a connected gateway for remote operations. */ - public bool|null $has_gateway, + public bool|null $has_gateway = null, /** * Operator-assigned alias for an Omnitec device. */ - public string|null $lock_alias, + public string|null $lock_alias = null, /** * Lock ID for an Omnitec device. */ - public float|null $lock_id, + public float|null $lock_id = null, /** * Bluetooth MAC address for an Omnitec device. */ - public string|null $lock_mac, + public string|null $lock_mac = null, /** * Lock name for an Omnitec device. */ - public string|null $lock_name, + public string|null $lock_name = null, /** * IANA time zone for the Omnitec device, used to schedule time-bound access codes at the correct local time (accounting for DST). */ - public string|null $time_zone, + public string|null $time_zone = null, /** * Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. */ - public float|null $timezone_raw_offset_ms, + public float|null $timezone_raw_offset_ms = null, ) {} } @@ -2483,11 +2483,11 @@ public function __construct( /** * Device ID for a Ring device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Ring device. */ - public string|null $device_name, + public string|null $device_name = null, ) {} } @@ -2519,39 +2519,39 @@ public function __construct( /** * Battery level for a Salto KS device. */ - public string|null $battery_level, + public string|null $battery_level = null, /** * Customer reference for a Salto KS device. */ - public string|null $customer_reference, + public string|null $customer_reference = null, /** * Indicates whether the site has a Salto KS subscription that supports custom PINs. */ - public bool|null $has_custom_pin_subscription, + public bool|null $has_custom_pin_subscription = null, /** * Lock ID for a Salto KS device. */ - public string|null $lock_id, + public string|null $lock_id = null, /** * Lock type for a Salto KS device. */ - public string|null $lock_type, + public string|null $lock_type = null, /** * Locked state for a Salto KS device. */ - public string|null $locked_state, + public string|null $locked_state = null, /** * Model for a Salto KS device. */ - public string|null $model, + public string|null $model = null, /** * Site ID for the Salto KS site to which the device belongs. */ - public string|null $site_id, + public string|null $site_id = null, /** * Site name for the Salto KS site to which the device belongs. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -2583,35 +2583,35 @@ public function __construct( /** * Battery level for a Salto device. */ - public string|null $battery_level, + public string|null $battery_level = null, /** * Customer reference for a Salto device. */ - public string|null $customer_reference, + public string|null $customer_reference = null, /** * Lock ID for a Salto device. */ - public string|null $lock_id, + public string|null $lock_id = null, /** * Lock type for a Salto device. */ - public string|null $lock_type, + public string|null $lock_type = null, /** * Locked state for a Salto device. */ - public string|null $locked_state, + public string|null $locked_state = null, /** * Model for a Salto device. */ - public string|null $model, + public string|null $model = null, /** * Site ID for the Salto KS site to which the device belongs. */ - public string|null $site_id, + public string|null $site_id = null, /** * Site name for the Salto KS site to which the device belongs. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -2636,15 +2636,15 @@ public function __construct( /** * Device ID for a Schlage device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Schlage device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Model for a Schlage device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -2669,15 +2669,15 @@ public function __construct( /** * Device number for Seam Bridge. */ - public float|null $device_num, + public float|null $device_num = null, /** * Name for Seam Bridge. */ - public string|null $name, + public string|null $name = null, /** * Unlock method for Seam Bridge. */ - public string|null $unlock_method, + public string|null $unlock_method = null, ) {} } @@ -2706,23 +2706,23 @@ public function __construct( /** * Device ID for a Sensi device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Sensi device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Set to true when the device does not support the /dual-setpoints API endpoint. */ - public bool|null $dual_setpoints_not_supported, + public bool|null $dual_setpoints_not_supported = null, /** * Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. */ - public array|null $enforced_setpoint_range_celsius, + public array|null $enforced_setpoint_range_celsius = null, /** * Product type for a Sensi device. */ - public string|null $product_type, + public string|null $product_type = null, ) {} } @@ -2748,19 +2748,19 @@ public function __construct( /** * Device ID for a SmartThings device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a SmartThings device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Location ID for a SmartThings device. */ - public string|null $location_id, + public string|null $location_id = null, /** * Model for a SmartThings device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -2784,11 +2784,11 @@ public function __construct( /** * Device type for a tado° device. */ - public string|null $device_type, + public string|null $device_type = null, /** * Serial number for a tado° device. */ - public string|null $serial_no, + public string|null $serial_no = null, ) {} } @@ -2817,31 +2817,31 @@ public function __construct( /** * Bridge ID for a Tedee device. */ - public float|null $bridge_id, + public float|null $bridge_id = null, /** * Bridge name for a Tedee device. */ - public string|null $bridge_name, + public string|null $bridge_name = null, /** * Device ID for a Tedee device. */ - public float|null $device_id, + public float|null $device_id = null, /** * Device model for a Tedee device. */ - public string|null $device_model, + public string|null $device_model = null, /** * Device name for a Tedee device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Keypad ID for a Tedee device. */ - public float|null $keypad_id, + public float|null $keypad_id = null, /** * Serial number for a Tedee device. */ - public string|null $serial_number, + public string|null $serial_number = null, ) {} } @@ -2875,31 +2875,31 @@ public function __construct( /** * Feature value for a TTLock device. */ - public string|null $feature_value, + public string|null $feature_value = null, /** * Features for a TTLock device. */ - public TtlockMetadata\Features|null $features, + public TtlockMetadata\Features|null $features = null, /** * Indicates whether a TTLock device has a gateway. */ - public bool|null $has_gateway, + public bool|null $has_gateway = null, /** * Lock alias for a TTLock device. */ - public string|null $lock_alias, + public string|null $lock_alias = null, /** * Lock ID for a TTLock device. */ - public float|null $lock_id, + public float|null $lock_id = null, /** * Lock-side timezone offset in milliseconds east of UTC, as configured in the TTLock app. Source of truth for the lock's wall-clock interpretation of access code start/end times — a misconfigured value here is the typical cause of customer "codes offset by N hours" reports. Diagnostic only; Seam does not convert times based on this value. */ - public float|null $timezone_raw_offset_ms, + public float|null $timezone_raw_offset_ms = null, /** * Wireless keypads for a TTLock device. */ - public array $wireless_keypads, + public array|null $wireless_keypads = null, ) {} } @@ -2923,11 +2923,11 @@ public function __construct( /** * Device ID for a 2N device. */ - public float|null $device_id, + public float|null $device_id = null, /** * Device name for a 2N device. */ - public string|null $device_name, + public string|null $device_name = null, ) {} } @@ -2953,19 +2953,19 @@ public function __construct( /** * Device ID for an Ultraloq device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for an Ultraloq device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device type for an Ultraloq device. */ - public string|null $device_type, + public string|null $device_type = null, /** * IANA timezone for the Ultraloq device. */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } @@ -2986,7 +2986,7 @@ public function __construct( /** * Encoder ID for an ASSA ABLOY Visionline system. */ - public string|null $encoder_id, + public string|null $encoder_id = null, ) {} } @@ -3016,35 +3016,35 @@ public function __construct( /** * Device ID for a Wyze device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device information model for a Wyze device. */ - public string|null $device_info_model, + public string|null $device_info_model = null, /** * Device name for a Wyze device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Keypad UUID for a Wyze device. */ - public string|null $keypad_uuid, + public string|null $keypad_uuid = null, /** * Locker status (hardlock) for a Wyze device. */ - public float|null $locker_status_hardlock, + public float|null $locker_status_hardlock = null, /** * Product model for a Wyze device. */ - public string|null $product_model, + public string|null $product_model = null, /** * Product name for a Wyze device. */ - public string|null $product_name, + public string|null $product_name = null, /** * Product type for a Wyze device. */ - public string|null $product_type, + public string|null $product_type = null, ) {} } @@ -3070,19 +3070,19 @@ public function __construct( /** * Device ID for a Yacan device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Yacan device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device type for a Yacan device. */ - public string|null $device_type, + public string|null $device_type = null, /** * Serial number for a Yacan device. */ - public string|null $serial_number, + public string|null $serial_number = null, ) {} } @@ -3108,11 +3108,11 @@ public function __construct( /** * Maximum name length constraint for access codes. */ - public float|null $max_length, + public float|null $max_length = null, /** * Minimum name length constraint for access codes. */ - public float|null $min_length, + public float|null $min_length = null, ) {} } @@ -3173,31 +3173,31 @@ public function __construct( /** * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. */ - public string|null $end_date_recurrence_rule, + public string|null $end_date_recurrence_rule = null, /** * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. */ - public bool|null $matching_start_end_time, + public bool|null $matching_start_end_time = null, /** * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. */ - public string|null $max_duration, + public string|null $max_duration = null, /** * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. */ - public string|null $min_duration, + public string|null $min_duration = null, /** * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. */ - public string|null $start_date_recurrence_rule, + public string|null $start_date_recurrence_rule = null, /** * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. */ - public array $time_pairs, + public array|null $time_pairs = null, /** * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } @@ -3237,31 +3237,31 @@ public function __construct( /** * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. */ - public string|null $end_date_recurrence_rule, + public string|null $end_date_recurrence_rule = null, /** * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. */ - public bool|null $matching_start_end_time, + public bool|null $matching_start_end_time = null, /** * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. */ - public string|null $max_duration, + public string|null $max_duration = null, /** * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. */ - public string|null $min_duration, + public string|null $min_duration = null, /** * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. */ - public string|null $start_date_recurrence_rule, + public string|null $start_date_recurrence_rule = null, /** * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. */ - public array $time_pairs, + public array|null $time_pairs = null, /** * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } @@ -3321,11 +3321,11 @@ public function __construct( /** * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. */ - public bool|null $is_override_allowed, + public bool|null $is_override_allowed = null, /** * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). */ - public int|null $max_override_period_minutes, + public int|null $max_override_period_minutes = null, /** * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ @@ -3404,15 +3404,15 @@ public function __construct( /** * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. */ - public string|null $climate_preset_mode, + public string|null $climate_preset_mode = null, /** * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_celsius, + public float|null $cooling_set_point_celsius = null, /** * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_fahrenheit, + public float|null $cooling_set_point_fahrenheit = null, /** * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ @@ -3420,23 +3420,23 @@ public function __construct( /** * Metadata specific to the Ecobee climate, if applicable. */ - public AvailableClimatePresets\EcobeeMetadata|null $ecobee_metadata, + public AvailableClimatePresets\EcobeeMetadata|null $ecobee_metadata = null, /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_celsius, + public float|null $heating_set_point_celsius = null, /** * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_fahrenheit, + public float|null $heating_set_point_fahrenheit = null, /** * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ - public string|null $hvac_mode_setting, + public string|null $hvac_mode_setting = null, /** * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). * @@ -3446,7 +3446,7 @@ public function __construct( /** * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $name, + public string|null $name = null, ) {} } @@ -3493,65 +3493,65 @@ public function __construct( /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. */ - public bool|null $can_delete, + public bool|null $can_delete = null, /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. */ - public bool|null $can_edit, + public bool|null $can_edit = null, /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. */ - public bool|null $can_use_with_thermostat_daily_programs, + public bool|null $can_use_with_thermostat_daily_programs = null, /** * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $climate_preset_key, + public string|null $climate_preset_key = null, /** * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. */ - public string|null $climate_preset_mode, + public string|null $climate_preset_mode = null, /** * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_celsius, + public float|null $cooling_set_point_celsius = null, /** * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_fahrenheit, + public float|null $cooling_set_point_fahrenheit = null, /** * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $display_name, + public string|null $display_name = null, /** * Metadata specific to the Ecobee climate, if applicable. */ - public CurrentClimateSetting\EcobeeMetadata|null $ecobee_metadata, + public CurrentClimateSetting\EcobeeMetadata|null $ecobee_metadata = null, /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_celsius, + public float|null $heating_set_point_celsius = null, /** * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_fahrenheit, + public float|null $heating_set_point_fahrenheit = null, /** * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ - public string|null $hvac_mode_setting, + public string|null $hvac_mode_setting = null, /** * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). * * @deprecated Use 'thermostat_schedule.is_override_allowed' */ - public bool|null $manual_override_allowed, + public bool|null $manual_override_allowed = null, /** * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $name, + public string|null $name = null, ) {} } @@ -3598,65 +3598,65 @@ public function __construct( /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. */ - public bool|null $can_delete, + public bool|null $can_delete = null, /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. */ - public bool|null $can_edit, + public bool|null $can_edit = null, /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. */ - public bool|null $can_use_with_thermostat_daily_programs, + public bool|null $can_use_with_thermostat_daily_programs = null, /** * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $climate_preset_key, + public string|null $climate_preset_key = null, /** * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. */ - public string|null $climate_preset_mode, + public string|null $climate_preset_mode = null, /** * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_celsius, + public float|null $cooling_set_point_celsius = null, /** * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_fahrenheit, + public float|null $cooling_set_point_fahrenheit = null, /** * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $display_name, + public string|null $display_name = null, /** * Metadata specific to the Ecobee climate, if applicable. */ - public DefaultClimateSetting\EcobeeMetadata|null $ecobee_metadata, + public DefaultClimateSetting\EcobeeMetadata|null $ecobee_metadata = null, /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_celsius, + public float|null $heating_set_point_celsius = null, /** * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_fahrenheit, + public float|null $heating_set_point_fahrenheit = null, /** * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ - public string|null $hvac_mode_setting, + public string|null $hvac_mode_setting = null, /** * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). * * @deprecated Use 'thermostat_schedule.is_override_allowed' */ - public bool|null $manual_override_allowed, + public bool|null $manual_override_allowed = null, /** * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $name, + public string|null $name = null, ) {} } @@ -3850,11 +3850,11 @@ public function __construct( /** * ID of the associated endpoint. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Indicated whether the endpoint is active. */ - public bool|null $is_active, + public bool|null $is_active = null, ) {} } } @@ -3890,43 +3890,43 @@ public function __construct( /** * Check in time for a time slot for a dormakaba Oracode device. */ - public string|null $check_in_time, + public string|null $check_in_time = null, /** * Checkout time for a time slot for a dormakaba Oracode device. */ - public string|null $check_out_time, + public string|null $check_out_time = null, /** * ID of a user level for a dormakaba Oracode device. */ - public string|null $dormakaba_oracode_user_level_id, + public string|null $dormakaba_oracode_user_level_id = null, /** * Prefix for a user level for a dormakaba Oracode device. */ - public float|null $dormakaba_oracode_user_level_prefix, + public float|null $dormakaba_oracode_user_level_prefix = null, /** * Indicates whether a time slot for a dormakaba Oracode device is a 24-hour time slot. */ - public bool|null $is_24_hour, + public bool|null $is_24_hour = null, /** * Indicates whether a time slot for a dormakaba Oracode device is in biweekly mode. */ - public bool|null $is_biweekly_mode, + public bool|null $is_biweekly_mode = null, /** * Indicates whether a time slot for a dormakaba Oracode device is a master time slot. */ - public bool|null $is_master, + public bool|null $is_master = null, /** * Indicates whether a time slot for a dormakaba Oracode device is a one-shot time slot. */ - public bool|null $is_one_shot, + public bool|null $is_one_shot = null, /** * Name of a time slot for a dormakaba Oracode device. */ - public string|null $name, + public string|null $name = null, /** * Prefix for a time slot for a dormakaba Oracode device. */ - public float|null $prefix, + public float|null $prefix = null, ) {} } } @@ -3969,23 +3969,23 @@ public function __construct( /** * Latest accelerometer Z-axis reading for a Minut device. */ - public LatestSensorValues\AccelerometerZ|null $accelerometer_z, + public LatestSensorValues\AccelerometerZ|null $accelerometer_z = null, /** * Latest humidity reading for a Minut device. */ - public LatestSensorValues\Humidity|null $humidity, + public LatestSensorValues\Humidity|null $humidity = null, /** * Latest pressure reading for a Minut device. */ - public LatestSensorValues\Pressure|null $pressure, + public LatestSensorValues\Pressure|null $pressure = null, /** * Latest sound reading for a Minut device. */ - public LatestSensorValues\Sound|null $sound, + public LatestSensorValues\Sound|null $sound = null, /** * Latest temperature reading for a Minut device. */ - public LatestSensorValues\Temperature|null $temperature, + public LatestSensorValues\Temperature|null $temperature = null, ) {} } } @@ -4011,11 +4011,11 @@ public function __construct( /** * Time of latest accelerometer Z-axis reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest accelerometer Z-axis reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } @@ -4039,11 +4039,11 @@ public function __construct( /** * Time of latest humidity reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest humidity reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } @@ -4067,11 +4067,11 @@ public function __construct( /** * Time of latest pressure reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest pressure reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } @@ -4095,11 +4095,11 @@ public function __construct( /** * Time of latest sound reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest sound reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } @@ -4123,11 +4123,11 @@ public function __construct( /** * Time of latest temperature reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest temperature reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } } @@ -4159,31 +4159,31 @@ public function __construct( /** * Indicates whether a TTLock device supports auto-lock time configuration. */ - public bool|null $auto_lock_time_config, + public bool|null $auto_lock_time_config = null, /** * Indicates whether a TTLock device supports an incomplete keyboard passcode. */ - public bool|null $incomplete_keyboard_passcode, + public bool|null $incomplete_keyboard_passcode = null, /** * Indicates whether a TTLock device supports the lock command. */ - public bool|null $lock_command, + public bool|null $lock_command = null, /** * Indicates whether a TTLock device supports a passcode. */ - public bool|null $passcode, + public bool|null $passcode = null, /** * Indicates whether a TTLock device supports passcode management. */ - public bool|null $passcode_management, + public bool|null $passcode_management = null, /** * Indicates whether a TTLock device supports unlock via gateway. */ - public bool|null $unlock_via_gateway, + public bool|null $unlock_via_gateway = null, /** * Indicates whether a TTLock device supports Wi-Fi. */ - public bool|null $wifi, + public bool|null $wifi = null, ) {} } @@ -4207,11 +4207,11 @@ public function __construct( /** * ID for a wireless keypad for a TTLock device. */ - public float|null $wireless_keypad_id, + public float|null $wireless_keypad_id = null, /** * Name for a wireless keypad for a TTLock device. */ - public string|null $wireless_keypad_name, + public string|null $wireless_keypad_name = null, ) {} } } @@ -4343,15 +4343,15 @@ public function __construct( /** * Reference to the Ecobee climate, if applicable. */ - public string|null $climate_ref, + public string|null $climate_ref = null, /** * Indicates if the climate preset is optimized by Ecobee. */ - public bool|null $is_optimized, + public bool|null $is_optimized = null, /** * Indicates whether the climate preset is owned by the user or the system. */ - public string|null $owner, + public string|null $owner = null, ) {} } } @@ -4378,15 +4378,15 @@ public function __construct( /** * Reference to the Ecobee climate, if applicable. */ - public string|null $climate_ref, + public string|null $climate_ref = null, /** * Indicates if the climate preset is optimized by Ecobee. */ - public bool|null $is_optimized, + public bool|null $is_optimized = null, /** * Indicates whether the climate preset is owned by the user or the system. */ - public string|null $owner, + public string|null $owner = null, ) {} } } @@ -4413,15 +4413,15 @@ public function __construct( /** * Reference to the Ecobee climate, if applicable. */ - public string|null $climate_ref, + public string|null $climate_ref = null, /** * Indicates if the climate preset is optimized by Ecobee. */ - public bool|null $is_optimized, + public bool|null $is_optimized = null, /** * Indicates whether the climate preset is owned by the user or the system. */ - public string|null $owner, + public string|null $owner = null, ) {} } } diff --git a/src/Resources/DeviceProvider.php b/src/Resources/DeviceProvider.php index a3276967..044ae59c 100644 --- a/src/Resources/DeviceProvider.php +++ b/src/Resources/DeviceProvider.php @@ -50,83 +50,83 @@ public function __construct( /** * Indicates whether the lock supports configuring automatic locking. */ - public bool|null $can_configure_auto_lock, + public bool|null $can_configure_auto_lock = null, /** * Indicates whether the thermostat supports cooling. */ - public bool|null $can_hvac_cool, + public bool|null $can_hvac_cool = null, /** * Indicates whether the thermostat supports heating. */ - public bool|null $can_hvac_heat, + public bool|null $can_hvac_heat = null, /** * Indicates whether the thermostat supports simultaneous heating and cooling. */ - public bool|null $can_hvac_heat_cool, + public bool|null $can_hvac_heat_cool = null, /** * Indicates whether the device supports programming offline access codes. */ - public bool|null $can_program_offline_access_codes, + public bool|null $can_program_offline_access_codes = null, /** * Indicates whether the device supports programming online access codes. */ - public bool|null $can_program_online_access_codes, + public bool|null $can_program_online_access_codes = null, /** * Indicates whether the thermostat supports different climate programs for each day of the week. */ - public bool|null $can_program_thermostat_programs_as_different_each_day, + public bool|null $can_program_thermostat_programs_as_different_each_day = null, /** * Indicates whether the thermostat supports a single climate program applied to every day. */ - public bool|null $can_program_thermostat_programs_as_same_each_day, + public bool|null $can_program_thermostat_programs_as_same_each_day = null, /** * Indicates whether the thermostat supports weekday/weekend climate programs. */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, + public bool|null $can_program_thermostat_programs_as_weekday_weekend = null, /** * Indicates whether the device supports remote locking. */ - public bool|null $can_remotely_lock, + public bool|null $can_remotely_lock = null, /** * Indicates whether the device supports remote unlocking. */ - public bool|null $can_remotely_unlock, + public bool|null $can_remotely_unlock = null, /** * Indicates whether the thermostat supports running climate programs. */ - public bool|null $can_run_thermostat_programs, + public bool|null $can_run_thermostat_programs = null, /** * Indicates whether the device supports simulating connection in a sandbox. */ - public bool|null $can_simulate_connection, + public bool|null $can_simulate_connection = null, /** * Indicates whether the device supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_disconnection, + public bool|null $can_simulate_disconnection = null, /** * Indicates whether the hub supports simulating connection in a sandbox. */ - public bool|null $can_simulate_hub_connection, + public bool|null $can_simulate_hub_connection = null, /** * Indicates whether the hub supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_hub_disconnection, + public bool|null $can_simulate_hub_disconnection = null, /** * Indicates whether the device supports simulating a paid subscription in a sandbox. */ - public bool|null $can_simulate_paid_subscription, + public bool|null $can_simulate_paid_subscription = null, /** * Indicates whether the device supports simulating removal in a sandbox. */ - public bool|null $can_simulate_removal, + public bool|null $can_simulate_removal = null, /** * Indicates whether the thermostat can be turned off. */ - public bool|null $can_turn_off_hvac, + public bool|null $can_turn_off_hvac = null, /** * Indicates whether the lock supports unlocking with an access code. */ - public bool|null $can_unlock_with_code, + public bool|null $can_unlock_with_code = null, /** * Name of the device provider. */ diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 73a16b05..641212cd 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -152,7 +152,7 @@ public function __construct( /** * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. */ - public bool|null $access_code_is_managed, + public bool|null $access_code_is_managed = null, /** * Warnings associated with the access code. */ @@ -168,11 +168,11 @@ public function __construct( /** * Key of the affected Access Grant (if present). */ - public string|null $access_grant_key, + public string|null $access_grant_key = null, /** * Keys of the access grants associated with this access method (if present). */ - public array|null $access_grant_keys, + public array|null $access_grant_keys = null, /** * ID of the affected access method. */ @@ -231,11 +231,11 @@ public function __construct( /** * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). */ - public string|null $change_reason, + public string|null $change_reason = null, /** * List of properties that changed on the access code. */ - public array $changed_properties, + public array|null $changed_properties = null, /** * ID of the affected client session. */ @@ -245,8 +245,8 @@ public function __construct( */ public string|null $climate_preset_key, public string|null $code, - public string|null $connect_webview_id, - public mixed $connected_account_custom_metadata, + public string|null $connect_webview_id = null, + public mixed $connected_account_custom_metadata = null, /** * Errors associated with the connected account. */ @@ -259,16 +259,16 @@ public function __construct( /** * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_celsius, + public float|null $cooling_set_point_celsius = null, /** * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_fahrenheit, + public float|null $cooling_set_point_fahrenheit = null, /** * Date and time at which the event was created. */ public string|null $created_at, - public string|null $customer_key, + public string|null $customer_key = null, /** * Human-readable description of the change and its source. */ @@ -276,19 +276,19 @@ public function __construct( /** * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. */ - public float|null $desired_temperature_celsius, + public float|null $desired_temperature_celsius = null, /** * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. */ - public float|null $desired_temperature_fahrenheit, - public mixed $device_custom_metadata, + public float|null $desired_temperature_fahrenheit = null, + public mixed $device_custom_metadata = null, /** * Errors associated with the device. */ public array $device_errors, public string|null $device_id, public array|null $device_ids, - public string|null $device_name, + public string|null $device_name = null, /** * Warnings associated with the device. */ @@ -296,7 +296,7 @@ public function __construct( /** * The new end time for the access grant. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Error code associated with the disconnection event, if any. */ @@ -308,7 +308,7 @@ public function __construct( /** * 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. */ - public string|null $event_description, + public string|null $event_description = null, /** * ID of the event. */ @@ -320,31 +320,31 @@ public function __construct( /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, public Event\From|null $from, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_celsius, + public float|null $heating_set_point_celsius = null, /** * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_fahrenheit, + public float|null $heating_set_point_fahrenheit = null, /** * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ - public string|null $hvac_mode_setting, - public string|null $image_url, + public string|null $hvac_mode_setting = null, + public string|null $image_url = null, /** * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). */ - public bool|null $is_backup_code, + public bool|null $is_backup_code = null, /** * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. */ public bool|null $is_fallback_climate_preset, - public bool|null $is_via_bluetooth, - public bool|null $is_via_nfc, + public bool|null $is_via_bluetooth = null, + public bool|null $is_via_nfc = null, /** * Lower temperature limit, in °C, defined by the set threshold. */ @@ -357,35 +357,35 @@ public function __construct( /** * Metadata from Minut. */ - public mixed $minut_metadata, + public mixed $minut_metadata = null, /** * 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. */ - public array|null $missing_device_ids, + public array|null $missing_device_ids = null, /** * Sub-type of motion detected, if available. */ - public string|null $motion_sub_type, + public string|null $motion_sub_type = null, /** * Detected noise level in decibels. */ - public float|null $noise_level_decibels, + public float|null $noise_level_decibels = null, /** * Detected noise level in Noiseaware Noise Risk Score (NRS). */ - public float|null $noise_level_nrs, + public float|null $noise_level_nrs = null, /** * ID of the noise threshold that was triggered. */ - public string|null $noise_threshold_id, + public string|null $noise_threshold_id = null, /** * Name of the noise threshold that was triggered. */ - public string|null $noise_threshold_name, + public string|null $noise_threshold_name = null, /** * Metadata from Noiseaware. */ - public mixed $noiseaware_metadata, + public mixed $noiseaware_metadata = null, /** * Date and time at which the event occurred. */ @@ -393,7 +393,7 @@ public function __construct( /** * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. */ - public Event\Reason|null $reason, + public Event\Reason|null $reason = null, /** * Array of mutations requested on the access code, each containing the mutation type and from/to values. */ @@ -405,11 +405,11 @@ public function __construct( /** * Unique key for the space within the workspace. */ - public string|null $space_key, + public string|null $space_key = null, /** * The new start time for the access grant. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * Status of the action. */ @@ -435,7 +435,7 @@ public function __construct( * Upper temperature limit, in °F, defined by the set threshold. */ public float|null $upper_limit_fahrenheit, - public string|null $video_url, + public string|null $video_url = null, /** * ID of the workspace associated with the event. */ @@ -828,7 +828,7 @@ public function __construct( /** * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. */ - public mixed $from, + public mixed $from = null, /** * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. */ @@ -836,7 +836,7 @@ public function __construct( /** * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. */ - public mixed $to, + public mixed $to = null, ) {} } diff --git a/src/Resources/InstantKey.php b/src/Resources/InstantKey.php index cbeb2807..f7a93404 100644 --- a/src/Resources/InstantKey.php +++ b/src/Resources/InstantKey.php @@ -41,11 +41,11 @@ public function __construct( /** * Customization applied to the Instant Key UI. */ - public InstantKey\Customization|null $customization, + public InstantKey\Customization|null $customization = null, /** * ID of the customization profile associated with the Instant Key. */ - public string|null $customization_profile_id, + public string|null $customization_profile_id = null, /** * Date and time at which the Instant Key expires. */ @@ -92,15 +92,15 @@ public function __construct( /** * URL of the logo displayed on the Instant Key. */ - public string|null $logo_url, + public string|null $logo_url = null, /** * Primary color used in the Instant Key UI. */ - public string|null $primary_color, + public string|null $primary_color = null, /** * Secondary color used in the Instant Key UI. */ - public string|null $secondary_color, + public string|null $secondary_color = null, ) {} } } diff --git a/src/Resources/NoiseThreshold.php b/src/Resources/NoiseThreshold.php index 765329d2..0545ab0f 100644 --- a/src/Resources/NoiseThreshold.php +++ b/src/Resources/NoiseThreshold.php @@ -47,7 +47,7 @@ public function __construct( /** * Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for [Noiseaware sensors](https://docs.seam.co/device-and-system-integration-guides/noiseaware-sensors). */ - public float|null $noise_threshold_nrs, + public float|null $noise_threshold_nrs = null, /** * Time at which the noise threshold should become active daily. */ diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index 28f7d0d8..3cb31a93 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -61,7 +61,7 @@ public function __construct( /** * Optional nickname to describe the phone, settable through Seam. */ - public string|null $nickname, + public string|null $nickname = null, /** * Properties of the phone. */ @@ -144,11 +144,11 @@ public function __construct( /** * ASSA ABLOY Credential Service metadata for the phone. */ - public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, + public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata = null, /** * Salto Space credential service metadata for the phone. */ - public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, + public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata = null, ) {} } @@ -215,11 +215,11 @@ public function __construct( /** * Endpoints associated with the phone. */ - public array $endpoints, + public array|null $endpoints = null, /** * Indicates whether the credential service has active endpoints associated with the phone. */ - public bool|null $has_active_endpoint, + public bool|null $has_active_endpoint = null, ) {} } @@ -241,7 +241,7 @@ public function __construct( /** * Indicates whether the credential service has an active associated phone. */ - public bool|null $has_active_phone, + public bool|null $has_active_phone = null, ) {} } } @@ -267,11 +267,11 @@ public function __construct( /** * ID of the associated endpoint. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Indicated whether the endpoint is active. */ - public bool|null $is_active, + public bool|null $is_active = null, ) {} } } diff --git a/src/Resources/Space.php b/src/Resources/Space.php index eb7c6777..c5acd6e5 100644 --- a/src/Resources/Space.php +++ b/src/Resources/Space.php @@ -42,11 +42,11 @@ public function __construct( /** * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). */ - public Space\CustomerData|null $customer_data, + public Space\CustomerData|null $customer_data = null, /** * Customer key associated with the space. */ - public string|null $customer_key, + public string|null $customer_key = null, /** * Number of devices in the space. */ @@ -58,7 +58,7 @@ public function __construct( /** * Geographic coordinates (latitude and longitude) of the space. */ - public Space\Geolocation|null $geolocation, + public Space\Geolocation|null $geolocation = null, /** * Name of the space. */ @@ -70,7 +70,7 @@ public function __construct( /** * Unique key for the space within the workspace. */ - public string|null $space_key, + public string|null $space_key = null, /** * ID of the workspace associated with the space. */ @@ -102,19 +102,19 @@ public function __construct( /** * Postal address for the space. */ - public string|null $address, + public string|null $address = null, /** * Default check-in time for reservations at the space, as HH:mm or HH:mm:ss. */ - public string|null $default_checkin_time, + public string|null $default_checkin_time = null, /** * Default check-out time for reservations at the space, as HH:mm or HH:mm:ss. */ - public string|null $default_checkout_time, + public string|null $default_checkout_time = null, /** * IANA time zone for the space, e.g. America/Los_Angeles. */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } diff --git a/src/Resources/ThermostatSchedule.php b/src/Resources/ThermostatSchedule.php index 9d719a87..e2bc5991 100644 --- a/src/Resources/ThermostatSchedule.php +++ b/src/Resources/ThermostatSchedule.php @@ -54,11 +54,11 @@ public function __construct( /** * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. */ - public bool|null $is_override_allowed, + public bool|null $is_override_allowed = null, /** * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). */ - public int|null $max_override_period_minutes, + public int|null $max_override_period_minutes = null, /** * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 5855b3ac..87d2566b 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -62,11 +62,11 @@ public function __construct( /** * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. */ - public bool|null $cannot_be_managed, + public bool|null $cannot_be_managed = null, /** * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. */ - public bool|null $cannot_delete_unmanaged_access_code, + public bool|null $cannot_delete_unmanaged_access_code = null, /** * Code used for access. Typically, a numeric or alphanumeric string. */ @@ -82,11 +82,11 @@ public function __construct( /** * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. */ - public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Date and time after which the time-bound access code becomes inactive. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). */ @@ -102,7 +102,7 @@ public function __construct( /** * Date and time at which the time-bound access code becomes active. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * Current status of the access code within the operational lifecycle. `set` indicates that the code is active and operational. `unset` indicates that the code exists on the provider but is not usable on the device. */ @@ -151,35 +151,35 @@ public function __construct( /** * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. */ - public bool|null $is_cancellable, + public bool|null $is_cancellable = null, /** * Indicates whether early check-in is available for this stay. */ - public bool|null $is_early_checkin_able, + public bool|null $is_early_checkin_able = null, /** * Indicates whether the stay can be extended via the Dormakaba Oracode API. */ - public bool|null $is_extendable, + public bool|null $is_extendable = null, /** * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. */ - public bool|null $is_overridable, + public bool|null $is_overridable = null, /** * Dormakaba Oracode site name associated with this access code. */ - public string|null $site_name, + public string|null $site_name = null, /** * Dormakaba Oracode stay ID associated with this access code. */ - public float|null $stay_id, + public float|null $stay_id = null, /** * Dormakaba Oracode user level ID associated with this access code. */ - public string|null $user_level_id, + public string|null $user_level_id = null, /** * Dormakaba Oracode user level name associated with this access code. */ - public string|null $user_level_name, + public string|null $user_level_name = null, ) {} } @@ -217,11 +217,11 @@ public function __construct( /** * 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. */ - public string|null $change_type, + public string|null $change_type = null, /** * Date and time at which Seam created the error. */ - public string|null $created_at, + public string|null $created_at = null, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ @@ -233,13 +233,13 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. */ - public string|null $managed_access_code_id, + public string|null $managed_access_code_id = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -247,11 +247,11 @@ public function __construct( /** * List of fields that were changed externally, with their previous and new values. */ - public array $modified_fields, + public array|null $modified_fields = null, /** * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. */ - public string|null $unmanaged_access_code_id, + public string|null $unmanaged_access_code_id = null, ) {} } @@ -281,11 +281,11 @@ public function __construct( /** * 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. */ - public string|null $change_type, + public string|null $change_type = null, /** * Date and time at which Seam created the warning. */ - public string|null $created_at, + public string|null $created_at = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -293,7 +293,7 @@ public function __construct( /** * List of fields that were changed externally, with their previous and new values. */ - public array $modified_fields, + public array|null $modified_fields = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/UnmanagedAccessGrant.php b/src/Resources/UnmanagedAccessGrant.php index cc079819..7a898a81 100644 --- a/src/Resources/UnmanagedAccessGrant.php +++ b/src/Resources/UnmanagedAccessGrant.php @@ -93,7 +93,7 @@ public function __construct( /** * Reservation key for the access grant. */ - public string|null $reservation_key, + public string|null $reservation_key = null, /** * IDs of the spaces to which the Access Grant gives access. */ @@ -105,7 +105,7 @@ public function __construct( /** * ID of user identity to which the Access Grant gives access. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). */ @@ -153,7 +153,7 @@ public function __construct( /** * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. */ - public array|null $missing_device_ids, + public array|null $missing_device_ids = null, ) {} } @@ -227,7 +227,7 @@ public function __construct( /** * Specific PIN code to use for this access method. Only applicable when mode is 'code'. */ - public string|null $code, + public string|null $code = null, /** * IDs of the access methods created for the requested access method. */ @@ -243,7 +243,7 @@ public function __construct( /** * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. */ - public int|null $instant_key_max_use_count, + public int|null $instant_key_max_use_count = null, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ @@ -290,7 +290,7 @@ public function __construct( /** * 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). */ - public array $failed_devices, + public array|null $failed_devices = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -365,7 +365,7 @@ public function __construct( /** * Common code key to ensure PIN code reuse across devices. */ - public string|null $common_code_key, + public string|null $common_code_key = null, /** * New device IDs where access codes should be created. */ diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index ab715685..acfc5177 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -50,7 +50,7 @@ public function __construct( /** * The actual PIN code for code access methods. */ - public string|null $code, + public string|null $code = null, /** * Date and time at which the access method was created. */ @@ -66,11 +66,11 @@ public function __construct( /** * 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. */ - public bool|null $is_assignment_required, + public bool|null $is_assignment_required = null, /** * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. */ - public bool|null $is_encoding_required, + public bool|null $is_encoding_required = null, /** * Indicates whether the access method has been issued. */ @@ -78,11 +78,11 @@ public function __construct( /** * 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. */ - public bool|null $is_ready_for_assignment, + public bool|null $is_ready_for_assignment = null, /** * 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. */ - public bool|null $is_ready_for_encoding, + public bool|null $is_ready_for_encoding = null, /** * Date and time at which the access method was issued. */ @@ -210,7 +210,7 @@ public function __construct( /** * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $original_access_method_id, + public string|null $original_access_method_id = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 87649de0..be566c4a 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -71,83 +71,83 @@ public function __construct( /** * Indicates whether the lock supports configuring automatic locking. */ - public bool|null $can_configure_auto_lock, + public bool|null $can_configure_auto_lock = null, /** * Indicates whether the thermostat supports cooling. */ - public bool|null $can_hvac_cool, + public bool|null $can_hvac_cool = null, /** * Indicates whether the thermostat supports heating. */ - public bool|null $can_hvac_heat, + public bool|null $can_hvac_heat = null, /** * Indicates whether the thermostat supports simultaneous heating and cooling. */ - public bool|null $can_hvac_heat_cool, + public bool|null $can_hvac_heat_cool = null, /** * Indicates whether the device supports programming offline access codes. */ - public bool|null $can_program_offline_access_codes, + public bool|null $can_program_offline_access_codes = null, /** * Indicates whether the device supports programming online access codes. */ - public bool|null $can_program_online_access_codes, + public bool|null $can_program_online_access_codes = null, /** * Indicates whether the thermostat supports different climate programs for each day of the week. */ - public bool|null $can_program_thermostat_programs_as_different_each_day, + public bool|null $can_program_thermostat_programs_as_different_each_day = null, /** * Indicates whether the thermostat supports a single climate program applied to every day. */ - public bool|null $can_program_thermostat_programs_as_same_each_day, + public bool|null $can_program_thermostat_programs_as_same_each_day = null, /** * Indicates whether the thermostat supports weekday/weekend climate programs. */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, + public bool|null $can_program_thermostat_programs_as_weekday_weekend = null, /** * Indicates whether the device supports remote locking. */ - public bool|null $can_remotely_lock, + public bool|null $can_remotely_lock = null, /** * Indicates whether the device supports remote unlocking. */ - public bool|null $can_remotely_unlock, + public bool|null $can_remotely_unlock = null, /** * Indicates whether the thermostat supports running climate programs. */ - public bool|null $can_run_thermostat_programs, + public bool|null $can_run_thermostat_programs = null, /** * Indicates whether the device supports simulating connection in a sandbox. */ - public bool|null $can_simulate_connection, + public bool|null $can_simulate_connection = null, /** * Indicates whether the device supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_disconnection, + public bool|null $can_simulate_disconnection = null, /** * Indicates whether the hub supports simulating connection in a sandbox. */ - public bool|null $can_simulate_hub_connection, + public bool|null $can_simulate_hub_connection = null, /** * Indicates whether the hub supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_hub_disconnection, + public bool|null $can_simulate_hub_disconnection = null, /** * Indicates whether the device supports simulating a paid subscription in a sandbox. */ - public bool|null $can_simulate_paid_subscription, + public bool|null $can_simulate_paid_subscription = null, /** * Indicates whether the device supports simulating removal in a sandbox. */ - public bool|null $can_simulate_removal, + public bool|null $can_simulate_removal = null, /** * Indicates whether the thermostat can be turned off. */ - public bool|null $can_turn_off_hvac, + public bool|null $can_turn_off_hvac = null, /** * Indicates whether the lock supports unlocking with an access code. */ - public bool|null $can_unlock_with_code, + public bool|null $can_unlock_with_code = null, /** * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). */ @@ -183,7 +183,7 @@ public function __construct( /** * Location information for the device. */ - public UnmanagedDevice\Location|null $location, + public UnmanagedDevice\Location|null $location = null, /** * properties of the device. */ @@ -234,7 +234,7 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** @@ -266,21 +266,21 @@ public function __construct( /** * Name of the device location. */ - public string|null $location_name, + public string|null $location_name = null, /** * Name of the room within the device location, when the provider reports one. */ - public string|null $room_name, + public string|null $room_name = null, /** * Time zone of the device location. */ - public string|null $time_zone, + public string|null $time_zone = null, /** * Time zone of the device location. * * @deprecated Use `time_zone` instead. */ - public string|null $timezone, + public string|null $timezone = null, ) {} } @@ -323,27 +323,27 @@ public function __construct( /** * Accessory keypad properties and state. */ - public Properties\AccessoryKeypad|null $accessory_keypad, + public Properties\AccessoryKeypad|null $accessory_keypad = null, /** * Represents the current status of the battery charge level. */ - public Properties\Battery|null $battery, + public Properties\Battery|null $battery = null, /** * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. */ - public float|null $battery_level, + public float|null $battery_level = null, /** * Alt text for the device image. */ - public string|null $image_alt_text, + public string|null $image_alt_text = null, /** * Image URL for the device. */ - public string|null $image_url, + public string|null $image_url = null, /** * 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. */ - public string|null $manufacturer, + public string|null $manufacturer = null, /** * Device model-related properties. */ @@ -359,7 +359,7 @@ public function __construct( * * @deprecated use device.can_program_offline_access_codes */ - public bool|null $offline_access_codes_enabled, + public bool|null $offline_access_codes_enabled = null, /** * Indicates whether the device is online. */ @@ -369,7 +369,7 @@ public function __construct( * * @deprecated use device.can_program_online_access_codes */ - public bool|null $online_access_codes_enabled, + public bool|null $online_access_codes_enabled = null, ) {} } @@ -442,7 +442,7 @@ public function __construct( /** * Keypad battery properties. */ - public AccessoryKeypad\Battery|null $battery, + public AccessoryKeypad\Battery|null $battery = null, /** * Indicates if an accessory keypad is connected to the device. */ @@ -508,11 +508,11 @@ public function __construct( /** * @deprecated use device.properties.model.can_connect_accessory_keypad */ - public bool|null $accessory_keypad_supported, + public bool|null $accessory_keypad_supported = null, /** * Indicates whether the device can connect a accessory keypad. */ - public bool|null $can_connect_accessory_keypad, + public bool|null $can_connect_accessory_keypad = null, /** * Display name of the device model. */ @@ -520,7 +520,7 @@ public function __construct( /** * Indicates whether the device has a built in accessory keypad. */ - public bool|null $has_built_in_keypad, + public bool|null $has_built_in_keypad = null, /** * Display name that corresponds to the manufacturer-specific terminology for the device. */ @@ -528,11 +528,11 @@ public function __construct( /** * @deprecated use device.can_program_offline_access_codes. */ - public bool|null $offline_access_codes_supported, + public bool|null $offline_access_codes_supported = null, /** * @deprecated use device.can_program_online_access_codes. */ - public bool|null $online_access_codes_supported, + public bool|null $online_access_codes_supported = null, ) {} } } diff --git a/src/Resources/Webhook.php b/src/Resources/Webhook.php index 37eb5751..7b50da16 100644 --- a/src/Resources/Webhook.php +++ b/src/Resources/Webhook.php @@ -23,11 +23,11 @@ public function __construct( /** * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. */ - public array|null $event_types, + public array|null $event_types = null, /** * Secret associated with the [webhook](https://docs.seam.co/developer-tools/webhooks). */ - public string|null $secret, + public string|null $secret = null, /** * URL for the [webhook](https://docs.seam.co/developer-tools/webhooks). */ diff --git a/src/Resources/Workspace.php b/src/Resources/Workspace.php index 4901d453..87e16359 100644 --- a/src/Resources/Workspace.php +++ b/src/Resources/Workspace.php @@ -65,7 +65,7 @@ public function __construct( /** * Publishable key for the [workspace](https://docs.seam.co/core-concepts/workspaces). This key is used to identify the workspace in client-side applications. */ - public string|null $publishable_key, + public string|null $publishable_key = null, /** * ID of the workspace. */ @@ -97,23 +97,23 @@ public function __construct( /** * URL of the inviter logo for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $inviter_logo_url, + public string|null $inviter_logo_url = null, /** * Logo shape for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $logo_shape, + public string|null $logo_shape = null, /** * Primary button color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $primary_button_color, + public string|null $primary_button_color = null, /** * Primary button text color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $primary_button_text_color, + public string|null $primary_button_text_color = null, /** * Success message for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $success_message, + public string|null $success_message = null, ) {} } } From 74917ee79d3107555627faadcd7d81db1ffcb7a8 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:59:00 -0700 Subject: [PATCH 29/80] Check for missing required params --- codegen/layouts/partials/route-method.hbs | 5 +++++ codegen/lib/layouts/route.ts | 11 ++++++++++- src/Paginator.php | 16 ++++++++++++++-- tests/PaginatorTest.php | 18 ++++++------------ 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 06c547d5..ea65269b 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,5 +1,10 @@ {{{methodPhpDoc this}}} public function {{methodName}}({{{signatureParams}}}): {{returnType}} { +{{#if hasRequiredParams}} + if ({{requiredParamsMissingCheck}}) { + throw new \\InvalidArgumentException("At least one parameter is required for {{path}}"); + } +{{/if}} {{#if hasParams}} $request_payload = []; diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index fbe11da4..03148a6f 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -37,6 +37,8 @@ export interface MethodLayoutContext { path: string returnType: string hasParams: boolean + hasRequiredParams: boolean + requiredParamsMissingCheck: string signatureParams: string usesActionAttempt: boolean usesOnResponse: boolean @@ -92,11 +94,16 @@ const getMethodLayoutContext = ( : 'void' const sortedParameters = sortPhpClientMethodParameters(parameters) + const requiredParameters = parameters.filter((p) => !p.isOptional) + const hasRequiredParams = requiredParameters.length > 0 + const requiredParamsMissingCheck = requiredParameters + .map((p) => `$${p.name} === null`) + .join(' && ') const signatureParams = sortedParameters .map( (p) => - `${(p.isNullable || p.isOptional) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional ? ' = null' : ''}`, + `${(p.isNullable || p.isOptional || hasRequiredParams) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional || hasRequiredParams ? ' = null' : ''}`, ) .concat( usesActionAttempt @@ -137,6 +144,8 @@ const getMethodLayoutContext = ( path, returnType, hasParams: parameters.length > 0, + hasRequiredParams, + requiredParamsMissingCheck, signatureParams, usesActionAttempt, usesOnResponse, diff --git a/src/Paginator.php b/src/Paginator.php index 812012d8..d98dfdc8 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -72,13 +72,25 @@ private function fetchPage(string $cursor): array $data = $request($params); - return [$data, $this->pagination_cache[$cursor] ?? new Pagination()]; + if (!array_key_exists($cursor, $this->pagination_cache)) { + throw new \InvalidArgumentException( + "Cannot use a paginator with an unpaginated endpoint", + ); + } + + return [$data, $this->pagination_cache[$cursor]]; } private function cachePagination($response, string $cursor): void { + if (!is_object($response) || !isset($response->pagination)) { + throw new \InvalidArgumentException( + "Cannot use a paginator with an unpaginated endpoint", + ); + } + $this->pagination_cache[$cursor] = Pagination::from_json( - $response->pagination ?? null, + $response->pagination, ); } diff --git a/tests/PaginatorTest.php b/tests/PaginatorTest.php index 4fbd2c39..c81460dd 100644 --- a/tests/PaginatorTest.php +++ b/tests/PaginatorTest.php @@ -124,21 +124,15 @@ public function testFlattenIteratesEveryResource(): void $this->assertSame(array_unique($ids), $ids); } - /** - * Not every list endpoint returns pagination metadata, and asking for a - * page of one that does not should not fail. - */ - public function testEndpointWithoutPaginationYieldsAnEmptyPagination(): void + public function testEndpointWithoutPaginationIsRejected(): void { $seam = $this->seam(); - $pages = $seam->createPaginator(fn($p) => $seam->workspaces->list()); - - [$workspaces, $pagination] = $pages->firstPage(); + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("unpaginated endpoint"); - $this->assertNotEmpty($workspaces); - $this->assertInstanceOf(Pagination::class, $pagination); - $this->assertFalse($pagination->has_next_page); - $this->assertNull($pagination->next_page_cursor); + $seam + ->createPaginator(fn($p) => $seam->workspaces->list()) + ->firstPage(); } } From 0aa136cd2b40b356c2c98dd942830adfa11e6114 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 07:15:41 +0000 Subject: [PATCH 30/80] fix: emit a single backslash for the global exception class The route method partial wrote \\InvalidArgumentException, so the generated PHP carried a literal double backslash and failed to parse, breaking the Generate code job. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017FnqoQVLzdhRMBvFa6CoFG --- codegen/layouts/partials/route-method.hbs | 2 +- src/Routes/AccessCodesClient.php | 59 ++++++++-- src/Routes/AccessCodesSimulateClient.php | 11 +- src/Routes/AccessCodesUnmanagedClient.php | 30 ++++- src/Routes/AccessGrantsClient.php | 23 +++- src/Routes/AccessGrantsUnmanagedClient.php | 16 ++- src/Routes/AccessMethodsClient.php | 41 +++++-- src/Routes/AccessMethodsUnmanagedClient.php | 14 ++- src/Routes/AcsAccessGroupsClient.php | 42 ++++++- src/Routes/AcsCredentialsClient.php | 52 +++++++-- src/Routes/AcsEncodersClient.php | 28 ++++- src/Routes/AcsEncodersSimulateClient.php | 28 ++++- src/Routes/AcsEntrancesClient.php | 30 ++++- src/Routes/AcsSystemsClient.php | 21 +++- src/Routes/AcsUsersClient.php | 25 ++++- src/Routes/ActionAttemptsClient.php | 7 +- src/Routes/ClientSessionsClient.php | 14 ++- src/Routes/ConnectWebviewsClient.php | 14 ++- src/Routes/ConnectedAccountsClient.php | 21 +++- .../ConnectedAccountsSimulateClient.php | 7 +- src/Routes/CustomersClient.php | 7 +- src/Routes/DevicesClient.php | 14 ++- src/Routes/DevicesSimulateClient.php | 46 ++++++-- src/Routes/DevicesUnmanagedClient.php | 7 +- src/Routes/InstantKeysClient.php | 7 +- src/Routes/LocksClient.php | 23 +++- src/Routes/LocksSimulateClient.php | 16 ++- .../NoiseSensorsNoiseThresholdsClient.php | 49 +++++++-- src/Routes/NoiseSensorsSimulateClient.php | 7 +- src/Routes/PhonesClient.php | 14 ++- src/Routes/PhonesSimulateClient.php | 7 +- src/Routes/SpacesClient.php | 72 +++++++++--- src/Routes/ThermostatsClient.php | 103 ++++++++++++++---- src/Routes/ThermostatsDailyProgramsClient.php | 33 ++++-- src/Routes/ThermostatsSchedulesClient.php | 49 +++++++-- src/Routes/ThermostatsSimulateClient.php | 16 ++- src/Routes/UserIdentitiesClient.php | 89 ++++++++++++--- src/Routes/UserIdentitiesUnmanagedClient.php | 16 ++- src/Routes/WebhooksClient.php | 36 +++++- src/Routes/WorkspacesClient.php | 7 +- 40 files changed, 914 insertions(+), 189 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index ea65269b..26409c56 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -2,7 +2,7 @@ public function {{methodName}}({{{signatureParams}}}): {{returnType}} { {{#if hasRequiredParams}} if ({{requiredParamsMissingCheck}}) { - throw new \\InvalidArgumentException("At least one parameter is required for {{path}}"); + throw new \InvalidArgumentException("At least one parameter is required for {{path}}"); } {{/if}} {{#if hasParams}} diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index a648f967..35a7023e 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -55,7 +55,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return AccessCode OK */ public function create( - string $device_id, + ?string $device_id = null, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $code = null, @@ -72,6 +72,11 @@ public function create( ?bool $use_backup_access_code_pool = null, ?bool $use_offline_access_code = null, ): AccessCode { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/create", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -178,7 +183,7 @@ public function create( * @return array OK */ public function create_multiple( - array $device_ids, + ?array $device_ids = null, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $behavior_when_code_cannot_be_shared = null, @@ -191,6 +196,11 @@ public function create_multiple( ?string $starts_at = null, ?bool $use_backup_access_code_pool = null, ): array { + if ($device_ids === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/create_multiple", + ); + } $request_payload = []; $request_payload["device_ids"] = $device_ids; @@ -260,9 +270,14 @@ public function create_multiple( * @return void OK */ public function delete( - string $access_code_id, + ?string $access_code_id = null, ?string $device_id = null, ): void { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/delete", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -281,8 +296,13 @@ public function delete( * @param string $device_id ID of the device for which you want to generate a code. * @return AccessCode OK */ - public function generate_code(string $device_id): AccessCode + public function generate_code(?string $device_id = null): AccessCode { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/generate_code", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -426,8 +446,14 @@ public function list( * @param string $access_code_id ID of the access code for which you want to pull a backup access code. * @return AccessCode OK */ - public function pull_backup_access_code(string $access_code_id): AccessCode - { + public function pull_backup_access_code( + ?string $access_code_id = null, + ): AccessCode { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/pull_backup_access_code", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -455,11 +481,16 @@ public function pull_backup_access_code(string $access_code_id): AccessCode * @return void OK */ public function report_device_constraints( - string $device_id, + ?string $device_id = null, ?int $max_code_length = null, ?int $min_code_length = null, ?array $supported_code_lengths = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/report_device_constraints", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -507,7 +538,7 @@ public function report_device_constraints( * @return void OK */ public function update( - string $access_code_id, + ?string $access_code_id = null, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $code = null, @@ -519,6 +550,11 @@ public function update( ?string $starts_at = null, ?string $type = null, ): void { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/update", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -584,11 +620,16 @@ public function update( * @return void OK */ public function update_multiple( - string $common_code_key, + ?string $common_code_key = null, ?string $ends_at = null, ?string $name = null, ?string $starts_at = null, ): void { + if ($common_code_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/update_multiple", + ); + } $request_payload = []; $request_payload["common_code_key"] = $common_code_key; diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index a2897660..6dd40b74 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -33,10 +33,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return UnmanagedAccessCode OK */ public function create_unmanaged_access_code( - string $code, - string $device_id, - string $name, + ?string $code = null, + ?string $device_id = null, + ?string $name = null, ): UnmanagedAccessCode { + if ($code === null && $device_id === null && $name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/simulate/create_unmanaged_access_code", + ); + } $request_payload = []; $request_payload["code"] = $code; diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 8662ce2f..9de9433c 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -38,11 +38,16 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function convert_to_managed( - string $access_code_id, + ?string $access_code_id = null, ?bool $allow_external_modification = null, ?bool $force = null, ?bool $is_external_modification_allowed = null, ): void { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/convert_to_managed", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -73,8 +78,13 @@ public function convert_to_managed( * @param string $access_code_id ID of the unmanaged access code that you want to delete. * @return void OK */ - public function delete(string $access_code_id): void + public function delete(?string $access_code_id = null): void { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/delete", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -132,13 +142,18 @@ public function get( * @return array OK */ public function list( - string $device_id, + ?string $device_id = null, ?float $limit = null, ?string $page_cursor = null, ?string $search = null, ?string $user_identifier_key = null, ?callable $on_response = null, ): array { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/list", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -182,12 +197,17 @@ public function list( * @return void OK */ public function update( - string $access_code_id, - bool $is_managed, + ?string $access_code_id = null, + ?bool $is_managed = null, ?bool $allow_external_modification = null, ?bool $force = null, ?bool $is_external_modification_allowed = null, ): void { + if ($access_code_id === null && $is_managed === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/update", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index a8c23f9c..c9c526c5 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -47,7 +47,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return AccessGrant OK */ public function create( - array $requested_access_methods, + ?array $requested_access_methods = null, ?string $user_identity_id = null, mixed $user_identity = null, ?string $access_grant_key = null, @@ -63,6 +63,11 @@ public function create( ?array $space_keys = null, ?string $starts_at = null, ): AccessGrant { + if ($requested_access_methods === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/create", + ); + } $request_payload = []; $request_payload[ @@ -128,8 +133,13 @@ public function create( * @param string $access_grant_id ID of Access Grant to delete. * @return void OK */ - public function delete(string $access_grant_id): void + public function delete(?string $access_grant_id = null): void { + if ($access_grant_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/delete", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; @@ -308,9 +318,14 @@ public function list( * @return AccessGrant OK */ public function request_access_methods( - string $access_grant_id, - array $requested_access_methods, + ?string $access_grant_id = null, + ?array $requested_access_methods = null, ): AccessGrant { + if ($access_grant_id === null && $requested_access_methods === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/request_access_methods", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 3b3d6658..850385dd 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $access_grant_id ID of unmanaged Access Grant to get. * @return UnmanagedAccessGrant OK */ - public function get(string $access_grant_id): UnmanagedAccessGrant + public function get(?string $access_grant_id = null): UnmanagedAccessGrant { + if ($access_grant_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/unmanaged/get", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; @@ -116,10 +121,15 @@ public function list( * @return void OK */ public function update( - string $access_grant_id, - bool $is_managed, + ?string $access_grant_id = null, + ?bool $is_managed = null, ?string $access_grant_key = null, ): void { + if ($access_grant_id === null && $is_managed === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/unmanaged/update", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index c8bb36c9..80476d33 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -37,10 +37,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function assign_card( - string $access_method_id, - string $card_number, + ?string $access_method_id = null, + ?string $card_number = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($access_method_id === null && $card_number === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/assign_card", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -99,10 +104,15 @@ public function delete( * @return ActionAttempt OK */ public function encode( - string $access_method_id, - string $acs_encoder_id, + ?string $access_method_id = null, + ?string $acs_encoder_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($access_method_id === null && $acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/encode", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -128,8 +138,13 @@ public function encode( * @param string $access_method_id ID of access method to get. * @return AccessMethod OK */ - public function get(string $access_method_id): AccessMethod + public function get(?string $access_method_id = null): AccessMethod { + if ($access_method_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/get", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -152,10 +167,15 @@ public function get(string $access_method_id): AccessMethod * @return Batch OK */ public function get_related( - array $access_method_ids, + ?array $access_method_ids = null, ?array $exclude = null, ?array $include = null, ): Batch { + if ($access_method_ids === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/get_related", + ); + } $request_payload = []; $request_payload["access_method_ids"] = $access_method_ids; @@ -252,10 +272,15 @@ public function list( * @return ActionAttempt OK */ public function unlock_door( - string $access_method_id, - string $acs_entrance_id, + ?string $access_method_id = null, + ?string $acs_entrance_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($access_method_id === null && $acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/unlock_door", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 9bc0a43a..2d6d918e 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $access_method_id ID of unmanaged access method to get. * @return UnmanagedAccessMethod OK */ - public function get(string $access_method_id): UnmanagedAccessMethod + public function get(?string $access_method_id = null): UnmanagedAccessMethod { + if ($access_method_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/unmanaged/get", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -55,11 +60,16 @@ public function get(string $access_method_id): UnmanagedAccessMethod * @return array OK */ public function list( - string $access_grant_id, + ?string $access_grant_id = null, ?string $acs_entrance_id = null, ?string $device_id = null, ?string $space_id = null, ): array { + if ($access_grant_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/unmanaged/list", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 74cb4208..6938fe4f 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -35,10 +35,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_user( - string $acs_access_group_id, + ?string $acs_access_group_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/add_user", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -60,8 +65,13 @@ public function add_user( * @param string $acs_access_group_id ID of the access group that you want to delete. * @return void OK */ - public function delete(string $acs_access_group_id): void + public function delete(?string $acs_access_group_id = null): void { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/delete", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -77,8 +87,13 @@ public function delete(string $acs_access_group_id): void * @param string $acs_access_group_id ID of the access group that you want to get. * @return AcsAccessGroup OK */ - public function get(string $acs_access_group_id): AcsAccessGroup + public function get(?string $acs_access_group_id = null): AcsAccessGroup { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/get", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -141,8 +156,13 @@ public function list( * @return array OK */ public function list_accessible_entrances( - string $acs_access_group_id, + ?string $acs_access_group_id = null, ): array { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/list_accessible_entrances", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -167,8 +187,13 @@ public function list_accessible_entrances( * @param string $acs_access_group_id ID of the access group for which you want to retrieve all access system users. * @return array OK */ - public function list_users(string $acs_access_group_id): array + public function list_users(?string $acs_access_group_id = null): array { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/list_users", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -191,10 +216,15 @@ public function list_users(string $acs_access_group_id): array * @return void OK */ public function remove_user( - string $acs_access_group_id, + ?string $acs_access_group_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/remove_user", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 86a884c8..33e1b1db 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -34,10 +34,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function assign( - string $acs_credential_id, + ?string $acs_credential_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/assign", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -72,7 +77,7 @@ public function assign( * @return AcsCredential OK */ public function create( - string $access_method, + ?string $access_method = null, ?string $acs_system_id = null, ?string $acs_user_id = null, ?array $allowed_acs_entrance_ids = null, @@ -86,6 +91,11 @@ public function create( ?string $user_identity_id = null, mixed $visionline_metadata = null, ): AcsCredential { + if ($access_method === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/create", + ); + } $request_payload = []; $request_payload["access_method"] = $access_method; @@ -149,8 +159,13 @@ public function create( * @param string $acs_credential_id ID of the credential that you want to delete. * @return void OK */ - public function delete(string $acs_credential_id): void + public function delete(?string $acs_credential_id = null): void { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/delete", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -166,8 +181,13 @@ public function delete(string $acs_credential_id): void * @param string $acs_credential_id ID of the credential that you want to get. * @return AcsCredential OK */ - public function get(string $acs_credential_id): AcsCredential + public function get(?string $acs_credential_id = null): AcsCredential { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/get", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -257,8 +277,14 @@ public function list( * @param string $acs_credential_id ID of the credential for which you want to retrieve all entrances to which the credential grants access. * @return array OK */ - public function list_accessible_entrances(string $acs_credential_id): array - { + public function list_accessible_entrances( + ?string $acs_credential_id = null, + ): array { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/list_accessible_entrances", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -286,10 +312,15 @@ public function list_accessible_entrances(string $acs_credential_id): array * @return void OK */ public function unassign( - string $acs_credential_id, + ?string $acs_credential_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/unassign", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -314,10 +345,15 @@ public function unassign( * @return void OK */ public function update( - string $acs_credential_id, + ?string $acs_credential_id = null, ?string $code = null, ?string $ends_at = null, ): void { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/update", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index b7cd6c15..f1f2acc8 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -37,11 +37,16 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function encode_credential( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $access_method_id = null, ?string $acs_credential_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/encode_credential", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -72,8 +77,13 @@ public function encode_credential( * @param string $acs_encoder_id ID of the encoder that you want to get. * @return AcsEncoder OK */ - public function get(string $acs_encoder_id): AcsEncoder + public function get(?string $acs_encoder_id = null): AcsEncoder { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/get", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -149,10 +159,15 @@ public function list( * @return ActionAttempt OK */ public function scan_credential( - string $acs_encoder_id, + ?string $acs_encoder_id = null, mixed $salto_ks_metadata = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/scan_credential", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -185,12 +200,17 @@ public function scan_credential( * @return ActionAttempt OK */ public function scan_to_assign_credential( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $acs_user_id = null, mixed $salto_ks_metadata = null, ?string $user_identity_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/scan_to_assign_credential", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; diff --git a/src/Routes/AcsEncodersSimulateClient.php b/src/Routes/AcsEncodersSimulateClient.php index f1616fea..b8be31ce 100644 --- a/src/Routes/AcsEncodersSimulateClient.php +++ b/src/Routes/AcsEncodersSimulateClient.php @@ -31,10 +31,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function next_credential_encode_will_fail( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $error_code = null, ?string $acs_credential_id = null, ): void { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_fail", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -60,9 +65,14 @@ public function next_credential_encode_will_fail( * @return void OK */ public function next_credential_encode_will_succeed( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $scenario = null, ): void { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_succeed", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -86,10 +96,15 @@ public function next_credential_encode_will_succeed( * @return void OK */ public function next_credential_scan_will_fail( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $error_code = null, ?string $acs_credential_id_on_seam = null, ): void { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_fail", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -118,10 +133,15 @@ public function next_credential_scan_will_fail( * @return void OK */ public function next_credential_scan_will_succeed( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $acs_credential_id_on_seam = null, ?string $scenario = null, ): void { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_succeed", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 1cd2c1c1..43e04dbf 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -33,8 +33,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $acs_entrance_id ID of the entrance that you want to get. * @return AcsEntrance OK */ - public function get(string $acs_entrance_id): AcsEntrance + public function get(?string $acs_entrance_id = null): AcsEntrance { + if ($acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/entrances/get", + ); + } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -57,10 +62,15 @@ public function get(string $acs_entrance_id): AcsEntrance * @return void OK */ public function grant_access( - string $acs_entrance_id, + ?string $acs_entrance_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/entrances/grant_access", + ); + } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -167,9 +177,14 @@ public function list( * @return array OK */ public function list_credentials_with_access( - string $acs_entrance_id, + ?string $acs_entrance_id = null, ?array $include_if = null, ): array { + if ($acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/entrances/list_credentials_with_access", + ); + } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -200,10 +215,15 @@ public function list_credentials_with_access( * @return ActionAttempt OK */ public function unlock( - string $acs_credential_id, - string $acs_entrance_id, + ?string $acs_credential_id = null, + ?string $acs_entrance_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($acs_credential_id === null && $acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/entrances/unlock", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index af1d681d..4943b172 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $acs_system_id ID of the access system that you want to get. * @return AcsSystem OK */ - public function get(string $acs_system_id): AcsSystem + public function get(?string $acs_system_id = null): AcsSystem { + if ($acs_system_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/systems/get", + ); + } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -90,8 +95,13 @@ public function list( * @return array OK */ public function list_compatible_credential_manager_acs_systems( - string $acs_system_id, + ?string $acs_system_id = null, ): array { + if ($acs_system_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/systems/list_compatible_credential_manager_acs_systems", + ); + } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -116,10 +126,15 @@ public function list_compatible_credential_manager_acs_systems( * @return void OK */ public function report_devices( - string $acs_system_id, + ?string $acs_system_id = null, ?array $acs_encoders = null, ?array $acs_entrances = null, ): void { + if ($acs_system_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/systems/report_devices", + ); + } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 5b72d157..5bc7ffe2 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -33,9 +33,14 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_to_access_group( - string $acs_access_group_id, - string $acs_user_id, + ?string $acs_access_group_id = null, + ?string $acs_user_id = null, ): void { + if ($acs_access_group_id === null && $acs_user_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/add_to_access_group", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -60,8 +65,8 @@ public function add_to_access_group( * @return AcsUser OK */ public function create( - string $acs_system_id, - string $full_name, + ?string $acs_system_id = null, + ?string $full_name = null, mixed $access_schedule = null, ?array $acs_access_group_ids = null, ?string $email = null, @@ -69,6 +74,11 @@ public function create( ?string $phone_number = null, ?string $user_identity_id = null, ): AcsUser { + if ($acs_system_id === null && $full_name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/create", + ); + } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -282,10 +292,15 @@ public function list_accessible_entrances( * @return void OK */ public function remove_from_access_group( - string $acs_access_group_id, + ?string $acs_access_group_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/remove_from_access_group", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 11454f3d..adacf1df 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -33,9 +33,14 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function get( - string $action_attempt_id, + ?string $action_attempt_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($action_attempt_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /action_attempts/get", + ); + } $request_payload = []; $request_payload["action_attempt_id"] = $action_attempt_id; diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 313d2dd6..8c54248a 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -89,8 +89,13 @@ public function create( * @param string $client_session_id ID of the client session that you want to delete. * @return void OK */ - public function delete(string $client_session_id): void + public function delete(?string $client_session_id = null): void { + if ($client_session_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /client_sessions/delete", + ); + } $request_payload = []; $request_payload["client_session_id"] = $client_session_id; @@ -280,8 +285,13 @@ public function list( * @param string $client_session_id ID of the client session that you want to revoke. * @return void OK */ - public function revoke(string $client_session_id): void + public function revoke(?string $client_session_id = null): void { + if ($client_session_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /client_sessions/revoke", + ); + } $request_payload = []; $request_payload["client_session_id"] = $client_session_id; diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index b5f171b2..d311626a 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -113,8 +113,13 @@ public function create( * @param string $connect_webview_id ID of the Connect Webview that you want to delete. * @return void OK */ - public function delete(string $connect_webview_id): void + public function delete(?string $connect_webview_id = null): void { + if ($connect_webview_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connect_webviews/delete", + ); + } $request_payload = []; $request_payload["connect_webview_id"] = $connect_webview_id; @@ -132,8 +137,13 @@ public function delete(string $connect_webview_id): void * @param string $connect_webview_id ID of the Connect Webview that you want to get. * @return ConnectWebview OK */ - public function get(string $connect_webview_id): ConnectWebview + public function get(?string $connect_webview_id = null): ConnectWebview { + if ($connect_webview_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connect_webviews/get", + ); + } $request_payload = []; $request_payload["connect_webview_id"] = $connect_webview_id; diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index abbb5914..766123a5 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -38,8 +38,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account that you want to delete. * @return void OK */ - public function delete(string $connected_account_id): void + public function delete(?string $connected_account_id = null): void { + if ($connected_account_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/delete", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -147,8 +152,13 @@ public function list( * @param string $connected_account_id ID of the connected account that you want to sync. * @return void OK */ - public function sync(string $connected_account_id): void + public function sync(?string $connected_account_id = null): void { + if ($connected_account_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/sync", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -170,13 +180,18 @@ public function sync(string $connected_account_id): void * @return void OK */ public function update( - string $connected_account_id, + ?string $connected_account_id = null, ?array $accepted_capabilities = null, ?bool $automatically_manage_new_devices = null, mixed $custom_metadata = null, ?string $customer_key = null, ?string $display_name = null, ): void { + if ($connected_account_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/update", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; diff --git a/src/Routes/ConnectedAccountsSimulateClient.php b/src/Routes/ConnectedAccountsSimulateClient.php index 85e0612e..cd4676ab 100644 --- a/src/Routes/ConnectedAccountsSimulateClient.php +++ b/src/Routes/ConnectedAccountsSimulateClient.php @@ -28,8 +28,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account you want to simulate as disconnected. * @return void OK */ - public function disconnect(string $connected_account_id): void + public function disconnect(?string $connected_account_id = null): void { + if ($connected_account_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/simulate/disconnect", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index 3f26137a..ab86ff02 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -239,7 +239,7 @@ public function delete_data( * @return void OK */ public function push_data( - string $customer_key, + ?string $customer_key = null, ?array $access_grants = null, ?array $bookings = null, ?array $buildings = null, @@ -260,6 +260,11 @@ public function push_data( ?array $user_identities = null, ?array $users = null, ): void { + if ($customer_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /customers/push_data", + ); + } $request_payload = []; $request_payload["customer_key"] = $customer_key; diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index f7807686..73ae542c 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -199,8 +199,13 @@ public function list_device_providers( * @param array $devices Array of devices with provider metadata to update * @return void OK */ - public function report_provider_metadata(array $devices): void + public function report_provider_metadata(?array $devices = null): void { + if ($devices === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/report_provider_metadata", + ); + } $request_payload = []; $request_payload["devices"] = $devices; @@ -224,13 +229,18 @@ public function report_provider_metadata(array $devices): void * @return void OK */ public function update( - string $device_id, + ?string $device_id = null, ?bool $backup_access_code_pool_enabled = null, mixed $custom_metadata = null, ?bool $is_managed = null, ?string $name = null, mixed $properties = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/update", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/DevicesSimulateClient.php b/src/Routes/DevicesSimulateClient.php index b33bceac..2f8a12fa 100644 --- a/src/Routes/DevicesSimulateClient.php +++ b/src/Routes/DevicesSimulateClient.php @@ -28,8 +28,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id ID of the device that you want to simulate connecting to Seam. * @return void OK */ - public function connect(string $device_id): void + public function connect(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/connect", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -48,8 +53,13 @@ public function connect(string $device_id): void * @param string $device_id ID of the device whose hub you want to reconnect. * @return void OK */ - public function connect_to_hub(string $device_id): void + public function connect_to_hub(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/connect_to_hub", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -65,8 +75,13 @@ public function connect_to_hub(string $device_id): void * @param string $device_id ID of the device that you want to simulate disconnecting from Seam. * @return void OK */ - public function disconnect(string $device_id): void + public function disconnect(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/disconnect", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -86,8 +101,13 @@ public function disconnect(string $device_id): void * @param string $device_id ID of the device whose hub you want to disconnect. * @return void OK */ - public function disconnect_from_hub(string $device_id): void + public function disconnect_from_hub(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/disconnect_from_hub", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -108,8 +128,15 @@ public function disconnect_from_hub(string $device_id): void * @param bool $is_expired * @return void OK */ - public function paid_subscription(string $device_id, bool $is_expired): void - { + public function paid_subscription( + ?string $device_id = null, + ?bool $is_expired = null, + ): void { + if ($device_id === null && $is_expired === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/paid_subscription", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -126,8 +153,13 @@ public function paid_subscription(string $device_id, bool $is_expired): void * @param string $device_id ID of the device that you want to simulate removing from Seam. * @return void OK */ - public function remove(string $device_id): void + public function remove(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/remove", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index e6c222ea..09aa1db4 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -158,10 +158,15 @@ public function list( * @return void OK */ public function update( - string $device_id, + ?string $device_id = null, mixed $custom_metadata = null, ?bool $is_managed = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/unmanaged/update", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index b04a987c..588bf0ad 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $instant_key_id ID of the Instant Key that you want to delete. * @return void OK */ - public function delete(string $instant_key_id): void + public function delete(?string $instant_key_id = null): void { + if ($instant_key_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /instant_keys/delete", + ); + } $request_payload = []; $request_payload["instant_key_id"] = $instant_key_id; diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 313a499f..c3f4d21b 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -37,11 +37,16 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function configure_auto_lock( - bool $auto_lock_enabled, - string $device_id, + ?bool $auto_lock_enabled = null, + ?string $device_id = null, ?float $auto_lock_delay_seconds = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($auto_lock_enabled === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/configure_auto_lock", + ); + } $request_payload = []; $request_payload["auto_lock_enabled"] = $auto_lock_enabled; @@ -151,9 +156,14 @@ public function list( * @return ActionAttempt OK */ public function lock_door( - string $device_id, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/lock_door", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -180,9 +190,14 @@ public function lock_door( * @return ActionAttempt OK */ public function unlock_door( - string $device_id, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/unlock_door", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index 70a5f08d..f2fd8dbd 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -34,10 +34,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function keypad_code_entry( - string $code, - string $device_id, + ?string $code = null, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($code === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/simulate/keypad_code_entry", + ); + } $request_payload = []; $request_payload["code"] = $code; @@ -67,9 +72,14 @@ public function keypad_code_entry( * @return ActionAttempt OK */ public function manual_lock_via_keypad( - string $device_id, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/simulate/manual_lock_via_keypad", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 88558830..657c0afa 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -36,13 +36,22 @@ public function __construct(ClientInterface $client, array $defaults) * @return NoiseThreshold OK */ public function create( - string $device_id, - string $ends_daily_at, - string $starts_daily_at, + ?string $device_id = null, + ?string $ends_daily_at = null, + ?string $starts_daily_at = null, ?string $name = null, ?float $noise_threshold_decibels = null, ?float $noise_threshold_nrs = null, ): NoiseThreshold { + if ( + $device_id === null && + $ends_daily_at === null && + $starts_daily_at === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/create", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -78,8 +87,15 @@ public function create( * @param string $noise_threshold_id ID of the noise threshold that you want to delete. * @return void OK */ - public function delete(string $device_id, string $noise_threshold_id): void - { + public function delete( + ?string $device_id = null, + ?string $noise_threshold_id = null, + ): void { + if ($device_id === null && $noise_threshold_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/delete", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -98,8 +114,13 @@ public function delete(string $device_id, string $noise_threshold_id): void * @param string $noise_threshold_id ID of the noise threshold that you want to get. * @return NoiseThreshold OK */ - public function get(string $noise_threshold_id): NoiseThreshold + public function get(?string $noise_threshold_id = null): NoiseThreshold { + if ($noise_threshold_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/get", + ); + } $request_payload = []; $request_payload["noise_threshold_id"] = $noise_threshold_id; @@ -121,8 +142,13 @@ public function get(string $noise_threshold_id): NoiseThreshold * @param string $device_id ID of the device for which you want to list noise thresholds. * @return array OK */ - public function list(string $device_id): array + public function list(?string $device_id = null): array { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/list", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -154,14 +180,19 @@ public function list(string $device_id): array * @return void OK */ public function update( - string $device_id, - string $noise_threshold_id, + ?string $device_id = null, + ?string $noise_threshold_id = null, ?string $ends_daily_at = null, ?string $name = null, ?float $noise_threshold_decibels = null, ?float $noise_threshold_nrs = null, ?string $starts_daily_at = null, ): void { + if ($device_id === null && $noise_threshold_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/update", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/NoiseSensorsSimulateClient.php b/src/Routes/NoiseSensorsSimulateClient.php index 7d387ece..5cfd5050 100644 --- a/src/Routes/NoiseSensorsSimulateClient.php +++ b/src/Routes/NoiseSensorsSimulateClient.php @@ -28,8 +28,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id ID of the device for which you want to simulate the triggering of a noise threshold. * @return void OK */ - public function trigger_noise_threshold(string $device_id): void + public function trigger_noise_threshold(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/simulate/trigger_noise_threshold", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index f56953e8..be618c20 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -31,8 +31,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id Device ID of the phone that you want to deactivate. * @return void OK */ - public function deactivate(string $device_id): void + public function deactivate(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /phones/deactivate", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -48,8 +53,13 @@ public function deactivate(string $device_id): void * @param string $device_id Device ID of the phone that you want to get. * @return Phone OK */ - public function get(string $device_id): Phone + public function get(?string $device_id = null): Phone { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /phones/get", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index ba99c52e..90d94afb 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -34,11 +34,16 @@ public function __construct(ClientInterface $client, array $defaults) * @return Phone OK */ public function create_sandbox_phone( - string $user_identity_id, + ?string $user_identity_id = null, mixed $assa_abloy_metadata = null, ?string $custom_sdk_installation_id = null, mixed $phone_metadata = null, ): Phone { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /phones/simulate/create_sandbox_phone", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 88d64988..6e0ba858 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -33,9 +33,14 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_acs_entrances( - array $acs_entrance_ids, - string $space_id, + ?array $acs_entrance_ids = null, + ?string $space_id = null, ): void { + if ($acs_entrance_ids === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/add_acs_entrances", + ); + } $request_payload = []; $request_payload["acs_entrance_ids"] = $acs_entrance_ids; @@ -54,9 +59,14 @@ public function add_acs_entrances( * @return void OK */ public function add_connected_account( - string $connected_account_id, - string $space_id, + ?string $connected_account_id = null, + ?string $space_id = null, ): void { + if ($connected_account_id === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/add_connected_account", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -74,8 +84,15 @@ public function add_connected_account( * @param string $space_id ID of the space to which you want to add devices. * @return void OK */ - public function add_devices(array $device_ids, string $space_id): void - { + public function add_devices( + ?array $device_ids = null, + ?string $space_id = null, + ): void { + if ($device_ids === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/add_devices", + ); + } $request_payload = []; $request_payload["device_ids"] = $device_ids; @@ -99,7 +116,7 @@ public function add_devices(array $device_ids, string $space_id): void * @return Space OK */ public function create( - string $name, + ?string $name = null, ?array $acs_entrance_ids = null, ?array $connected_account_ids = null, mixed $customer_data = null, @@ -107,6 +124,11 @@ public function create( ?array $device_ids = null, ?string $space_key = null, ): Space { + if ($name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/create", + ); + } $request_payload = []; $request_payload["name"] = $name; @@ -144,8 +166,13 @@ public function create( * @param string $space_id ID of the space that you want to delete. * @return void OK */ - public function delete(string $space_id): void + public function delete(?string $space_id = null): void { + if ($space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/delete", + ); + } $request_payload = []; $request_payload["space_id"] = $space_id; @@ -281,9 +308,14 @@ public function list( * @return void OK */ public function remove_acs_entrances( - array $acs_entrance_ids, - string $space_id, + ?array $acs_entrance_ids = null, + ?string $space_id = null, ): void { + if ($acs_entrance_ids === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/remove_acs_entrances", + ); + } $request_payload = []; $request_payload["acs_entrance_ids"] = $acs_entrance_ids; @@ -302,9 +334,14 @@ public function remove_acs_entrances( * @return void OK */ public function remove_connected_account( - string $connected_account_id, - string $space_id, + ?string $connected_account_id = null, + ?string $space_id = null, ): void { + if ($connected_account_id === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/remove_connected_account", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -322,8 +359,15 @@ public function remove_connected_account( * @param string $space_id ID of the space from which you want to remove devices. * @return void OK */ - public function remove_devices(array $device_ids, string $space_id): void - { + public function remove_devices( + ?array $device_ids = null, + ?string $space_id = null, + ): void { + if ($device_ids === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/remove_devices", + ); + } $request_payload = []; $request_payload["device_ids"] = $device_ids; diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index af0f5e09..7e7dd4cf 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -43,10 +43,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function activate_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/activate_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -78,11 +83,16 @@ public function activate_climate_preset( * @return ActionAttempt OK */ public function cool( - string $device_id, + ?string $device_id = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/cool", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -129,8 +139,8 @@ public function cool( * @return void OK */ public function create_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, ?string $climate_preset_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, @@ -142,6 +152,11 @@ public function create_climate_preset( ?bool $manual_override_allowed = null, ?string $name = null, ): void { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/create_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -200,9 +215,14 @@ public function create_climate_preset( * @return void OK */ public function delete_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, ): void { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/delete_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -223,11 +243,16 @@ public function delete_climate_preset( * @return ActionAttempt OK */ public function heat( - string $device_id, + ?string $device_id = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/heat", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -268,13 +293,18 @@ public function heat( * @return ActionAttempt OK */ public function heat_cool( - string $device_id, + ?string $device_id = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/heat_cool", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -370,9 +400,14 @@ public function list( * @return ActionAttempt OK */ public function off( - string $device_id, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/off", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -399,9 +434,14 @@ public function off( * @return void OK */ public function set_fallback_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, ): void { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/set_fallback_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -424,11 +464,16 @@ public function set_fallback_climate_preset( * @return ActionAttempt OK */ public function set_fan_mode( - string $device_id, + ?string $device_id = null, ?string $fan_mode = null, ?string $fan_mode_setting = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/set_fan_mode", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -466,14 +511,19 @@ public function set_fan_mode( * @return ActionAttempt OK */ public function set_hvac_mode( - string $device_id, - string $hvac_mode_setting, + ?string $device_id = null, + ?string $hvac_mode_setting = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null && $hvac_mode_setting === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/set_hvac_mode", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -524,12 +574,17 @@ public function set_hvac_mode( * @return void OK */ public function set_temperature_threshold( - string $device_id, + ?string $device_id = null, ?float $lower_limit_celsius = null, ?float $lower_limit_fahrenheit = null, ?float $upper_limit_celsius = null, ?float $upper_limit_fahrenheit = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/set_temperature_threshold", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -575,8 +630,8 @@ public function set_temperature_threshold( * @return void OK */ public function update_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, ?string $climate_preset_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, @@ -588,6 +643,11 @@ public function update_climate_preset( ?bool $manual_override_allowed = null, ?string $name = null, ): void { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/update_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -653,7 +713,7 @@ public function update_climate_preset( * @return ActionAttempt OK */ public function update_weekly_program( - string $device_id, + ?string $device_id = null, ?string $friday_program_id = null, ?string $monday_program_id = null, ?string $saturday_program_id = null, @@ -663,6 +723,11 @@ public function update_weekly_program( ?string $wednesday_program_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/update_weekly_program", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 3d64dcbc..015fdf1b 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -35,10 +35,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return ThermostatDailyProgram OK */ public function create( - string $device_id, - string $name, - array $periods, + ?string $device_id = null, + ?string $name = null, + ?array $periods = null, ): ThermostatDailyProgram { + if ($device_id === null && $name === null && $periods === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/daily_programs/create", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -64,8 +69,13 @@ public function create( * @param string $thermostat_daily_program_id ID of the thermostat daily program that you want to delete. * @return void OK */ - public function delete(string $thermostat_daily_program_id): void + public function delete(?string $thermostat_daily_program_id = null): void { + if ($thermostat_daily_program_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/daily_programs/delete", + ); + } $request_payload = []; $request_payload[ @@ -87,11 +97,20 @@ public function delete(string $thermostat_daily_program_id): void * @return ActionAttempt OK */ public function update( - string $name, - array $periods, - string $thermostat_daily_program_id, + ?string $name = null, + ?array $periods = null, + ?string $thermostat_daily_program_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ( + $name === null && + $periods === null && + $thermostat_daily_program_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/daily_programs/update", + ); + } $request_payload = []; $request_payload["name"] = $name; diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index 38ad4f07..b2b12fb7 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -37,14 +37,24 @@ public function __construct(ClientInterface $client, array $defaults) * @return ThermostatSchedule OK */ public function create( - string $climate_preset_key, - string $device_id, - string $ends_at, - string $starts_at, + ?string $climate_preset_key = null, + ?string $device_id = null, + ?string $ends_at = null, + ?string $starts_at = null, ?bool $is_override_allowed = null, ?int $max_override_period_minutes = null, ?string $name = null, ): ThermostatSchedule { + if ( + $climate_preset_key === null && + $device_id === null && + $ends_at === null && + $starts_at === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/create", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -78,8 +88,13 @@ public function create( * @param string $thermostat_schedule_id ID of the thermostat schedule that you want to delete. * @return void OK */ - public function delete(string $thermostat_schedule_id): void + public function delete(?string $thermostat_schedule_id = null): void { + if ($thermostat_schedule_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/delete", + ); + } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; @@ -95,8 +110,14 @@ public function delete(string $thermostat_schedule_id): void * @param string $thermostat_schedule_id ID of the thermostat schedule that you want to get. * @return ThermostatSchedule OK */ - public function get(string $thermostat_schedule_id): ThermostatSchedule - { + public function get( + ?string $thermostat_schedule_id = null, + ): ThermostatSchedule { + if ($thermostat_schedule_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/get", + ); + } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; @@ -118,9 +139,14 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule * @return array OK */ public function list( - string $device_id, + ?string $device_id = null, ?string $user_identifier_key = null, ): array { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/list", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -153,7 +179,7 @@ public function list( * @return void OK */ public function update( - string $thermostat_schedule_id, + ?string $thermostat_schedule_id = null, ?string $climate_preset_key = null, ?string $ends_at = null, ?bool $is_override_allowed = null, @@ -161,6 +187,11 @@ public function update( ?string $name = null, ?string $starts_at = null, ): void { + if ($thermostat_schedule_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/update", + ); + } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; diff --git a/src/Routes/ThermostatsSimulateClient.php b/src/Routes/ThermostatsSimulateClient.php index 244539cd..221eedc8 100644 --- a/src/Routes/ThermostatsSimulateClient.php +++ b/src/Routes/ThermostatsSimulateClient.php @@ -34,13 +34,18 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function hvac_mode_adjusted( - string $device_id, - string $hvac_mode, + ?string $device_id = null, + ?string $hvac_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, ): void { + if ($device_id === null && $hvac_mode === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/simulate/hvac_mode_adjusted", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -82,10 +87,15 @@ public function hvac_mode_adjusted( * @return void OK */ public function temperature_reached( - string $device_id, + ?string $device_id = null, ?float $temperature_celsius = null, ?float $temperature_fahrenheit = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/simulate/temperature_reached", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index c30cf586..deaf9f2d 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -46,10 +46,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_acs_user( - string $acs_user_id, + ?string $acs_user_id = null, ?string $user_identity_id = null, ?string $user_identity_key = null, ): void { + if ($acs_user_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/add_acs_user", + ); + } $request_payload = []; $request_payload["acs_user_id"] = $acs_user_id; @@ -115,8 +120,13 @@ public function create( * @param string $user_identity_id ID of the user identity that you want to delete. * @return void OK */ - public function delete(string $user_identity_id): void + public function delete(?string $user_identity_id = null): void { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/delete", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -135,10 +145,15 @@ public function delete(string $user_identity_id): void * @return InstantKey OK */ public function generate_instant_key( - string $user_identity_id, + ?string $user_identity_id = null, ?string $customization_profile_id = null, ?float $max_use_count = null, ): InstantKey { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/generate_instant_key", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -199,9 +214,14 @@ public function get( * @return void OK */ public function grant_access_to_device( - string $device_id, - string $user_identity_id, + ?string $device_id = null, + ?string $user_identity_id = null, ): void { + if ($device_id === null && $user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/grant_access_to_device", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -280,8 +300,14 @@ public function list( * @param string $user_identity_id ID of the user identity for which you want to retrieve all accessible devices. * @return array OK */ - public function list_accessible_devices(string $user_identity_id): array - { + public function list_accessible_devices( + ?string $user_identity_id = null, + ): array { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/list_accessible_devices", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -303,8 +329,14 @@ public function list_accessible_devices(string $user_identity_id): array * @param string $user_identity_id ID of the user identity for which you want to retrieve all accessible entrances. * @return array OK */ - public function list_accessible_entrances(string $user_identity_id): array - { + public function list_accessible_entrances( + ?string $user_identity_id = null, + ): array { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/list_accessible_entrances", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -329,8 +361,13 @@ public function list_accessible_entrances(string $user_identity_id): array * @param string $user_identity_id ID of the user identity for which you want to retrieve all access systems. * @return array OK */ - public function list_acs_systems(string $user_identity_id): array + public function list_acs_systems(?string $user_identity_id = null): array { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/list_acs_systems", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -350,8 +387,13 @@ public function list_acs_systems(string $user_identity_id): array * @param string $user_identity_id ID of the user identity for which you want to retrieve all access system users. * @return array OK */ - public function list_acs_users(string $user_identity_id): array + public function list_acs_users(?string $user_identity_id = null): array { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/list_acs_users", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -373,9 +415,14 @@ public function list_acs_users(string $user_identity_id): array * @return void OK */ public function remove_acs_user( - string $acs_user_id, - string $user_identity_id, + ?string $acs_user_id = null, + ?string $user_identity_id = null, ): void { + if ($acs_user_id === null && $user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/remove_acs_user", + ); + } $request_payload = []; $request_payload["acs_user_id"] = $acs_user_id; @@ -394,9 +441,14 @@ public function remove_acs_user( * @return void OK */ public function revoke_access_to_device( - string $device_id, - string $user_identity_id, + ?string $device_id = null, + ?string $user_identity_id = null, ): void { + if ($device_id === null && $user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/revoke_access_to_device", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -420,12 +472,17 @@ public function revoke_access_to_device( * @return void OK */ public function update( - string $user_identity_id, + ?string $user_identity_id = null, ?string $email_address = null, ?string $full_name = null, ?string $phone_number = null, ?string $user_identity_key = null, ): void { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/update", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 92a77c06..ec53b23c 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $user_identity_id ID of the unmanaged user identity that you want to get. * @return UnmanagedUserIdentity OK */ - public function get(string $user_identity_id): UnmanagedUserIdentity + public function get(?string $user_identity_id = null): UnmanagedUserIdentity { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/unmanaged/get", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -104,10 +109,15 @@ public function list( * @return void OK */ public function update( - bool $is_managed, - string $user_identity_id, + ?bool $is_managed = null, + ?string $user_identity_id = null, ?string $user_identity_key = null, ): void { + if ($is_managed === null && $user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/unmanaged/update", + ); + } $request_payload = []; $request_payload["is_managed"] = $is_managed; diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 29cbb648..6a6dba94 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -31,8 +31,15 @@ public function __construct(ClientInterface $client, array $defaults) * @param array $event_types Types of events that you want the new webhook to receive. * @return Webhook OK */ - public function create(string $url, ?array $event_types = null): Webhook - { + public function create( + ?string $url = null, + ?array $event_types = null, + ): Webhook { + if ($url === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /webhooks/create", + ); + } $request_payload = []; $request_payload["url"] = $url; @@ -55,8 +62,13 @@ public function create(string $url, ?array $event_types = null): Webhook * @param string $webhook_id ID of the webhook that you want to delete. * @return void OK */ - public function delete(string $webhook_id): void + public function delete(?string $webhook_id = null): void { + if ($webhook_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /webhooks/delete", + ); + } $request_payload = []; $request_payload["webhook_id"] = $webhook_id; @@ -72,8 +84,13 @@ public function delete(string $webhook_id): void * @param string $webhook_id ID of the webhook that you want to get. * @return Webhook OK */ - public function get(string $webhook_id): Webhook + public function get(?string $webhook_id = null): Webhook { + if ($webhook_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /webhooks/get", + ); + } $request_payload = []; $request_payload["webhook_id"] = $webhook_id; @@ -106,8 +123,15 @@ public function list(): array * @param string $webhook_id ID of the webhook that you want to update. * @return void OK */ - public function update(array $event_types, string $webhook_id): void - { + public function update( + ?array $event_types = null, + ?string $webhook_id = null, + ): void { + if ($event_types === null && $webhook_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /webhooks/update", + ); + } $request_payload = []; $request_payload["event_types"] = $event_types; diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 752b182b..d378acef 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -42,7 +42,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return Workspace OK */ public function create( - string $name, + ?string $name = null, ?string $company_name = null, ?string $connect_partner_name = null, mixed $connect_webview_customization = null, @@ -53,6 +53,11 @@ public function create( ?string $webview_primary_button_text_color = null, ?string $webview_success_message = null, ): Workspace { + if ($name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /workspaces/create", + ); + } $request_payload = []; $request_payload["name"] = $name; From cb2650209d82c5130e2502efb620e3f9fd551ed0 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 07:18:46 +0000 Subject: [PATCH 31/80] 4.0.0-beta.2 --- package-lock.json | 4 ++-- package.json | 2 +- src/Utils/PackageVersion.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index da2a2607..acb24669 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 2bce7fc9..a384b169 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "type": "module", "private": true, "license": "MIT", diff --git a/src/Utils/PackageVersion.php b/src/Utils/PackageVersion.php index fecc898a..a194602d 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Utils/PackageVersion.php @@ -10,7 +10,7 @@ class PackageVersion * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.1"; + public const VERSION = "4.0.0-beta.2"; public static function get(): string { From 1652d01bcb1fb6694234d057ac498e2ce6da54bf Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 07:27:39 +0000 Subject: [PATCH 32/80] refactor: let PHP enforce the required endpoint parameters Giving every parameter a null default disabled PHP's own arity check, so the generated methods hand rolled one. The check joined the required parameters with && and only fired when every one of them was missing, letting a call that omits some of them through to the API. PHP already rejects a missing argument, and no endpoint method declares an optional parameter ahead of a required one, so the defaults are dropped and the check goes away with them. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017FnqoQVLzdhRMBvFa6CoFG --- codegen/layouts/partials/route-method.hbs | 5 - codegen/lib/layouts/route.ts | 12 +- src/Routes/AccessCodesClient.php | 59 ++-------- src/Routes/AccessCodesSimulateClient.php | 11 +- src/Routes/AccessCodesUnmanagedClient.php | 30 +---- src/Routes/AccessGrantsClient.php | 23 +--- src/Routes/AccessGrantsUnmanagedClient.php | 16 +-- src/Routes/AccessMethodsClient.php | 41 ++----- src/Routes/AccessMethodsUnmanagedClient.php | 14 +-- src/Routes/AcsAccessGroupsClient.php | 42 +------ src/Routes/AcsCredentialsClient.php | 52 ++------- src/Routes/AcsEncodersClient.php | 28 +---- src/Routes/AcsEncodersSimulateClient.php | 28 +---- src/Routes/AcsEntrancesClient.php | 30 +---- src/Routes/AcsSystemsClient.php | 21 +--- src/Routes/AcsUsersClient.php | 25 +---- src/Routes/ActionAttemptsClient.php | 7 +- src/Routes/ClientSessionsClient.php | 14 +-- src/Routes/ConnectWebviewsClient.php | 14 +-- src/Routes/ConnectedAccountsClient.php | 21 +--- .../ConnectedAccountsSimulateClient.php | 7 +- src/Routes/CustomersClient.php | 7 +- src/Routes/DevicesClient.php | 14 +-- src/Routes/DevicesSimulateClient.php | 46 ++------ src/Routes/DevicesUnmanagedClient.php | 7 +- src/Routes/InstantKeysClient.php | 7 +- src/Routes/LocksClient.php | 23 +--- src/Routes/LocksSimulateClient.php | 16 +-- .../NoiseSensorsNoiseThresholdsClient.php | 49 ++------- src/Routes/NoiseSensorsSimulateClient.php | 7 +- src/Routes/PhonesClient.php | 14 +-- src/Routes/PhonesSimulateClient.php | 7 +- src/Routes/SpacesClient.php | 72 +++--------- src/Routes/ThermostatsClient.php | 103 ++++-------------- src/Routes/ThermostatsDailyProgramsClient.php | 33 ++---- src/Routes/ThermostatsSchedulesClient.php | 49 ++------- src/Routes/ThermostatsSimulateClient.php | 16 +-- src/Routes/UserIdentitiesClient.php | 89 +++------------ src/Routes/UserIdentitiesUnmanagedClient.php | 16 +-- src/Routes/WebhooksClient.php | 36 +----- src/Routes/WorkspacesClient.php | 7 +- 41 files changed, 189 insertions(+), 929 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 26409c56..06c547d5 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,10 +1,5 @@ {{{methodPhpDoc this}}} public function {{methodName}}({{{signatureParams}}}): {{returnType}} { -{{#if hasRequiredParams}} - if ({{requiredParamsMissingCheck}}) { - throw new \InvalidArgumentException("At least one parameter is required for {{path}}"); - } -{{/if}} {{#if hasParams}} $request_payload = []; diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 03148a6f..1f6d5d6e 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -37,8 +37,6 @@ export interface MethodLayoutContext { path: string returnType: string hasParams: boolean - hasRequiredParams: boolean - requiredParamsMissingCheck: string signatureParams: string usesActionAttempt: boolean usesOnResponse: boolean @@ -94,16 +92,10 @@ const getMethodLayoutContext = ( : 'void' const sortedParameters = sortPhpClientMethodParameters(parameters) - const requiredParameters = parameters.filter((p) => !p.isOptional) - const hasRequiredParams = requiredParameters.length > 0 - const requiredParamsMissingCheck = requiredParameters - .map((p) => `$${p.name} === null`) - .join(' && ') - const signatureParams = sortedParameters .map( (p) => - `${(p.isNullable || p.isOptional || hasRequiredParams) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional || hasRequiredParams ? ' = null' : ''}`, + `${(p.isNullable || p.isOptional) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional ? ' = null' : ''}`, ) .concat( usesActionAttempt @@ -144,8 +136,6 @@ const getMethodLayoutContext = ( path, returnType, hasParams: parameters.length > 0, - hasRequiredParams, - requiredParamsMissingCheck, signatureParams, usesActionAttempt, usesOnResponse, diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 35a7023e..a648f967 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -55,7 +55,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return AccessCode OK */ public function create( - ?string $device_id = null, + string $device_id, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $code = null, @@ -72,11 +72,6 @@ public function create( ?bool $use_backup_access_code_pool = null, ?bool $use_offline_access_code = null, ): AccessCode { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/create", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -183,7 +178,7 @@ public function create( * @return array OK */ public function create_multiple( - ?array $device_ids = null, + array $device_ids, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $behavior_when_code_cannot_be_shared = null, @@ -196,11 +191,6 @@ public function create_multiple( ?string $starts_at = null, ?bool $use_backup_access_code_pool = null, ): array { - if ($device_ids === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/create_multiple", - ); - } $request_payload = []; $request_payload["device_ids"] = $device_ids; @@ -270,14 +260,9 @@ public function create_multiple( * @return void OK */ public function delete( - ?string $access_code_id = null, + string $access_code_id, ?string $device_id = null, ): void { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/delete", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -296,13 +281,8 @@ public function delete( * @param string $device_id ID of the device for which you want to generate a code. * @return AccessCode OK */ - public function generate_code(?string $device_id = null): AccessCode + public function generate_code(string $device_id): AccessCode { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/generate_code", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -446,14 +426,8 @@ public function list( * @param string $access_code_id ID of the access code for which you want to pull a backup access code. * @return AccessCode OK */ - public function pull_backup_access_code( - ?string $access_code_id = null, - ): AccessCode { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/pull_backup_access_code", - ); - } + public function pull_backup_access_code(string $access_code_id): AccessCode + { $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -481,16 +455,11 @@ public function pull_backup_access_code( * @return void OK */ public function report_device_constraints( - ?string $device_id = null, + string $device_id, ?int $max_code_length = null, ?int $min_code_length = null, ?array $supported_code_lengths = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/report_device_constraints", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -538,7 +507,7 @@ public function report_device_constraints( * @return void OK */ public function update( - ?string $access_code_id = null, + string $access_code_id, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $code = null, @@ -550,11 +519,6 @@ public function update( ?string $starts_at = null, ?string $type = null, ): void { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/update", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -620,16 +584,11 @@ public function update( * @return void OK */ public function update_multiple( - ?string $common_code_key = null, + string $common_code_key, ?string $ends_at = null, ?string $name = null, ?string $starts_at = null, ): void { - if ($common_code_key === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/update_multiple", - ); - } $request_payload = []; $request_payload["common_code_key"] = $common_code_key; diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index 6dd40b74..a2897660 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -33,15 +33,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return UnmanagedAccessCode OK */ public function create_unmanaged_access_code( - ?string $code = null, - ?string $device_id = null, - ?string $name = null, + string $code, + string $device_id, + string $name, ): UnmanagedAccessCode { - if ($code === null && $device_id === null && $name === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/simulate/create_unmanaged_access_code", - ); - } $request_payload = []; $request_payload["code"] = $code; diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 9de9433c..8662ce2f 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -38,16 +38,11 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function convert_to_managed( - ?string $access_code_id = null, + string $access_code_id, ?bool $allow_external_modification = null, ?bool $force = null, ?bool $is_external_modification_allowed = null, ): void { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/unmanaged/convert_to_managed", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -78,13 +73,8 @@ public function convert_to_managed( * @param string $access_code_id ID of the unmanaged access code that you want to delete. * @return void OK */ - public function delete(?string $access_code_id = null): void + public function delete(string $access_code_id): void { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/unmanaged/delete", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -142,18 +132,13 @@ public function get( * @return array OK */ public function list( - ?string $device_id = null, + string $device_id, ?float $limit = null, ?string $page_cursor = null, ?string $search = null, ?string $user_identifier_key = null, ?callable $on_response = null, ): array { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/unmanaged/list", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -197,17 +182,12 @@ public function list( * @return void OK */ public function update( - ?string $access_code_id = null, - ?bool $is_managed = null, + string $access_code_id, + bool $is_managed, ?bool $allow_external_modification = null, ?bool $force = null, ?bool $is_external_modification_allowed = null, ): void { - if ($access_code_id === null && $is_managed === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/unmanaged/update", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index c9c526c5..a8c23f9c 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -47,7 +47,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return AccessGrant OK */ public function create( - ?array $requested_access_methods = null, + array $requested_access_methods, ?string $user_identity_id = null, mixed $user_identity = null, ?string $access_grant_key = null, @@ -63,11 +63,6 @@ public function create( ?array $space_keys = null, ?string $starts_at = null, ): AccessGrant { - if ($requested_access_methods === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/create", - ); - } $request_payload = []; $request_payload[ @@ -133,13 +128,8 @@ public function create( * @param string $access_grant_id ID of Access Grant to delete. * @return void OK */ - public function delete(?string $access_grant_id = null): void + public function delete(string $access_grant_id): void { - if ($access_grant_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/delete", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; @@ -318,14 +308,9 @@ public function list( * @return AccessGrant OK */ public function request_access_methods( - ?string $access_grant_id = null, - ?array $requested_access_methods = null, + string $access_grant_id, + array $requested_access_methods, ): AccessGrant { - if ($access_grant_id === null && $requested_access_methods === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/request_access_methods", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 850385dd..3b3d6658 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $access_grant_id ID of unmanaged Access Grant to get. * @return UnmanagedAccessGrant OK */ - public function get(?string $access_grant_id = null): UnmanagedAccessGrant + public function get(string $access_grant_id): UnmanagedAccessGrant { - if ($access_grant_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/unmanaged/get", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; @@ -121,15 +116,10 @@ public function list( * @return void OK */ public function update( - ?string $access_grant_id = null, - ?bool $is_managed = null, + string $access_grant_id, + bool $is_managed, ?string $access_grant_key = null, ): void { - if ($access_grant_id === null && $is_managed === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/unmanaged/update", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 80476d33..c8bb36c9 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -37,15 +37,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function assign_card( - ?string $access_method_id = null, - ?string $card_number = null, + string $access_method_id, + string $card_number, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($access_method_id === null && $card_number === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/assign_card", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -104,15 +99,10 @@ public function delete( * @return ActionAttempt OK */ public function encode( - ?string $access_method_id = null, - ?string $acs_encoder_id = null, + string $access_method_id, + string $acs_encoder_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($access_method_id === null && $acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/encode", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -138,13 +128,8 @@ public function encode( * @param string $access_method_id ID of access method to get. * @return AccessMethod OK */ - public function get(?string $access_method_id = null): AccessMethod + public function get(string $access_method_id): AccessMethod { - if ($access_method_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/get", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -167,15 +152,10 @@ public function get(?string $access_method_id = null): AccessMethod * @return Batch OK */ public function get_related( - ?array $access_method_ids = null, + array $access_method_ids, ?array $exclude = null, ?array $include = null, ): Batch { - if ($access_method_ids === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/get_related", - ); - } $request_payload = []; $request_payload["access_method_ids"] = $access_method_ids; @@ -272,15 +252,10 @@ public function list( * @return ActionAttempt OK */ public function unlock_door( - ?string $access_method_id = null, - ?string $acs_entrance_id = null, + string $access_method_id, + string $acs_entrance_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($access_method_id === null && $acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/unlock_door", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 2d6d918e..9bc0a43a 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $access_method_id ID of unmanaged access method to get. * @return UnmanagedAccessMethod OK */ - public function get(?string $access_method_id = null): UnmanagedAccessMethod + public function get(string $access_method_id): UnmanagedAccessMethod { - if ($access_method_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/unmanaged/get", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -60,16 +55,11 @@ public function get(?string $access_method_id = null): UnmanagedAccessMethod * @return array OK */ public function list( - ?string $access_grant_id = null, + string $access_grant_id, ?string $acs_entrance_id = null, ?string $device_id = null, ?string $space_id = null, ): array { - if ($access_grant_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/unmanaged/list", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 6938fe4f..74cb4208 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -35,15 +35,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_user( - ?string $acs_access_group_id = null, + string $acs_access_group_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/add_user", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -65,13 +60,8 @@ public function add_user( * @param string $acs_access_group_id ID of the access group that you want to delete. * @return void OK */ - public function delete(?string $acs_access_group_id = null): void + public function delete(string $acs_access_group_id): void { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/delete", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -87,13 +77,8 @@ public function delete(?string $acs_access_group_id = null): void * @param string $acs_access_group_id ID of the access group that you want to get. * @return AcsAccessGroup OK */ - public function get(?string $acs_access_group_id = null): AcsAccessGroup + public function get(string $acs_access_group_id): AcsAccessGroup { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/get", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -156,13 +141,8 @@ public function list( * @return array OK */ public function list_accessible_entrances( - ?string $acs_access_group_id = null, + string $acs_access_group_id, ): array { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/list_accessible_entrances", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -187,13 +167,8 @@ public function list_accessible_entrances( * @param string $acs_access_group_id ID of the access group for which you want to retrieve all access system users. * @return array OK */ - public function list_users(?string $acs_access_group_id = null): array + public function list_users(string $acs_access_group_id): array { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/list_users", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -216,15 +191,10 @@ public function list_users(?string $acs_access_group_id = null): array * @return void OK */ public function remove_user( - ?string $acs_access_group_id = null, + string $acs_access_group_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/remove_user", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 33e1b1db..86a884c8 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -34,15 +34,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function assign( - ?string $acs_credential_id = null, + string $acs_credential_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/assign", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -77,7 +72,7 @@ public function assign( * @return AcsCredential OK */ public function create( - ?string $access_method = null, + string $access_method, ?string $acs_system_id = null, ?string $acs_user_id = null, ?array $allowed_acs_entrance_ids = null, @@ -91,11 +86,6 @@ public function create( ?string $user_identity_id = null, mixed $visionline_metadata = null, ): AcsCredential { - if ($access_method === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/create", - ); - } $request_payload = []; $request_payload["access_method"] = $access_method; @@ -159,13 +149,8 @@ public function create( * @param string $acs_credential_id ID of the credential that you want to delete. * @return void OK */ - public function delete(?string $acs_credential_id = null): void + public function delete(string $acs_credential_id): void { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/delete", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -181,13 +166,8 @@ public function delete(?string $acs_credential_id = null): void * @param string $acs_credential_id ID of the credential that you want to get. * @return AcsCredential OK */ - public function get(?string $acs_credential_id = null): AcsCredential + public function get(string $acs_credential_id): AcsCredential { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/get", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -277,14 +257,8 @@ public function list( * @param string $acs_credential_id ID of the credential for which you want to retrieve all entrances to which the credential grants access. * @return array OK */ - public function list_accessible_entrances( - ?string $acs_credential_id = null, - ): array { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/list_accessible_entrances", - ); - } + public function list_accessible_entrances(string $acs_credential_id): array + { $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -312,15 +286,10 @@ public function list_accessible_entrances( * @return void OK */ public function unassign( - ?string $acs_credential_id = null, + string $acs_credential_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/unassign", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -345,15 +314,10 @@ public function unassign( * @return void OK */ public function update( - ?string $acs_credential_id = null, + string $acs_credential_id, ?string $code = null, ?string $ends_at = null, ): void { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/update", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index f1f2acc8..b7cd6c15 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -37,16 +37,11 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function encode_credential( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $access_method_id = null, ?string $acs_credential_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/encode_credential", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -77,13 +72,8 @@ public function encode_credential( * @param string $acs_encoder_id ID of the encoder that you want to get. * @return AcsEncoder OK */ - public function get(?string $acs_encoder_id = null): AcsEncoder + public function get(string $acs_encoder_id): AcsEncoder { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/get", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -159,15 +149,10 @@ public function list( * @return ActionAttempt OK */ public function scan_credential( - ?string $acs_encoder_id = null, + string $acs_encoder_id, mixed $salto_ks_metadata = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/scan_credential", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -200,17 +185,12 @@ public function scan_credential( * @return ActionAttempt OK */ public function scan_to_assign_credential( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $acs_user_id = null, mixed $salto_ks_metadata = null, ?string $user_identity_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/scan_to_assign_credential", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; diff --git a/src/Routes/AcsEncodersSimulateClient.php b/src/Routes/AcsEncodersSimulateClient.php index b8be31ce..f1616fea 100644 --- a/src/Routes/AcsEncodersSimulateClient.php +++ b/src/Routes/AcsEncodersSimulateClient.php @@ -31,15 +31,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function next_credential_encode_will_fail( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $error_code = null, ?string $acs_credential_id = null, ): void { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_fail", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -65,14 +60,9 @@ public function next_credential_encode_will_fail( * @return void OK */ public function next_credential_encode_will_succeed( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $scenario = null, ): void { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_succeed", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -96,15 +86,10 @@ public function next_credential_encode_will_succeed( * @return void OK */ public function next_credential_scan_will_fail( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $error_code = null, ?string $acs_credential_id_on_seam = null, ): void { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_fail", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -133,15 +118,10 @@ public function next_credential_scan_will_fail( * @return void OK */ public function next_credential_scan_will_succeed( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $acs_credential_id_on_seam = null, ?string $scenario = null, ): void { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_succeed", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 43e04dbf..1cd2c1c1 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -33,13 +33,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $acs_entrance_id ID of the entrance that you want to get. * @return AcsEntrance OK */ - public function get(?string $acs_entrance_id = null): AcsEntrance + public function get(string $acs_entrance_id): AcsEntrance { - if ($acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/entrances/get", - ); - } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -62,15 +57,10 @@ public function get(?string $acs_entrance_id = null): AcsEntrance * @return void OK */ public function grant_access( - ?string $acs_entrance_id = null, + string $acs_entrance_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/entrances/grant_access", - ); - } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -177,14 +167,9 @@ public function list( * @return array OK */ public function list_credentials_with_access( - ?string $acs_entrance_id = null, + string $acs_entrance_id, ?array $include_if = null, ): array { - if ($acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/entrances/list_credentials_with_access", - ); - } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -215,15 +200,10 @@ public function list_credentials_with_access( * @return ActionAttempt OK */ public function unlock( - ?string $acs_credential_id = null, - ?string $acs_entrance_id = null, + string $acs_credential_id, + string $acs_entrance_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($acs_credential_id === null && $acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/entrances/unlock", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index 4943b172..af1d681d 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $acs_system_id ID of the access system that you want to get. * @return AcsSystem OK */ - public function get(?string $acs_system_id = null): AcsSystem + public function get(string $acs_system_id): AcsSystem { - if ($acs_system_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/systems/get", - ); - } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -95,13 +90,8 @@ public function list( * @return array OK */ public function list_compatible_credential_manager_acs_systems( - ?string $acs_system_id = null, + string $acs_system_id, ): array { - if ($acs_system_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/systems/list_compatible_credential_manager_acs_systems", - ); - } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -126,15 +116,10 @@ public function list_compatible_credential_manager_acs_systems( * @return void OK */ public function report_devices( - ?string $acs_system_id = null, + string $acs_system_id, ?array $acs_encoders = null, ?array $acs_entrances = null, ): void { - if ($acs_system_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/systems/report_devices", - ); - } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 5bc7ffe2..5b72d157 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -33,14 +33,9 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_to_access_group( - ?string $acs_access_group_id = null, - ?string $acs_user_id = null, + string $acs_access_group_id, + string $acs_user_id, ): void { - if ($acs_access_group_id === null && $acs_user_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/users/add_to_access_group", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -65,8 +60,8 @@ public function add_to_access_group( * @return AcsUser OK */ public function create( - ?string $acs_system_id = null, - ?string $full_name = null, + string $acs_system_id, + string $full_name, mixed $access_schedule = null, ?array $acs_access_group_ids = null, ?string $email = null, @@ -74,11 +69,6 @@ public function create( ?string $phone_number = null, ?string $user_identity_id = null, ): AcsUser { - if ($acs_system_id === null && $full_name === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/users/create", - ); - } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -292,15 +282,10 @@ public function list_accessible_entrances( * @return void OK */ public function remove_from_access_group( - ?string $acs_access_group_id = null, + string $acs_access_group_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/users/remove_from_access_group", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index adacf1df..11454f3d 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -33,14 +33,9 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function get( - ?string $action_attempt_id = null, + string $action_attempt_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($action_attempt_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /action_attempts/get", - ); - } $request_payload = []; $request_payload["action_attempt_id"] = $action_attempt_id; diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 8c54248a..313d2dd6 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -89,13 +89,8 @@ public function create( * @param string $client_session_id ID of the client session that you want to delete. * @return void OK */ - public function delete(?string $client_session_id = null): void + public function delete(string $client_session_id): void { - if ($client_session_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /client_sessions/delete", - ); - } $request_payload = []; $request_payload["client_session_id"] = $client_session_id; @@ -285,13 +280,8 @@ public function list( * @param string $client_session_id ID of the client session that you want to revoke. * @return void OK */ - public function revoke(?string $client_session_id = null): void + public function revoke(string $client_session_id): void { - if ($client_session_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /client_sessions/revoke", - ); - } $request_payload = []; $request_payload["client_session_id"] = $client_session_id; diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index d311626a..b5f171b2 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -113,13 +113,8 @@ public function create( * @param string $connect_webview_id ID of the Connect Webview that you want to delete. * @return void OK */ - public function delete(?string $connect_webview_id = null): void + public function delete(string $connect_webview_id): void { - if ($connect_webview_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connect_webviews/delete", - ); - } $request_payload = []; $request_payload["connect_webview_id"] = $connect_webview_id; @@ -137,13 +132,8 @@ public function delete(?string $connect_webview_id = null): void * @param string $connect_webview_id ID of the Connect Webview that you want to get. * @return ConnectWebview OK */ - public function get(?string $connect_webview_id = null): ConnectWebview + public function get(string $connect_webview_id): ConnectWebview { - if ($connect_webview_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connect_webviews/get", - ); - } $request_payload = []; $request_payload["connect_webview_id"] = $connect_webview_id; diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 766123a5..abbb5914 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -38,13 +38,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account that you want to delete. * @return void OK */ - public function delete(?string $connected_account_id = null): void + public function delete(string $connected_account_id): void { - if ($connected_account_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connected_accounts/delete", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -152,13 +147,8 @@ public function list( * @param string $connected_account_id ID of the connected account that you want to sync. * @return void OK */ - public function sync(?string $connected_account_id = null): void + public function sync(string $connected_account_id): void { - if ($connected_account_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connected_accounts/sync", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -180,18 +170,13 @@ public function sync(?string $connected_account_id = null): void * @return void OK */ public function update( - ?string $connected_account_id = null, + string $connected_account_id, ?array $accepted_capabilities = null, ?bool $automatically_manage_new_devices = null, mixed $custom_metadata = null, ?string $customer_key = null, ?string $display_name = null, ): void { - if ($connected_account_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connected_accounts/update", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; diff --git a/src/Routes/ConnectedAccountsSimulateClient.php b/src/Routes/ConnectedAccountsSimulateClient.php index cd4676ab..85e0612e 100644 --- a/src/Routes/ConnectedAccountsSimulateClient.php +++ b/src/Routes/ConnectedAccountsSimulateClient.php @@ -28,13 +28,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account you want to simulate as disconnected. * @return void OK */ - public function disconnect(?string $connected_account_id = null): void + public function disconnect(string $connected_account_id): void { - if ($connected_account_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connected_accounts/simulate/disconnect", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index ab86ff02..3f26137a 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -239,7 +239,7 @@ public function delete_data( * @return void OK */ public function push_data( - ?string $customer_key = null, + string $customer_key, ?array $access_grants = null, ?array $bookings = null, ?array $buildings = null, @@ -260,11 +260,6 @@ public function push_data( ?array $user_identities = null, ?array $users = null, ): void { - if ($customer_key === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /customers/push_data", - ); - } $request_payload = []; $request_payload["customer_key"] = $customer_key; diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 73ae542c..f7807686 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -199,13 +199,8 @@ public function list_device_providers( * @param array $devices Array of devices with provider metadata to update * @return void OK */ - public function report_provider_metadata(?array $devices = null): void + public function report_provider_metadata(array $devices): void { - if ($devices === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/report_provider_metadata", - ); - } $request_payload = []; $request_payload["devices"] = $devices; @@ -229,18 +224,13 @@ public function report_provider_metadata(?array $devices = null): void * @return void OK */ public function update( - ?string $device_id = null, + string $device_id, ?bool $backup_access_code_pool_enabled = null, mixed $custom_metadata = null, ?bool $is_managed = null, ?string $name = null, mixed $properties = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/update", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/DevicesSimulateClient.php b/src/Routes/DevicesSimulateClient.php index 2f8a12fa..b33bceac 100644 --- a/src/Routes/DevicesSimulateClient.php +++ b/src/Routes/DevicesSimulateClient.php @@ -28,13 +28,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id ID of the device that you want to simulate connecting to Seam. * @return void OK */ - public function connect(?string $device_id = null): void + public function connect(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/connect", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -53,13 +48,8 @@ public function connect(?string $device_id = null): void * @param string $device_id ID of the device whose hub you want to reconnect. * @return void OK */ - public function connect_to_hub(?string $device_id = null): void + public function connect_to_hub(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/connect_to_hub", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -75,13 +65,8 @@ public function connect_to_hub(?string $device_id = null): void * @param string $device_id ID of the device that you want to simulate disconnecting from Seam. * @return void OK */ - public function disconnect(?string $device_id = null): void + public function disconnect(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/disconnect", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -101,13 +86,8 @@ public function disconnect(?string $device_id = null): void * @param string $device_id ID of the device whose hub you want to disconnect. * @return void OK */ - public function disconnect_from_hub(?string $device_id = null): void + public function disconnect_from_hub(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/disconnect_from_hub", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -128,15 +108,8 @@ public function disconnect_from_hub(?string $device_id = null): void * @param bool $is_expired * @return void OK */ - public function paid_subscription( - ?string $device_id = null, - ?bool $is_expired = null, - ): void { - if ($device_id === null && $is_expired === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/paid_subscription", - ); - } + public function paid_subscription(string $device_id, bool $is_expired): void + { $request_payload = []; $request_payload["device_id"] = $device_id; @@ -153,13 +126,8 @@ public function paid_subscription( * @param string $device_id ID of the device that you want to simulate removing from Seam. * @return void OK */ - public function remove(?string $device_id = null): void + public function remove(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/remove", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 09aa1db4..e6c222ea 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -158,15 +158,10 @@ public function list( * @return void OK */ public function update( - ?string $device_id = null, + string $device_id, mixed $custom_metadata = null, ?bool $is_managed = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/unmanaged/update", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index 588bf0ad..b04a987c 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $instant_key_id ID of the Instant Key that you want to delete. * @return void OK */ - public function delete(?string $instant_key_id = null): void + public function delete(string $instant_key_id): void { - if ($instant_key_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /instant_keys/delete", - ); - } $request_payload = []; $request_payload["instant_key_id"] = $instant_key_id; diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index c3f4d21b..313a499f 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -37,16 +37,11 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function configure_auto_lock( - ?bool $auto_lock_enabled = null, - ?string $device_id = null, + bool $auto_lock_enabled, + string $device_id, ?float $auto_lock_delay_seconds = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($auto_lock_enabled === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/configure_auto_lock", - ); - } $request_payload = []; $request_payload["auto_lock_enabled"] = $auto_lock_enabled; @@ -156,14 +151,9 @@ public function list( * @return ActionAttempt OK */ public function lock_door( - ?string $device_id = null, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/lock_door", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -190,14 +180,9 @@ public function lock_door( * @return ActionAttempt OK */ public function unlock_door( - ?string $device_id = null, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/unlock_door", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index f2fd8dbd..70a5f08d 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -34,15 +34,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function keypad_code_entry( - ?string $code = null, - ?string $device_id = null, + string $code, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($code === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/simulate/keypad_code_entry", - ); - } $request_payload = []; $request_payload["code"] = $code; @@ -72,14 +67,9 @@ public function keypad_code_entry( * @return ActionAttempt OK */ public function manual_lock_via_keypad( - ?string $device_id = null, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/simulate/manual_lock_via_keypad", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 657c0afa..88558830 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -36,22 +36,13 @@ public function __construct(ClientInterface $client, array $defaults) * @return NoiseThreshold OK */ public function create( - ?string $device_id = null, - ?string $ends_daily_at = null, - ?string $starts_daily_at = null, + string $device_id, + string $ends_daily_at, + string $starts_daily_at, ?string $name = null, ?float $noise_threshold_decibels = null, ?float $noise_threshold_nrs = null, ): NoiseThreshold { - if ( - $device_id === null && - $ends_daily_at === null && - $starts_daily_at === null - ) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/create", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -87,15 +78,8 @@ public function create( * @param string $noise_threshold_id ID of the noise threshold that you want to delete. * @return void OK */ - public function delete( - ?string $device_id = null, - ?string $noise_threshold_id = null, - ): void { - if ($device_id === null && $noise_threshold_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/delete", - ); - } + public function delete(string $device_id, string $noise_threshold_id): void + { $request_payload = []; $request_payload["device_id"] = $device_id; @@ -114,13 +98,8 @@ public function delete( * @param string $noise_threshold_id ID of the noise threshold that you want to get. * @return NoiseThreshold OK */ - public function get(?string $noise_threshold_id = null): NoiseThreshold + public function get(string $noise_threshold_id): NoiseThreshold { - if ($noise_threshold_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/get", - ); - } $request_payload = []; $request_payload["noise_threshold_id"] = $noise_threshold_id; @@ -142,13 +121,8 @@ public function get(?string $noise_threshold_id = null): NoiseThreshold * @param string $device_id ID of the device for which you want to list noise thresholds. * @return array OK */ - public function list(?string $device_id = null): array + public function list(string $device_id): array { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/list", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -180,19 +154,14 @@ public function list(?string $device_id = null): array * @return void OK */ public function update( - ?string $device_id = null, - ?string $noise_threshold_id = null, + string $device_id, + string $noise_threshold_id, ?string $ends_daily_at = null, ?string $name = null, ?float $noise_threshold_decibels = null, ?float $noise_threshold_nrs = null, ?string $starts_daily_at = null, ): void { - if ($device_id === null && $noise_threshold_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/update", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/NoiseSensorsSimulateClient.php b/src/Routes/NoiseSensorsSimulateClient.php index 5cfd5050..7d387ece 100644 --- a/src/Routes/NoiseSensorsSimulateClient.php +++ b/src/Routes/NoiseSensorsSimulateClient.php @@ -28,13 +28,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id ID of the device for which you want to simulate the triggering of a noise threshold. * @return void OK */ - public function trigger_noise_threshold(?string $device_id = null): void + public function trigger_noise_threshold(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/simulate/trigger_noise_threshold", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index be618c20..f56953e8 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -31,13 +31,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id Device ID of the phone that you want to deactivate. * @return void OK */ - public function deactivate(?string $device_id = null): void + public function deactivate(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /phones/deactivate", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -53,13 +48,8 @@ public function deactivate(?string $device_id = null): void * @param string $device_id Device ID of the phone that you want to get. * @return Phone OK */ - public function get(?string $device_id = null): Phone + public function get(string $device_id): Phone { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /phones/get", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index 90d94afb..ba99c52e 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -34,16 +34,11 @@ public function __construct(ClientInterface $client, array $defaults) * @return Phone OK */ public function create_sandbox_phone( - ?string $user_identity_id = null, + string $user_identity_id, mixed $assa_abloy_metadata = null, ?string $custom_sdk_installation_id = null, mixed $phone_metadata = null, ): Phone { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /phones/simulate/create_sandbox_phone", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 6e0ba858..88d64988 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -33,14 +33,9 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_acs_entrances( - ?array $acs_entrance_ids = null, - ?string $space_id = null, + array $acs_entrance_ids, + string $space_id, ): void { - if ($acs_entrance_ids === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/add_acs_entrances", - ); - } $request_payload = []; $request_payload["acs_entrance_ids"] = $acs_entrance_ids; @@ -59,14 +54,9 @@ public function add_acs_entrances( * @return void OK */ public function add_connected_account( - ?string $connected_account_id = null, - ?string $space_id = null, + string $connected_account_id, + string $space_id, ): void { - if ($connected_account_id === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/add_connected_account", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -84,15 +74,8 @@ public function add_connected_account( * @param string $space_id ID of the space to which you want to add devices. * @return void OK */ - public function add_devices( - ?array $device_ids = null, - ?string $space_id = null, - ): void { - if ($device_ids === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/add_devices", - ); - } + public function add_devices(array $device_ids, string $space_id): void + { $request_payload = []; $request_payload["device_ids"] = $device_ids; @@ -116,7 +99,7 @@ public function add_devices( * @return Space OK */ public function create( - ?string $name = null, + string $name, ?array $acs_entrance_ids = null, ?array $connected_account_ids = null, mixed $customer_data = null, @@ -124,11 +107,6 @@ public function create( ?array $device_ids = null, ?string $space_key = null, ): Space { - if ($name === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/create", - ); - } $request_payload = []; $request_payload["name"] = $name; @@ -166,13 +144,8 @@ public function create( * @param string $space_id ID of the space that you want to delete. * @return void OK */ - public function delete(?string $space_id = null): void + public function delete(string $space_id): void { - if ($space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/delete", - ); - } $request_payload = []; $request_payload["space_id"] = $space_id; @@ -308,14 +281,9 @@ public function list( * @return void OK */ public function remove_acs_entrances( - ?array $acs_entrance_ids = null, - ?string $space_id = null, + array $acs_entrance_ids, + string $space_id, ): void { - if ($acs_entrance_ids === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/remove_acs_entrances", - ); - } $request_payload = []; $request_payload["acs_entrance_ids"] = $acs_entrance_ids; @@ -334,14 +302,9 @@ public function remove_acs_entrances( * @return void OK */ public function remove_connected_account( - ?string $connected_account_id = null, - ?string $space_id = null, + string $connected_account_id, + string $space_id, ): void { - if ($connected_account_id === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/remove_connected_account", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -359,15 +322,8 @@ public function remove_connected_account( * @param string $space_id ID of the space from which you want to remove devices. * @return void OK */ - public function remove_devices( - ?array $device_ids = null, - ?string $space_id = null, - ): void { - if ($device_ids === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/remove_devices", - ); - } + public function remove_devices(array $device_ids, string $space_id): void + { $request_payload = []; $request_payload["device_ids"] = $device_ids; diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 7e7dd4cf..af0f5e09 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -43,15 +43,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function activate_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/activate_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -83,16 +78,11 @@ public function activate_climate_preset( * @return ActionAttempt OK */ public function cool( - ?string $device_id = null, + string $device_id, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/cool", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -139,8 +129,8 @@ public function cool( * @return void OK */ public function create_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, ?string $climate_preset_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, @@ -152,11 +142,6 @@ public function create_climate_preset( ?bool $manual_override_allowed = null, ?string $name = null, ): void { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/create_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -215,14 +200,9 @@ public function create_climate_preset( * @return void OK */ public function delete_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, ): void { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/delete_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -243,16 +223,11 @@ public function delete_climate_preset( * @return ActionAttempt OK */ public function heat( - ?string $device_id = null, + string $device_id, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/heat", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -293,18 +268,13 @@ public function heat( * @return ActionAttempt OK */ public function heat_cool( - ?string $device_id = null, + string $device_id, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/heat_cool", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -400,14 +370,9 @@ public function list( * @return ActionAttempt OK */ public function off( - ?string $device_id = null, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/off", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -434,14 +399,9 @@ public function off( * @return void OK */ public function set_fallback_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, ): void { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/set_fallback_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -464,16 +424,11 @@ public function set_fallback_climate_preset( * @return ActionAttempt OK */ public function set_fan_mode( - ?string $device_id = null, + string $device_id, ?string $fan_mode = null, ?string $fan_mode_setting = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/set_fan_mode", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -511,19 +466,14 @@ public function set_fan_mode( * @return ActionAttempt OK */ public function set_hvac_mode( - ?string $device_id = null, - ?string $hvac_mode_setting = null, + string $device_id, + string $hvac_mode_setting, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null && $hvac_mode_setting === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/set_hvac_mode", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -574,17 +524,12 @@ public function set_hvac_mode( * @return void OK */ public function set_temperature_threshold( - ?string $device_id = null, + string $device_id, ?float $lower_limit_celsius = null, ?float $lower_limit_fahrenheit = null, ?float $upper_limit_celsius = null, ?float $upper_limit_fahrenheit = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/set_temperature_threshold", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -630,8 +575,8 @@ public function set_temperature_threshold( * @return void OK */ public function update_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, ?string $climate_preset_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, @@ -643,11 +588,6 @@ public function update_climate_preset( ?bool $manual_override_allowed = null, ?string $name = null, ): void { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/update_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -713,7 +653,7 @@ public function update_climate_preset( * @return ActionAttempt OK */ public function update_weekly_program( - ?string $device_id = null, + string $device_id, ?string $friday_program_id = null, ?string $monday_program_id = null, ?string $saturday_program_id = null, @@ -723,11 +663,6 @@ public function update_weekly_program( ?string $wednesday_program_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/update_weekly_program", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 015fdf1b..3d64dcbc 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -35,15 +35,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return ThermostatDailyProgram OK */ public function create( - ?string $device_id = null, - ?string $name = null, - ?array $periods = null, + string $device_id, + string $name, + array $periods, ): ThermostatDailyProgram { - if ($device_id === null && $name === null && $periods === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/daily_programs/create", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -69,13 +64,8 @@ public function create( * @param string $thermostat_daily_program_id ID of the thermostat daily program that you want to delete. * @return void OK */ - public function delete(?string $thermostat_daily_program_id = null): void + public function delete(string $thermostat_daily_program_id): void { - if ($thermostat_daily_program_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/daily_programs/delete", - ); - } $request_payload = []; $request_payload[ @@ -97,20 +87,11 @@ public function delete(?string $thermostat_daily_program_id = null): void * @return ActionAttempt OK */ public function update( - ?string $name = null, - ?array $periods = null, - ?string $thermostat_daily_program_id = null, + string $name, + array $periods, + string $thermostat_daily_program_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ( - $name === null && - $periods === null && - $thermostat_daily_program_id === null - ) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/daily_programs/update", - ); - } $request_payload = []; $request_payload["name"] = $name; diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index b2b12fb7..38ad4f07 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -37,24 +37,14 @@ public function __construct(ClientInterface $client, array $defaults) * @return ThermostatSchedule OK */ public function create( - ?string $climate_preset_key = null, - ?string $device_id = null, - ?string $ends_at = null, - ?string $starts_at = null, + string $climate_preset_key, + string $device_id, + string $ends_at, + string $starts_at, ?bool $is_override_allowed = null, ?int $max_override_period_minutes = null, ?string $name = null, ): ThermostatSchedule { - if ( - $climate_preset_key === null && - $device_id === null && - $ends_at === null && - $starts_at === null - ) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/create", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -88,13 +78,8 @@ public function create( * @param string $thermostat_schedule_id ID of the thermostat schedule that you want to delete. * @return void OK */ - public function delete(?string $thermostat_schedule_id = null): void + public function delete(string $thermostat_schedule_id): void { - if ($thermostat_schedule_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/delete", - ); - } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; @@ -110,14 +95,8 @@ public function delete(?string $thermostat_schedule_id = null): void * @param string $thermostat_schedule_id ID of the thermostat schedule that you want to get. * @return ThermostatSchedule OK */ - public function get( - ?string $thermostat_schedule_id = null, - ): ThermostatSchedule { - if ($thermostat_schedule_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/get", - ); - } + public function get(string $thermostat_schedule_id): ThermostatSchedule + { $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; @@ -139,14 +118,9 @@ public function get( * @return array OK */ public function list( - ?string $device_id = null, + string $device_id, ?string $user_identifier_key = null, ): array { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/list", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -179,7 +153,7 @@ public function list( * @return void OK */ public function update( - ?string $thermostat_schedule_id = null, + string $thermostat_schedule_id, ?string $climate_preset_key = null, ?string $ends_at = null, ?bool $is_override_allowed = null, @@ -187,11 +161,6 @@ public function update( ?string $name = null, ?string $starts_at = null, ): void { - if ($thermostat_schedule_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/update", - ); - } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; diff --git a/src/Routes/ThermostatsSimulateClient.php b/src/Routes/ThermostatsSimulateClient.php index 221eedc8..244539cd 100644 --- a/src/Routes/ThermostatsSimulateClient.php +++ b/src/Routes/ThermostatsSimulateClient.php @@ -34,18 +34,13 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function hvac_mode_adjusted( - ?string $device_id = null, - ?string $hvac_mode = null, + string $device_id, + string $hvac_mode, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, ): void { - if ($device_id === null && $hvac_mode === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/simulate/hvac_mode_adjusted", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -87,15 +82,10 @@ public function hvac_mode_adjusted( * @return void OK */ public function temperature_reached( - ?string $device_id = null, + string $device_id, ?float $temperature_celsius = null, ?float $temperature_fahrenheit = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/simulate/temperature_reached", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index deaf9f2d..c30cf586 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -46,15 +46,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_acs_user( - ?string $acs_user_id = null, + string $acs_user_id, ?string $user_identity_id = null, ?string $user_identity_key = null, ): void { - if ($acs_user_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/add_acs_user", - ); - } $request_payload = []; $request_payload["acs_user_id"] = $acs_user_id; @@ -120,13 +115,8 @@ public function create( * @param string $user_identity_id ID of the user identity that you want to delete. * @return void OK */ - public function delete(?string $user_identity_id = null): void + public function delete(string $user_identity_id): void { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/delete", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -145,15 +135,10 @@ public function delete(?string $user_identity_id = null): void * @return InstantKey OK */ public function generate_instant_key( - ?string $user_identity_id = null, + string $user_identity_id, ?string $customization_profile_id = null, ?float $max_use_count = null, ): InstantKey { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/generate_instant_key", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -214,14 +199,9 @@ public function get( * @return void OK */ public function grant_access_to_device( - ?string $device_id = null, - ?string $user_identity_id = null, + string $device_id, + string $user_identity_id, ): void { - if ($device_id === null && $user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/grant_access_to_device", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -300,14 +280,8 @@ public function list( * @param string $user_identity_id ID of the user identity for which you want to retrieve all accessible devices. * @return array OK */ - public function list_accessible_devices( - ?string $user_identity_id = null, - ): array { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/list_accessible_devices", - ); - } + public function list_accessible_devices(string $user_identity_id): array + { $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -329,14 +303,8 @@ public function list_accessible_devices( * @param string $user_identity_id ID of the user identity for which you want to retrieve all accessible entrances. * @return array OK */ - public function list_accessible_entrances( - ?string $user_identity_id = null, - ): array { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/list_accessible_entrances", - ); - } + public function list_accessible_entrances(string $user_identity_id): array + { $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -361,13 +329,8 @@ public function list_accessible_entrances( * @param string $user_identity_id ID of the user identity for which you want to retrieve all access systems. * @return array OK */ - public function list_acs_systems(?string $user_identity_id = null): array + public function list_acs_systems(string $user_identity_id): array { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/list_acs_systems", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -387,13 +350,8 @@ public function list_acs_systems(?string $user_identity_id = null): array * @param string $user_identity_id ID of the user identity for which you want to retrieve all access system users. * @return array OK */ - public function list_acs_users(?string $user_identity_id = null): array + public function list_acs_users(string $user_identity_id): array { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/list_acs_users", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -415,14 +373,9 @@ public function list_acs_users(?string $user_identity_id = null): array * @return void OK */ public function remove_acs_user( - ?string $acs_user_id = null, - ?string $user_identity_id = null, + string $acs_user_id, + string $user_identity_id, ): void { - if ($acs_user_id === null && $user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/remove_acs_user", - ); - } $request_payload = []; $request_payload["acs_user_id"] = $acs_user_id; @@ -441,14 +394,9 @@ public function remove_acs_user( * @return void OK */ public function revoke_access_to_device( - ?string $device_id = null, - ?string $user_identity_id = null, + string $device_id, + string $user_identity_id, ): void { - if ($device_id === null && $user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/revoke_access_to_device", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -472,17 +420,12 @@ public function revoke_access_to_device( * @return void OK */ public function update( - ?string $user_identity_id = null, + string $user_identity_id, ?string $email_address = null, ?string $full_name = null, ?string $phone_number = null, ?string $user_identity_key = null, ): void { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/update", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index ec53b23c..92a77c06 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $user_identity_id ID of the unmanaged user identity that you want to get. * @return UnmanagedUserIdentity OK */ - public function get(?string $user_identity_id = null): UnmanagedUserIdentity + public function get(string $user_identity_id): UnmanagedUserIdentity { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/unmanaged/get", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -109,15 +104,10 @@ public function list( * @return void OK */ public function update( - ?bool $is_managed = null, - ?string $user_identity_id = null, + bool $is_managed, + string $user_identity_id, ?string $user_identity_key = null, ): void { - if ($is_managed === null && $user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/unmanaged/update", - ); - } $request_payload = []; $request_payload["is_managed"] = $is_managed; diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 6a6dba94..29cbb648 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -31,15 +31,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param array $event_types Types of events that you want the new webhook to receive. * @return Webhook OK */ - public function create( - ?string $url = null, - ?array $event_types = null, - ): Webhook { - if ($url === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /webhooks/create", - ); - } + public function create(string $url, ?array $event_types = null): Webhook + { $request_payload = []; $request_payload["url"] = $url; @@ -62,13 +55,8 @@ public function create( * @param string $webhook_id ID of the webhook that you want to delete. * @return void OK */ - public function delete(?string $webhook_id = null): void + public function delete(string $webhook_id): void { - if ($webhook_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /webhooks/delete", - ); - } $request_payload = []; $request_payload["webhook_id"] = $webhook_id; @@ -84,13 +72,8 @@ public function delete(?string $webhook_id = null): void * @param string $webhook_id ID of the webhook that you want to get. * @return Webhook OK */ - public function get(?string $webhook_id = null): Webhook + public function get(string $webhook_id): Webhook { - if ($webhook_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /webhooks/get", - ); - } $request_payload = []; $request_payload["webhook_id"] = $webhook_id; @@ -123,15 +106,8 @@ public function list(): array * @param string $webhook_id ID of the webhook that you want to update. * @return void OK */ - public function update( - ?array $event_types = null, - ?string $webhook_id = null, - ): void { - if ($event_types === null && $webhook_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /webhooks/update", - ); - } + public function update(array $event_types, string $webhook_id): void + { $request_payload = []; $request_payload["event_types"] = $event_types; diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index d378acef..752b182b 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -42,7 +42,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return Workspace OK */ public function create( - ?string $name = null, + string $name, ?string $company_name = null, ?string $connect_partner_name = null, mixed $connect_webview_customization = null, @@ -53,11 +53,6 @@ public function create( ?string $webview_primary_button_text_color = null, ?string $webview_success_message = null, ): Workspace { - if ($name === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /workspaces/create", - ); - } $request_payload = []; $request_payload["name"] = $name; From 02294e448e96b2f5901231316c12e054b33969df Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 07:33:19 +0000 Subject: [PATCH 33/80] fix: reject a call that omits every one of the required parameters An endpoint whose schema requires one of its parameters marks none of them individually required, so PHP cannot enforce it through the method signature. Blueprint reports this as hasRequiredParameters, which the generated method now guards on. The condition is rendered from the parameter list in the layout rather than assembled in the context builder. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017FnqoQVLzdhRMBvFa6CoFG --- codegen/layouts/partials/route-method.hbs | 5 ++ codegen/lib/class-model.ts | 1 + codegen/lib/layouts/route.ts | 2 + codegen/lib/routes.ts | 5 ++ src/Routes/AccessCodesClient.php | 21 +++++++ src/Routes/AccessCodesUnmanagedClient.php | 5 ++ src/Routes/AccessGrantsClient.php | 26 +++++++++ src/Routes/AccessMethodsClient.php | 23 ++++++++ src/Routes/AcsUsersClient.php | 69 +++++++++++++++++++++++ src/Routes/ClientSessionsClient.php | 12 ++++ src/Routes/ConnectedAccountsClient.php | 5 ++ src/Routes/DevicesClient.php | 5 ++ src/Routes/DevicesUnmanagedClient.php | 5 ++ src/Routes/EventsClient.php | 39 +++++++++++++ src/Routes/InstantKeysClient.php | 5 ++ src/Routes/LocksClient.php | 5 ++ src/Routes/SpacesClient.php | 15 +++++ src/Routes/UserIdentitiesClient.php | 5 ++ 18 files changed, 253 insertions(+) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 06c547d5..7ce8dfdc 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,5 +1,10 @@ {{{methodPhpDoc this}}} public function {{methodName}}({{{signatureParams}}}): {{returnType}} { +{{#if requiresAtLeastOneParameter}} + if ({{#each parameters}}{{#unless @first}} && {{/unless}}${{name}} === null{{/each}}) { + throw new \InvalidArgumentException("At least one parameter is required for {{path}}"); + } +{{/if}} {{#if hasParams}} $request_payload = []; diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index c335114e..ebb8e5aa 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -18,6 +18,7 @@ export interface PhpClientMethod { responseDescription: string isDeprecated: boolean deprecationMessage: string + requiresAtLeastOneParameter: boolean parameters: PhpClientMethodParameter[] returnResource: string returnPath: string diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 1f6d5d6e..a1e0b018 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -37,6 +37,7 @@ export interface MethodLayoutContext { path: string returnType: string hasParams: boolean + requiresAtLeastOneParameter: boolean signatureParams: string usesActionAttempt: boolean usesOnResponse: boolean @@ -136,6 +137,7 @@ const getMethodLayoutContext = ( path, returnType, hasParams: parameters.length > 0, + requiresAtLeastOneParameter: method.requiresAtLeastOneParameter, signatureParams, usesActionAttempt, usesOnResponse, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index d81cb9f7..b75aa155 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -127,6 +127,11 @@ const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { responseDescription: response.description, isDeprecated: endpoint.isDeprecated, deprecationMessage: endpoint.deprecationMessage, + // An endpoint that takes no individually required parameter may still + // require one of them, so PHP cannot enforce it through the signature. + requiresAtLeastOneParameter: + endpoint.request.hasRequiredParameters && + endpoint.request.parameters.every(({ isRequired }) => !isRequired), parameters: endpoint.request.parameters.map((parameter) => ({ name: parameter.name, type: getPhpType(parameter), diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index a648f967..aabfff1e 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -311,6 +311,11 @@ public function get( ?string $code = null, ?string $device_id = null, ): AccessCode { + if ($access_code_id === null && $code === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/get", + ); + } $request_payload = []; if ($access_code_id !== null) { @@ -363,6 +368,22 @@ public function list( ?string $user_identifier_key = null, ?callable $on_response = null, ): array { + if ( + $access_code_ids === null && + $access_grant_id === null && + $access_grant_key === null && + $access_method_id === null && + $customer_key === null && + $device_id === null && + $limit === null && + $page_cursor === null && + $search === null && + $user_identifier_key === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/list", + ); + } $request_payload = []; if ($access_code_ids !== null) { diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 8662ce2f..98b1973f 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -99,6 +99,11 @@ public function get( ?string $code = null, ?string $device_id = null, ): UnmanagedAccessCode { + if ($access_code_id === null && $code === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/get", + ); + } $request_payload = []; if ($access_code_id !== null) { diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index a8c23f9c..1a17a5ff 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -150,6 +150,11 @@ public function get( ?string $access_grant_id = null, ?string $access_grant_key = null, ): AccessGrant { + if ($access_grant_id === null && $access_grant_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/get", + ); + } $request_payload = []; if ($access_grant_id !== null) { @@ -183,6 +188,16 @@ public function get_related( ?array $exclude = null, ?array $include = null, ): Batch { + if ( + $access_grant_ids === null && + $access_grant_keys === null && + $exclude === null && + $include === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/get_related", + ); + } $request_payload = []; if ($access_grant_ids !== null) { @@ -346,6 +361,17 @@ public function update( ?string $name = null, ?string $starts_at = null, ): void { + if ( + $access_grant_id === null && + $access_grant_key === null && + $ends_at === null && + $name === null && + $starts_at === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/update", + ); + } $request_payload = []; if ($access_grant_id !== null) { diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index c8bb36c9..9f22dddc 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -73,6 +73,15 @@ public function delete( ?string $access_grant_id = null, ?string $reservation_key = null, ): void { + if ( + $access_method_id === null && + $access_grant_id === null && + $reservation_key === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/delete", + ); + } $request_payload = []; if ($access_method_id !== null) { @@ -200,6 +209,20 @@ public function list( ?string $space_id = null, ?callable $on_response = null, ): array { + if ( + $access_code_id === null && + $access_grant_id === null && + $access_grant_key === null && + $acs_entrance_id === null && + $device_id === null && + $limit === null && + $page_cursor === null && + $space_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/list", + ); + } $request_payload = []; if ($access_code_id !== null) { diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 5b72d157..5a1b3c11 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -114,6 +114,15 @@ public function delete( ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/delete", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -144,6 +153,15 @@ public function get( ?string $acs_system_id = null, ?string $user_identity_id = null, ): AcsUser { + if ( + $acs_user_id === null && + $acs_system_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/get", + ); + } $request_payload = []; if ($acs_user_id !== null) { @@ -247,6 +265,15 @@ public function list_accessible_entrances( ?string $acs_user_id = null, ?string $user_identity_id = null, ): array { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/list_accessible_entrances", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -316,6 +343,15 @@ public function revoke_access_to_all_entrances( ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/revoke_access_to_all_entrances", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -348,6 +384,15 @@ public function suspend( ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/suspend", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -378,6 +423,15 @@ public function unsuspend( ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/unsuspend", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -420,6 +474,21 @@ public function update( ?string $phone_number = null, ?string $user_identity_id = null, ): void { + if ( + $access_schedule === null && + $acs_system_id === null && + $acs_user_id === null && + $email === null && + $email_address === null && + $full_name === null && + $hid_acs_system_id === null && + $phone_number === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/update", + ); + } $request_payload = []; if ($access_schedule !== null) { diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 313d2dd6..dbd36c2b 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -197,6 +197,18 @@ public function grant_access( ?string $user_identity_id = null, ?array $user_identity_ids = null, ): void { + if ( + $client_session_id === null && + $connect_webview_ids === null && + $connected_account_ids === null && + $user_identifier_key === null && + $user_identity_id === null && + $user_identity_ids === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /client_sessions/grant_access", + ); + } $request_payload = []; if ($client_session_id !== null) { diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index abbb5914..175a34e7 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -60,6 +60,11 @@ public function get( ?string $connected_account_id = null, ?string $email = null, ): ConnectedAccount { + if ($connected_account_id === null && $email === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/get", + ); + } $request_payload = []; if ($connected_account_id !== null) { diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index f7807686..0c8ee8b8 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -39,6 +39,11 @@ public function __construct(ClientInterface $client, array $defaults) */ public function get(?string $device_id = null, ?string $name = null): Device { + if ($device_id === null && $name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/get", + ); + } $request_payload = []; if ($device_id !== null) { diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index e6c222ea..a2412032 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -39,6 +39,11 @@ public function get( ?string $device_id = null, ?string $name = null, ): UnmanagedDevice { + if ($device_id === null && $name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/unmanaged/get", + ); + } $request_payload = []; if ($device_id !== null) { diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index f787e91b..62951f44 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -37,6 +37,11 @@ public function get( ?string $device_id = null, ?string $event_type = null, ): Event { + if ($event_id === null && $device_id === null && $event_type === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /events/get", + ); + } $request_payload = []; if ($event_id !== null) { @@ -121,6 +126,40 @@ public function list( ?float $unstable_offset = null, ?string $user_identity_id = null, ): array { + if ( + $access_code_id === null && + $access_code_ids === null && + $access_grant_id === null && + $access_grant_ids === null && + $access_method_id === null && + $access_method_ids === null && + $acs_access_group_id === null && + $acs_credential_id === null && + $acs_encoder_id === null && + $acs_entrance_id === null && + $acs_system_id === null && + $acs_system_ids === null && + $acs_user_id === null && + $between === null && + $connect_webview_id === null && + $connected_account_id === null && + $customer_key === null && + $device_id === null && + $device_ids === null && + $event_ids === null && + $event_type === null && + $event_types === null && + $limit === null && + $since === null && + $space_id === null && + $space_ids === null && + $unstable_offset === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /events/list", + ); + } $request_payload = []; if ($access_code_id !== null) { diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index b04a987c..ce21eff7 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -52,6 +52,11 @@ public function get( ?string $instant_key_id = null, ?string $instant_key_url = null, ): InstantKey { + if ($instant_key_id === null && $instant_key_url === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /instant_keys/get", + ); + } $request_payload = []; if ($instant_key_id !== null) { diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 313a499f..6d13235d 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -76,6 +76,11 @@ public function configure_auto_lock( */ public function get(?string $device_id = null, ?string $name = null): Device { + if ($device_id === null && $name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/get", + ); + } $request_payload = []; if ($device_id !== null) { diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 88d64988..9b68f45c 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -166,6 +166,11 @@ public function get( ?string $space_id = null, ?string $space_key = null, ): Space { + if ($space_id === null && $space_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/get", + ); + } $request_payload = []; if ($space_id !== null) { @@ -199,6 +204,16 @@ public function get_related( ?array $space_ids = null, ?array $space_keys = null, ): Batch { + if ( + $exclude === null && + $include === null && + $space_ids === null && + $space_keys === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/get_related", + ); + } $request_payload = []; if ($exclude !== null) { diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index c30cf586..7acc9569 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -173,6 +173,11 @@ public function get( ?string $user_identity_id = null, ?string $user_identity_key = null, ): UserIdentity { + if ($user_identity_id === null && $user_identity_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/get", + ); + } $request_payload = []; if ($user_identity_id !== null) { From e9f996cb45583911327bfddcbdd909a0884dde19 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 07:34:31 +0000 Subject: [PATCH 34/80] 4.0.0-beta.3 --- package-lock.json | 4 ++-- package.json | 2 +- src/Utils/PackageVersion.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index acb24669..915b4902 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.2", + "version": "4.0.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.2", + "version": "4.0.0-beta.3", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index a384b169..1d40393a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.2", + "version": "4.0.0-beta.3", "type": "module", "private": true, "license": "MIT", diff --git a/src/Utils/PackageVersion.php b/src/Utils/PackageVersion.php index a194602d..20bdf3b5 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Utils/PackageVersion.php @@ -10,7 +10,7 @@ class PackageVersion * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.2"; + public const VERSION = "4.0.0-beta.3"; public static function get(): string { From d9b54ea0adabc61751f881c030c9f332718837bc Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 11:36:25 -0700 Subject: [PATCH 35/80] Per version semantic release refs --- .github/workflows/version.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index fe504ad7..f463d055 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -41,8 +41,7 @@ jobs: *-*) channel="${VERSION#*-}"; channel="${channel%%.*}" ;; *) echo "Stable release, no channel note required."; exit 0 ;; esac - git fetch origin "+refs/notes/semantic-release:refs/notes/semantic-release" || true - git notes --ref semantic-release add --force \ + git notes --ref "semantic-release-v$VERSION" add --force \ --message "{\"channels\":[\"$channel\"]}" "v$VERSION^{commit}" - git push origin refs/notes/semantic-release + git push origin "refs/notes/semantic-release-v$VERSION" echo "Recorded v$VERSION on the '$channel' channel." From ac1789bb04276e4938ceb43e99dbfefdcc80c6f0 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 23:05:34 -0700 Subject: [PATCH 36/80] feat: implement the URL search params serialization standard (#463) * feat: implement the URL search params serialization standard Port @seamapi/url-search-params-serializer to PHP as Seam\UrlSearchParamsSerializer over a Seam\UrlSearchParams pair collection, byte-for-byte identical to the TypeScript reference implementation. The unit test suite mirrors the reference and Python SDK suites, covering every branch of the standard. PHP's own primitives each diverge from the standard, so the port implements them directly: urlencode() is RFC 3986 flavored (escapes *, keeps ~) where the WHATWG form encoding does the opposite; float casts render 1.0, switch to exponents at the wrong thresholds, and spell them E+21, so floats follow the ECMAScript Number::toString algorithm; sorting compares UTF-16 code units, not UTF-8 bytes; and dates always carry exactly three fractional digits and a literal Z. PHP has a single absence value, so the Seam\NullValue enum adds the explicit null sentinel: null means the safe option of omitting a param, and sending null is always spelled NullValue::NULL. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HMKbYgbsoFdKiiaxXB6jh2 * feat: apply the URL search params standard and type nullable params Wrap the Guzzle client in Seam\Http\SerializingClient so every request follows the serialization standard. Query params given as a map are serialized with UrlSearchParamsSerializer and handed to Guzzle as a raw query string, since Guzzle's own encoder escapes *, keeps ~, and drops an empty array entirely instead of sending name= (which the API reads as the empty array rather than an unfiltered request). NullValue::NULL sentinels in JSON bodies become JSON null, so the sentinel works on both transports. A query already given as a string passes through untouched, and nothing serialized means no query at all rather than a bare trailing ?. Consume the blueprint's isNullable flag in the codegen: a nullable param is typed string|NullValue|null and accepts the sentinel, while a merely optional one keeps ?string and rejects it, so the type system catches sending an accidental null where it would unset a value. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HMKbYgbsoFdKiiaxXB6jh2 * feat: add _strict=true to the URL search params serializer Mirror seamapi/python#617: the new StrictUrlSearchParamsSerializer wraps the base serializer and appends _strict=true to any non-empty query, telling the Seam API to use strict, schema-aware parsing. The flag is appended after the sort so it always sits last, a caller-supplied _strict param is replaced rather than repeated, and a query with no serializable params stays empty. The flag is Seam API behavior, not part of the serialization standard, so it is isolated in the wrapper and the base UrlSearchParamsSerializer stays a pure implementation of the standard. The SDK client serializes every request with the strict wrapper. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HMKbYgbsoFdKiiaxXB6jh2 * refactor: drop comments that narrate instead of constrain Keep a comment only when it says something the code cannot: a non-obvious why, an external constraint, or an invariant a future edit would break. Comments that narrate a test, restate an assertion, or argue the code is correct are deleted; the tests are the explanation. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HMKbYgbsoFdKiiaxXB6jh2 * docs: align the README sections with the other SDKs Setting a param to null becomes a top-level usage section and Serializing URL search params follows the structure of the Python and Ruby READMEs, including the note explaining why PHP spells the sentinel NullValue::NULL where the other SDKs spell it NULL with type Null: both names are reserved in PHP, so the type and the value live on one enum. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HMKbYgbsoFdKiiaxXB6jh2 * refactor: sort search params by stable byte order Drop the UTF-16 sort key: a stable sort is what the standard needs to keep array element order, and byte order matches URLSearchParams.sort() for every ASCII name, which all Seam param names are. Only a name beyond the Basic Multilingual Plane could order differently than the reference implementation, and then only against a name in U+E000 to U+FFFF. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HMKbYgbsoFdKiiaxXB6jh2 --------- Co-authored-by: Claude --- README.md | 96 +++ codegen/layouts/seam-client.hbs | 9 +- codegen/lib/layouts/route.ts | 27 +- src/Http/SerializingClient.php | 117 ++++ src/NullValue.php | 67 ++ src/Routes/AccessCodesClient.php | 5 +- src/Routes/AccessCodesUnmanagedClient.php | 5 +- src/Routes/AccessGrantsClient.php | 25 +- src/Routes/AccessGrantsUnmanagedClient.php | 5 +- src/Routes/AccessMethodsClient.php | 5 +- src/Routes/AcsCredentialsClient.php | 5 +- src/Routes/AcsEncodersClient.php | 5 +- src/Routes/AcsEntrancesClient.php | 9 +- src/Routes/AcsUsersClient.php | 5 +- src/Routes/ActionAttemptsClient.php | 5 +- src/Routes/ConnectWebviewsClient.php | 5 +- src/Routes/ConnectedAccountsClient.php | 5 +- src/Routes/DevicesClient.php | 13 +- src/Routes/DevicesUnmanagedClient.php | 5 +- src/Routes/SpacesClient.php | 5 +- src/Routes/ThermostatsClient.php | 53 +- src/Routes/ThermostatsSchedulesClient.php | 9 +- src/Routes/UserIdentitiesClient.php | 37 +- src/Routes/UserIdentitiesUnmanagedClient.php | 5 +- src/Routes/WorkspacesClient.php | 5 +- src/Seam.php | 28 +- src/SeamWithoutWorkspace.php | 20 +- src/StrictUrlSearchParamsSerializer.php | 52 ++ src/UnserializableParamError.php | 26 + src/UrlSearchParams.php | 241 +++++++ src/UrlSearchParamsSerializer.php | 354 ++++++++++ tests/HeadersTest.php | 5 +- tests/NullValueTest.php | 78 +++ tests/SearchParamsTest.php | 267 ++++++++ tests/StrictUrlSearchParamsSerializerTest.php | 45 ++ tests/UrlSearchParamsSerializerTest.php | 645 ++++++++++++++++++ 36 files changed, 2170 insertions(+), 123 deletions(-) create mode 100644 src/Http/SerializingClient.php create mode 100644 src/NullValue.php create mode 100644 src/StrictUrlSearchParamsSerializer.php create mode 100644 src/UnserializableParamError.php create mode 100644 src/UrlSearchParams.php create mode 100644 src/UrlSearchParamsSerializer.php create mode 100644 tests/NullValueTest.php create mode 100644 tests/SearchParamsTest.php create mode 100644 tests/StrictUrlSearchParamsSerializerTest.php create mode 100644 tests/UrlSearchParamsSerializerTest.php diff --git a/README.md b/README.md index 5366cad2..4741a984 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,39 @@ $seam->locks->unlock_door( ); ``` +### Setting a param to null + +The Seam API distinguishes three states for an updatable param: +omitted (leave the stored value unchanged), null (unset the stored value), +and a value (set it). + +PHP's `null` means omitted. +The SDK removes `null` params from the request entirely, +so passing `null` never unsets a value. +To unset a value, pass the `Seam\NullValue::NULL` sentinel, +which the SDK sends as JSON `null` in request bodies +and as an empty value in query strings: + +```php +use Seam\NullValue; + +// Leaves the name unchanged. +$seam->devices->update(device_id: $device_id, name: null); + +// Unsets the name. +$seam->devices->update(device_id: $device_id, name: NullValue::NULL); +``` + +The other Seam SDKs spell the sentinel `NULL` and its type `Null`, +but those names are reserved in PHP, so both live on one enum: +`Seam\NullValue` is the type, and its single case `NullValue::NULL` +is the value to pass. + +Only pass `NullValue::NULL` for params the API documents as nullable. +Generated methods type nullable params as a union with the sentinel, +e.g. `string|NullValue|null`, so passing it anywhere else fails with a +`TypeError`. + ### Pagination Some Seam API endpoints that return lists of resources support pagination. @@ -388,6 +421,69 @@ $client = new GuzzleHttp\Client([ $seam = Seam\Seam::from_client($client); ``` +#### Serializing URL search params + +The Seam API parses URL search params as complex types. +If you call it with your own HTTP client, +`Seam\StrictUrlSearchParamsSerializer` is exported for that purpose. +The `_strict=true` param is added to any non-empty query +so the Seam API uses strict, schema-aware parsing. +A query with no serializable params remains empty. + +```php +use Seam\StrictUrlSearchParamsSerializer; + +$query = StrictUrlSearchParamsSerializer::serialize([ + "device_ids" => ["device1", "device2"], +]); + +$response = file_get_contents( + "https://connect.getseam.com/devices/list?{$query}", + context: stream_context_create([ + "http" => ["header" => "Authorization: Bearer your-api-key"], + ]), +); +``` + +The serialization defines the name and value of each search param, +where every value is a string. +`Seam\UrlSearchParams` holds those pairs and renders the query string, +as [URLSearchParams] does for the [reference implementation]: + +```php +use Seam\StrictUrlSearchParamsSerializer; +use Seam\UrlSearchParams; + +$search_params = new UrlSearchParams(); + +StrictUrlSearchParamsSerializer::update($search_params, [ + "device_ids" => ["device1", "device2"], +]); + +iterator_to_array($search_params); +// => [["device_ids", "device1"], ["device_ids", "device2"], ["_strict", "true"]] + +(string) $search_params; +// => 'device_ids=device1&device_ids=device2&_strict=true' +``` + +Pass either the query string or the pairs to your HTTP client. +A client may percent-encode a few characters differently than +`URLSearchParams` does, e.g. Guzzle escapes `*` and leaves `~` unescaped, +which the Seam API reads as the same params either way. + +A param set to `null` is omitted, +while a param set to `NullValue::NULL` is serialized to an empty value, +which the Seam API reads as null, +as described in [Setting a param to null](#setting-a-param-to-null). +A param that cannot be represented raises a `Seam\UnserializableParamError`. + +The Seam API parses these params with the corresponding [parser]. + +[URLSearchParams]: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams +[reference implementation]: https://github.com/seamapi/url-search-params-serializer +[parser]: https://github.com/seamapi/url-search-params-parser + #### Errors Every exception the SDK raises implements `Seam\SeamException`, so it can be diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index 24c40fbd..e3813c6d 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -9,6 +9,7 @@ use {{this}}; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; use Seam\Http\ClientFactory; +use Seam\Http\SerializingClient; /** * Client for the Seam API. @@ -33,6 +34,10 @@ class Seam /** * The Guzzle client this instance makes its requests with. + * + * Query params given as a map and NullValue::NULL sentinels in JSON + * bodies are serialized with the Seam standard before the request goes + * out; see Seam\Http\SerializingClient. */ public ClientInterface $client; @@ -78,13 +83,13 @@ class Seam "timeout" => $timeout, ]); - $this->client = $client ?? ClientFactory::create( + $this->client = SerializingClient::wrap($client ?? ClientFactory::create( Options::get_endpoint($endpoint), Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), $guzzle_options, $retries, $timeout - ); + )); {{#each parentClients}} $this->{{namespace}} = new {{clientName}}Client($this->client, $this->defaults); diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index a1e0b018..868640ab 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -10,6 +10,7 @@ import { const clientInterfaceClass = 'GuzzleHttp\\ClientInterface' const bodyClass = 'Seam\\Http\\Body' +const nullValueClass = 'Seam\\NullValue' const resolveActionAttemptClass = 'Seam\\Http\\ResolveActionAttempt' const resourcesNamespace = 'Seam\\Resources' @@ -74,6 +75,21 @@ const onResponseParameter = { required: false, } +// A nullable param accepts the NullValue::NULL sentinel, which sends an +// explicit null to unset a value. A merely optional param does not: optional +// means omit by passing null, and sending null there would unset a value +// instead. Optionality composes with nullability rather than replacing it. +const getParameterPhpType = (parameter: { + type: string + isOptional: boolean + isNullable: boolean +}): string => { + const { type, isOptional, isNullable } = parameter + if (type === 'mixed') return type + if (isNullable) return `${type}|NullValue${isOptional ? '|null' : ''}` + return `${isOptional ? '?' : ''}${type}` +} + const getMethodLayoutContext = ( method: PhpClientMethod, ): MethodLayoutContext => { @@ -96,7 +112,7 @@ const getMethodLayoutContext = ( const signatureParams = sortedParameters .map( (p) => - `${(p.isNullable || p.isOptional) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional ? ' = null' : ''}`, + `${getParameterPhpType(p)} $${p.name}${p.isOptional ? ' = null' : ''}`, ) .concat( usesActionAttempt @@ -109,7 +125,7 @@ const getMethodLayoutContext = ( const documentedEndpointParameters = sortedParameters.map( ({ name, type, description, isOptional, isNullable }) => ({ name, - type, + type: isNullable && type !== 'mixed' ? `${type}|NullValue` : type, description, required: !isOptional, isOptional, @@ -165,9 +181,16 @@ const getUseStatements = (client: PhpClient): string[] => { // Void endpoints never read the response, so they do not decode it. const readsBody = client.methods.some((m) => m.returnResource !== '') + // Only nullable params reference the null sentinel type; importing it + // elsewhere would trip the unused-import lint. + const usesNullValue = client.methods.some((m) => + m.parameters.some((p) => p.isNullable && p.type !== 'mixed'), + ) + return [ clientInterfaceClass, ...(readsBody ? [bodyClass] : []), + ...(usesNullValue ? [nullValueClass] : []), ...(usesActionAttempt ? [resolveActionAttemptClass] : []), ...[...resourceNames].map((name) => `${resourcesNamespace}\\${name}`), ].sort((a, b) => a.localeCompare(b)) diff --git a/src/Http/SerializingClient.php b/src/Http/SerializingClient.php new file mode 100644 index 00000000..7e58742f --- /dev/null +++ b/src/Http/SerializingClient.php @@ -0,0 +1,117 @@ +client->send($request, self::serialize_options($options)); + } + + #[\Override] + public function sendAsync( + RequestInterface $request, + array $options = [], + ): PromiseInterface { + return $this->client->sendAsync( + $request, + self::serialize_options($options), + ); + } + + #[\Override] + public function request( + string $method, + $uri = "", + array $options = [], + ): ResponseInterface { + return $this->client->request( + $method, + $uri, + self::serialize_options($options), + ); + } + + #[\Override] + public function requestAsync( + string $method, + $uri = "", + array $options = [], + ): PromiseInterface { + return $this->client->requestAsync( + $method, + $uri, + self::serialize_options($options), + ); + } + + #[\Override] + public function getConfig(?string $option = null) + { + return $this->client->getConfig($option); + } + + /** + * @param array $options + * @return array + */ + private static function serialize_options(array $options): array + { + if ( + isset($options["query"]) && + ($options["query"] instanceof \stdClass || + is_array($options["query"])) + ) { + $serialized = StrictUrlSearchParamsSerializer::serialize( + $options["query"], + ); + + if ($serialized === "") { + unset($options["query"]); + } else { + $options["query"] = $serialized; + } + } + + if (array_key_exists("json", $options)) { + $options["json"] = NullValue::replace($options["json"]); + } + + return $options; + } +} diff --git a/src/NullValue.php b/src/NullValue.php new file mode 100644 index 00000000..75fbeed3 --- /dev/null +++ b/src/NullValue.php @@ -0,0 +1,67 @@ + NullValue::NULL, "limit" => 20]); + * // => 'limit=20&name=' + * + * UrlSearchParamsSerializer::serialize(["name" => null, "limit" => 20]); + * // => 'limit=20' + * ``` + * + * Use it wherever the Seam API documents null as a meaningful value, e.g., + * to unset a value in an update request, or to filter by an unset value. + */ +enum NullValue +{ + /** + * Sentinel for a param explicitly set to null. + */ + case NULL; + + /** + * Returns a copy of a value with every NullValue::NULL replaced by null. + * + * The sentinel only distinguishes an explicit null from an omitted param + * within this SDK. Once a request body is being serialized, the param is + * known to be present, so the sentinel becomes the null that JSON has. + * + * Recurses into arrays and stdClass objects without mutating them; every + * other value is returned unchanged. + */ + public static function replace(mixed $value): mixed + { + if ($value instanceof self) { + return null; + } + + if (is_array($value)) { + return array_map(self::replace(...), $value); + } + + if ($value instanceof \stdClass) { + return (object) array_map( + self::replace(...), + get_object_vars($value), + ); + } + + return $value; + } +} diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index aabfff1e..d5fd5a91 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\AccessCode; class AccessCodesClient @@ -349,7 +350,7 @@ public function get( * @param string $customer_key Customer key for which you want to list access codes. * @param string $device_id ID of the device for which you want to list access codes. Specify `device_id`, `access_code_ids`, `access_method_id`, `access_grant_id`, or `access_grant_key`. * @param float $limit Numerical limit on the number of access codes to return. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned access codes to include all records that satisfy a partial match using `name`, `code` or `access_code_id`. * @param string $user_identifier_key Your user ID for the user by which to filter access codes. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. @@ -363,7 +364,7 @@ public function list( ?string $customer_key = null, ?string $device_id = null, ?float $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?string $user_identifier_key = null, ?callable $on_response = null, diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 98b1973f..8e28e0de 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\UnmanagedAccessCode; class AccessCodesUnmanagedClient @@ -130,7 +131,7 @@ public function get( * * @param string $device_id ID of the device for which you want to list unmanaged access codes. * @param float $limit Numerical limit on the number of unmanaged access codes to return. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned access codes to include all records that satisfy a partial match using `name`, `code` or `access_code_id`. * @param string $user_identifier_key Your user ID for the user by which to filter unmanaged access codes. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. @@ -139,7 +140,7 @@ public function get( public function list( string $device_id, ?float $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?string $user_identifier_key = null, ?callable $on_response = null, diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 1a17a5ff..9d9074ff 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\AccessGrant; use Seam\Resources\Batch; @@ -36,10 +37,10 @@ public function __construct(ClientInterface $client, array $defaults) * @param array $acs_entrance_ids Set of IDs of the [entrances](https://docs.seam.co/api/acs/systems/list) to which access is being granted. * @param string $customization_profile_id ID of the customization profile to apply to the Access Grant and its access methods. * @param array $device_ids Set of IDs of the [devices](https://docs.seam.co/api/devices/list) to which access is being granted. - * @param string $ends_at Date and time at which the validity of the new grant ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + * @param string|NullValue $ends_at Date and time at which the validity of the new grant ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. * @param mixed $location * @param array $location_ids - * @param string $name Name for the access grant. + * @param string|NullValue $name Name for the access grant. * @param string $reservation_key Reservation key for the access grant. * @param array $space_ids Set of IDs of existing spaces to which access is being granted. * @param array $space_keys Set of keys of existing spaces to which access is being granted. @@ -54,10 +55,10 @@ public function create( ?array $acs_entrance_ids = null, ?string $customization_profile_id = null, ?array $device_ids = null, - ?string $ends_at = null, + string|NullValue|null $ends_at = null, mixed $location = null, ?array $location_ids = null, - ?string $name = null, + string|NullValue|null $name = null, ?string $reservation_key = null, ?array $space_ids = null, ?array $space_keys = null, @@ -227,14 +228,14 @@ public function get_related( * * @param string $access_code_id ID of the access code by which you want to filter the list of Access Grants. * @param array $access_grant_ids IDs of the access grants to retrieve. - * @param string $access_grant_key Filter Access Grants by access_grant_key. Use null to filter for Access Grants without an access_grant_key. + * @param string|NullValue $access_grant_key Filter Access Grants by access_grant_key. Use null to filter for Access Grants without an access_grant_key. * @param string $acs_entrance_id ID of the entrance by which you want to filter the list of Access Grants. * @param string $acs_system_id ID of the access system by which you want to filter the list of Access Grants. * @param string $customer_key Customer key for which you want to list access grants. * @param string $device_id ID of the device by which you want to filter the list of Access Grants. * @param float $limit Numerical limit on the number of access grants to return. * @param string $location_id - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $reservation_key Filter Access Grants by reservation_key. * @param string $space_id ID of the space by which you want to filter the list of Access Grants. * @param string $user_identity_id ID of user identity by which you want to filter the list of Access Grants. @@ -244,14 +245,14 @@ public function get_related( public function list( ?string $access_code_id = null, ?array $access_grant_ids = null, - ?string $access_grant_key = null, + string|NullValue|null $access_grant_key = null, ?string $acs_entrance_id = null, ?string $acs_system_id = null, ?string $customer_key = null, ?string $device_id = null, ?float $limit = null, ?string $location_id = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $reservation_key = null, ?string $space_id = null, ?string $user_identity_id = null, @@ -349,16 +350,16 @@ public function request_access_methods( * * @param string $access_grant_id ID of the Access Grant to update. Provide either `access_grant_id` or `access_grant_key`. * @param string $access_grant_key Key of the Access Grant to update. Provide either `access_grant_id` or `access_grant_key`. - * @param string $ends_at Date and time at which the validity of the grant ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. - * @param string $name Display name for the access grant. + * @param string|NullValue $ends_at Date and time at which the validity of the grant ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + * @param string|NullValue $name Display name for the access grant. * @param string $starts_at Date and time at which the validity of the grant starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @return void OK */ public function update( ?string $access_grant_id = null, ?string $access_grant_key = null, - ?string $ends_at = null, - ?string $name = null, + string|NullValue|null $ends_at = null, + string|NullValue|null $name = null, ?string $starts_at = null, ): void { if ( diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 3b3d6658..e872978d 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\UnmanagedAccessGrant; class AccessGrantsUnmanagedClient @@ -51,7 +52,7 @@ public function get(string $access_grant_id): UnmanagedAccessGrant * @param string $acs_entrance_id ID of the entrance by which you want to filter the list of unmanaged Access Grants. * @param string $acs_system_id ID of the access system by which you want to filter the list of unmanaged Access Grants. * @param float $limit Numerical limit on the number of unmanaged access grants to return. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $reservation_key Filter unmanaged Access Grants by reservation_key. * @param string $user_identity_id ID of user identity by which you want to filter the list of unmanaged Access Grants. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. @@ -61,7 +62,7 @@ public function list( ?string $acs_entrance_id = null, ?string $acs_system_id = null, ?float $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $reservation_key = null, ?string $user_identity_id = null, ?callable $on_response = null, diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 9f22dddc..561c9cd6 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -5,6 +5,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; +use Seam\NullValue; use Seam\Resources\AccessMethod; use Seam\Resources\ActionAttempt; use Seam\Resources\Batch; @@ -193,7 +194,7 @@ public function get_related( * @param string $acs_entrance_id ID of the entrance for which you want to retrieve all access methods that grant access to it. * @param string $device_id ID of the device by which to filter the returned access methods. Must be combined with `access_grant_id`, `access_grant_key`, or `acs_entrance_id`. * @param int $limit Maximum number of records to return per page. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $space_id ID of the space by which to filter the returned access methods. Must be combined with `access_grant_id`, `access_grant_key`, or `acs_entrance_id`. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK @@ -205,7 +206,7 @@ public function list( ?string $acs_entrance_id = null, ?string $device_id = null, ?int $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $space_id = null, ?callable $on_response = null, ): array { diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 86a884c8..12291c33 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; @@ -190,7 +191,7 @@ public function get(string $acs_credential_id): AcsCredential * @param string $created_before Date and time, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format, before which events to return were created. * @param bool $is_multi_phone_sync_credential Indicates whether you want to retrieve only multi-phone sync credentials or non-multi-phone sync credentials. * @param float $limit Number of credentials to return. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned credentials to include all records that satisfy a partial match using `display_name`, `code`, `card_number`, `acs_user_id` or `acs_credential_id`. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK @@ -202,7 +203,7 @@ public function list( ?string $created_before = null, ?bool $is_multi_phone_sync_credential = null, ?float $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?callable $on_response = null, ): array { diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index b7cd6c15..e21f8c41 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -5,6 +5,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; +use Seam\NullValue; use Seam\Resources\AcsEncoder; use Seam\Resources\ActionAttempt; @@ -94,7 +95,7 @@ public function get(string $acs_encoder_id): AcsEncoder * @param array $acs_system_ids IDs of the access systems for which you want to retrieve all encoders. * @param array $acs_encoder_ids IDs of the encoders that you want to retrieve. * @param float $limit Number of encoders to return. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ @@ -103,7 +104,7 @@ public function list( ?array $acs_system_ids = null, ?array $acs_encoder_ids = null, ?float $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?callable $on_response = null, ): array { $request_payload = []; diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 1cd2c1c1..d3c7502f 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -5,6 +5,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; +use Seam\NullValue; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; use Seam\Resources\ActionAttempt; @@ -86,8 +87,8 @@ public function grant_access( * @param string $connected_account_id ID of the connected account for which you want to retrieve all entrances. * @param string $customer_key Customer key for which you want to list entrances. * @param int $limit Maximum number of records to return per page. - * @param string $location_id - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $location_id + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned entrances to include all records that satisfy a partial match using `display_name`. * @param string $space_id ID of the space for which you want to list entrances. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. @@ -101,8 +102,8 @@ public function list( ?string $connected_account_id = null, ?string $customer_key = null, ?int $limit = null, - ?string $location_id = null, - ?string $page_cursor = null, + string|NullValue|null $location_id = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?string $space_id = null, ?callable $on_response = null, diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 5a1b3c11..9c0d8399 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; @@ -189,7 +190,7 @@ public function get( * @param string $acs_system_id ID of the `acs_system` for which you want to retrieve all access system users. * @param string $created_before Timestamp by which to limit returned access system users. Returns users created before this timestamp. * @param int $limit Maximum number of records to return per page. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned access system users to include all records that satisfy a partial match using `full_name`, `phone_number`, `email_address`, `acs_user_id`, `user_identity_id`, `user_identity_full_name` or `user_identity_phone_number`. * @param string $user_identity_email_address Email address of the user identity for which you want to retrieve all access system users. * @param string $user_identity_id ID of the user identity for which you want to retrieve all access system users. @@ -201,7 +202,7 @@ public function list( ?string $acs_system_id = null, ?string $created_before = null, ?int $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?string $user_identity_email_address = null, ?string $user_identity_id = null, diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 11454f3d..79d7eaf6 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -5,6 +5,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; +use Seam\NullValue; use Seam\Resources\ActionAttempt; class ActionAttemptsClient @@ -60,7 +61,7 @@ public function get( * @param array $action_attempt_ids IDs of the action attempts that you want to retrieve. * @param string $device_id ID of the device to filter action attempts by. * @param int $limit Maximum number of records to return per page. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ @@ -68,7 +69,7 @@ public function list( ?array $action_attempt_ids = null, ?string $device_id = null, ?int $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?callable $on_response = null, ): array { $request_payload = []; diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index b5f171b2..25caf8a0 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\ConnectWebview; class ConnectWebviewsClient @@ -153,7 +154,7 @@ public function get(string $connect_webview_id): ConnectWebview * @param mixed $custom_metadata_has Custom metadata pairs by which you want to [filter Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/filtering-connect-webviews-by-custom-metadata). Returns Connect Webviews with `custom_metadata` that contains all of the provided key:value pairs. * @param string $customer_key Customer key for which you want to list connect webviews. * @param float $limit Maximum number of records to return per page. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned Connect Webviews to include all records that satisfy a partial match using `connect_webview_id`, `accepted_providers`, `custom_metadata`, or `customer_key`. * @param string $user_identifier_key Your user ID for the user by which you want to filter Connect Webviews. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. @@ -163,7 +164,7 @@ public function list( mixed $custom_metadata_has = null, ?string $customer_key = null, ?float $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?string $user_identifier_key = null, ?callable $on_response = null, diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 175a34e7..c706e9fa 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\ConnectedAccount; class ConnectedAccountsClient @@ -89,7 +90,7 @@ public function get( * @param mixed $custom_metadata_has Custom metadata pairs by which you want to filter connected accounts. Returns connected accounts with `custom_metadata` that contains all of the provided key:value pairs. * @param string $customer_key Customer key by which you want to filter connected accounts. * @param int $limit Maximum number of records to return per page. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned connected accounts to include all records that satisfy a partial match using `connected_account_id`, `account_type`, `customer_key`, `custom_metadata`, `user_identifier.username`, `user_identifier.email` or `user_identifier.phone`. * @param string $space_id ID of the space by which you want to filter connected accounts. * @param string $user_identifier_key Your user ID for the user by which you want to filter connected accounts. @@ -100,7 +101,7 @@ public function list( mixed $custom_metadata_has = null, ?string $customer_key = null, ?int $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?string $space_id = null, ?string $user_identifier_key = null, diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 0c8ee8b8..ef905a9d 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\Device; use Seam\Resources\DeviceProvider; @@ -76,10 +77,10 @@ public function get(?string $device_id = null, ?string $name = null): Device * @param array $device_types Array of device types for which you want to list devices. * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturer for which you want to list devices. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id + * @param string|NullValue $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK @@ -96,10 +97,10 @@ public function list( ?array $device_types = null, ?float $limit = null, ?string $manufacturer = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?string $space_id = null, - ?string $unstable_location_id = null, + string|NullValue|null $unstable_location_id = null, ?string $user_identifier_key = null, ?callable $on_response = null, ): array { @@ -224,7 +225,7 @@ public function report_provider_metadata(array $devices): void * @param bool $backup_access_code_pool_enabled Indicates whether the device's [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is enabled. Set to `false` to disable the pool: Seam stops refilling it and removes any backup codes that have not yet been pulled into active use. * @param mixed $custom_metadata Custom metadata that you want to associate with the device. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) enables you to store custom information, like customer details or internal IDs from your application. Then, you can [filter devices by the desired metadata](https://docs.seam.co/core-concepts/devices/filtering-devices-by-custom-metadata). * @param bool $is_managed Indicates whether the device is managed. To unmanage a device, set `is_managed` to `false`. - * @param string $name Name for the device. + * @param string|NullValue $name Name for the device. * @param mixed $properties * @return void OK */ @@ -233,7 +234,7 @@ public function update( ?bool $backup_access_code_pool_enabled = null, mixed $custom_metadata = null, ?bool $is_managed = null, - ?string $name = null, + string|NullValue|null $name = null, mixed $properties = null, ): void { $request_payload = []; diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index a2412032..436f55d8 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\UnmanagedDevice; class DevicesUnmanagedClient @@ -77,7 +78,7 @@ public function get( * @param array $device_types Array of device types for which you want to list devices. * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturer for which you want to list devices. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK @@ -93,7 +94,7 @@ public function list( ?array $device_types = null, ?float $limit = null, ?string $manufacturer = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?callable $on_response = null, ): array { diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 9b68f45c..0b152a08 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\Batch; use Seam\Resources\Space; @@ -243,7 +244,7 @@ public function get_related( * * @param string $customer_key Customer key for which you want to list spaces. * @param float $limit Maximum number of records to return per page. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned spaces to include all records that satisfy a partial match using `name`, `space_key`, or `customer_key`. * @param string $space_key Filter spaces by space_key. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. @@ -252,7 +253,7 @@ public function get_related( public function list( ?string $customer_key = null, ?float $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?string $space_key = null, ?callable $on_response = null, diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index af0f5e09..1c299708 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -5,6 +5,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; +use Seam\NullValue; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; @@ -125,7 +126,7 @@ public function cool( * @param float $heating_set_point_fahrenheit Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). * @param string $hvac_mode_setting Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. * @param bool $manual_override_allowed Indicates whether a person at the thermostat or using the API can change the thermostat's settings. - * @param string $name User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + * @param string|NullValue $name User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). * @return void OK */ public function create_climate_preset( @@ -140,7 +141,7 @@ public function create_climate_preset( ?float $heating_set_point_fahrenheit = null, ?string $hvac_mode_setting = null, ?bool $manual_override_allowed = null, - ?string $name = null, + string|NullValue|null $name = null, ): void { $request_payload = []; @@ -517,18 +518,18 @@ public function set_hvac_mode( * Sets a [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) for a specified thermostat. Seam emits a `thermostat.temperature_threshold_exceeded` event and adds a warning on a thermostat if it reports a temperature outside the threshold range. * * @param string $device_id ID of the thermostat device for which you want to set a temperature threshold. - * @param float $lower_limit_celsius Lower temperature limit in in °C. Seam alerts you if the reported temperature is lower than this value. You can specify either `lower_limit` but not both. - * @param float $lower_limit_fahrenheit Lower temperature limit in in °F. Seam alerts you if the reported temperature is lower than this value. You can specify either `lower_limit` but not both. - * @param float $upper_limit_celsius Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either `upper_limit` but not both. - * @param float $upper_limit_fahrenheit Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either `upper_limit` but not both. + * @param float|NullValue $lower_limit_celsius Lower temperature limit in in °C. Seam alerts you if the reported temperature is lower than this value. You can specify either `lower_limit` but not both. + * @param float|NullValue $lower_limit_fahrenheit Lower temperature limit in in °F. Seam alerts you if the reported temperature is lower than this value. You can specify either `lower_limit` but not both. + * @param float|NullValue $upper_limit_celsius Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either `upper_limit` but not both. + * @param float|NullValue $upper_limit_fahrenheit Upper temperature limit in in °C. Seam alerts you if the reported temperature is higher than this value. You can specify either `upper_limit` but not both. * @return void OK */ public function set_temperature_threshold( string $device_id, - ?float $lower_limit_celsius = null, - ?float $lower_limit_fahrenheit = null, - ?float $upper_limit_celsius = null, - ?float $upper_limit_fahrenheit = null, + float|NullValue|null $lower_limit_celsius = null, + float|NullValue|null $lower_limit_fahrenheit = null, + float|NullValue|null $upper_limit_celsius = null, + float|NullValue|null $upper_limit_fahrenheit = null, ): void { $request_payload = []; @@ -571,7 +572,7 @@ public function set_temperature_threshold( * @param float $heating_set_point_fahrenheit Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). * @param string $hvac_mode_setting Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. * @param bool $manual_override_allowed Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * @param string $name User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + * @param string|NullValue $name User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). * @return void OK */ public function update_climate_preset( @@ -586,7 +587,7 @@ public function update_climate_preset( ?float $heating_set_point_fahrenheit = null, ?string $hvac_mode_setting = null, ?bool $manual_override_allowed = null, - ?string $name = null, + string|NullValue|null $name = null, ): void { $request_payload = []; @@ -642,25 +643,25 @@ public function update_climate_preset( * Updates the thermostat weekly program for a thermostat device. To configure a weekly program, specify the ID of the daily program that you want to use for each day of the week. When you update a weekly program, the set of programs that you specify overwrites any previous weekly program for the thermostat. * * @param string $device_id ID of the thermostat device for which you want to update the weekly program. - * @param string $friday_program_id ID of the thermostat daily program to run on Fridays. - * @param string $monday_program_id ID of the thermostat daily program to run on Mondays. - * @param string $saturday_program_id ID of the thermostat daily program to run on Saturdays. - * @param string $sunday_program_id ID of the thermostat daily program to run on Sundays. - * @param string $thursday_program_id ID of the thermostat daily program to run on Thursdays. - * @param string $tuesday_program_id ID of the thermostat daily program to run on Tuesdays. - * @param string $wednesday_program_id ID of the thermostat daily program to run on Wednesdays. + * @param string|NullValue $friday_program_id ID of the thermostat daily program to run on Fridays. + * @param string|NullValue $monday_program_id ID of the thermostat daily program to run on Mondays. + * @param string|NullValue $saturday_program_id ID of the thermostat daily program to run on Saturdays. + * @param string|NullValue $sunday_program_id ID of the thermostat daily program to run on Sundays. + * @param string|NullValue $thursday_program_id ID of the thermostat daily program to run on Thursdays. + * @param string|NullValue $tuesday_program_id ID of the thermostat daily program to run on Tuesdays. + * @param string|NullValue $wednesday_program_id ID of the thermostat daily program to run on Wednesdays. * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function update_weekly_program( string $device_id, - ?string $friday_program_id = null, - ?string $monday_program_id = null, - ?string $saturday_program_id = null, - ?string $sunday_program_id = null, - ?string $thursday_program_id = null, - ?string $tuesday_program_id = null, - ?string $wednesday_program_id = null, + string|NullValue|null $friday_program_id = null, + string|NullValue|null $monday_program_id = null, + string|NullValue|null $saturday_program_id = null, + string|NullValue|null $sunday_program_id = null, + string|NullValue|null $thursday_program_id = null, + string|NullValue|null $tuesday_program_id = null, + string|NullValue|null $wednesday_program_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index 38ad4f07..e9d1c21d 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\ThermostatSchedule; class ThermostatsSchedulesClient @@ -32,7 +33,7 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $ends_at Date and time at which the new thermostat schedule ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @param string $starts_at Date and time at which the new thermostat schedule starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @param bool $is_override_allowed Indicates whether a person at the thermostat or using the API can change the thermostat's settings while the new schedule is active. See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * @param int $max_override_period_minutes Number of minutes for which a person at the thermostat or using the API can change the thermostat's settings after the activation of the scheduled climate preset. See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * @param int|NullValue $max_override_period_minutes Number of minutes for which a person at the thermostat or using the API can change the thermostat's settings after the activation of the scheduled climate preset. See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). * @param string $name Name of the thermostat schedule. * @return ThermostatSchedule OK */ @@ -42,7 +43,7 @@ public function create( string $ends_at, string $starts_at, ?bool $is_override_allowed = null, - ?int $max_override_period_minutes = null, + int|NullValue|null $max_override_period_minutes = null, ?string $name = null, ): ThermostatSchedule { $request_payload = []; @@ -147,7 +148,7 @@ public function list( * @param string $climate_preset_key Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the thermostat schedule. * @param string $ends_at Date and time at which the thermostat schedule ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @param bool $is_override_allowed Indicates whether a person at the thermostat or using the API can change the thermostat's settings while the schedule is active. See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * @param int $max_override_period_minutes Number of minutes for which a person at the thermostat or using the API can change the thermostat's settings after the activation of the scheduled climate preset. See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * @param int|NullValue $max_override_period_minutes Number of minutes for which a person at the thermostat or using the API can change the thermostat's settings after the activation of the scheduled climate preset. See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). * @param string $name Name of the thermostat schedule. * @param string $starts_at Date and time at which the thermostat schedule starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @return void OK @@ -157,7 +158,7 @@ public function update( ?string $climate_preset_key = null, ?string $ends_at = null, ?bool $is_override_allowed = null, - ?int $max_override_period_minutes = null, + int|NullValue|null $max_override_period_minutes = null, ?string $name = null, ?string $starts_at = null, ): void { diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 7acc9569..7138c3f1 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsSystem; use Seam\Resources\AcsUser; @@ -69,18 +70,18 @@ public function add_acs_user( * Creates a new [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity). * * @param array $acs_system_ids List of access system IDs to associate with the new user identity through access system users. If there's no user with the same email address or phone number in the specified access systems, a new access system user is created. If there is an existing user with the same email or phone number in the specified access systems, the user is linked to the user identity. - * @param string $email_address Unique email address for the new user identity. - * @param string $full_name Full name of the user associated with the new user identity. - * @param string $phone_number Unique phone number for the new user identity in E.164 format (for example, +15555550100). - * @param string $user_identity_key Unique key for the new user identity. + * @param string|NullValue $email_address Unique email address for the new user identity. + * @param string|NullValue $full_name Full name of the user associated with the new user identity. + * @param string|NullValue $phone_number Unique phone number for the new user identity in E.164 format (for example, +15555550100). + * @param string|NullValue $user_identity_key Unique key for the new user identity. * @return UserIdentity OK */ public function create( ?array $acs_system_ids = null, - ?string $email_address = null, - ?string $full_name = null, - ?string $phone_number = null, - ?string $user_identity_key = null, + string|NullValue|null $email_address = null, + string|NullValue|null $full_name = null, + string|NullValue|null $phone_number = null, + string|NullValue|null $user_identity_key = null, ): UserIdentity { $request_payload = []; @@ -225,7 +226,7 @@ public function grant_access_to_device( * @param string $created_before Timestamp by which to limit returned user identities. Returns user identities created before this timestamp. * @param string $credential_manager_acs_system_id `acs_system_id` of the credential manager by which you want to filter the list of user identities. * @param int $limit Maximum number of records to return per page. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned user identities to include all records that satisfy a partial match using `full_name`, `phone_number`, `email_address` or `user_identity_id`. * @param array $user_identity_ids Array of user identity IDs by which to filter the list of user identities. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. @@ -235,7 +236,7 @@ public function list( ?string $created_before = null, ?string $credential_manager_acs_system_id = null, ?int $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?array $user_identity_ids = null, ?callable $on_response = null, @@ -418,18 +419,18 @@ public function revoke_access_to_device( * Updates a specified [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity). * * @param string $user_identity_id ID of the user identity that you want to update. - * @param string $email_address Unique email address for the user identity. - * @param string $full_name Full name of the user associated with the user identity. - * @param string $phone_number Unique phone number for the user identity. - * @param string $user_identity_key Unique key for the user identity. + * @param string|NullValue $email_address Unique email address for the user identity. + * @param string|NullValue $full_name Full name of the user associated with the user identity. + * @param string|NullValue $phone_number Unique phone number for the user identity. + * @param string|NullValue $user_identity_key Unique key for the user identity. * @return void OK */ public function update( string $user_identity_id, - ?string $email_address = null, - ?string $full_name = null, - ?string $phone_number = null, - ?string $user_identity_key = null, + string|NullValue|null $email_address = null, + string|NullValue|null $full_name = null, + string|NullValue|null $phone_number = null, + string|NullValue|null $user_identity_key = null, ): void { $request_payload = []; diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 92a77c06..58540237 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; +use Seam\NullValue; use Seam\Resources\UnmanagedUserIdentity; class UserIdentitiesUnmanagedClient @@ -50,7 +51,7 @@ public function get(string $user_identity_id): UnmanagedUserIdentity * * @param string $created_before Timestamp by which to limit returned unmanaged user identities. Returns user identities created before this timestamp. * @param int $limit Maximum number of records to return per page. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned unmanaged user identities to include all records that satisfy a partial match using `full_name`, `phone_number`, `email_address`, `user_identity_id` or `acs_system_id`. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK @@ -58,7 +59,7 @@ public function get(string $user_identity_id): UnmanagedUserIdentity public function list( ?string $created_before = null, ?int $limit = null, - ?string $page_cursor = null, + string|NullValue|null $page_cursor = null, ?string $search = null, ?callable $on_response = null, ): array { diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 752b182b..a1336c9d 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -5,6 +5,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; +use Seam\NullValue; use Seam\Resources\ActionAttempt; use Seam\Resources\Workspace; @@ -31,7 +32,7 @@ public function __construct(ClientInterface $client, array $defaults) * * @param string $name Name of the new workspace. * @param string $company_name Company name for the new workspace. - * @param string $connect_partner_name Connect partner name for the new workspace. + * @param string|NullValue $connect_partner_name Connect partner name for the new workspace. * @param mixed $connect_webview_customization [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews) customizations for the new workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). * @param bool $is_sandbox Indicates whether the new workspace is a [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces). * @param string $organization_id ID of the organization to associate with the new workspace. @@ -44,7 +45,7 @@ public function __construct(ClientInterface $client, array $defaults) public function create( string $name, ?string $company_name = null, - ?string $connect_partner_name = null, + string|NullValue|null $connect_partner_name = null, mixed $connect_webview_customization = null, ?bool $is_sandbox = null, ?string $organization_id = null, diff --git a/src/Seam.php b/src/Seam.php index d6109e6b..43a5ba4c 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -26,6 +26,7 @@ use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; use Seam\Http\ClientFactory; +use Seam\Http\SerializingClient; /** * Client for the Seam API. @@ -67,6 +68,10 @@ class Seam /** * The Guzzle client this instance makes its requests with. + * + * Query params given as a map and NullValue::NULL sentinels in JSON + * bodies are serialized with the Seam standard before the request goes + * out; see Seam\Http\SerializingClient. */ public ClientInterface $client; @@ -112,19 +117,20 @@ public function __construct( "timeout" => $timeout, ]); - $this->client = + $this->client = SerializingClient::wrap( $client ?? - ClientFactory::create( - Options::get_endpoint($endpoint), - Auth::get_auth_headers( - $api_key, - $personal_access_token, - $workspace_id, + ClientFactory::create( + Options::get_endpoint($endpoint), + Auth::get_auth_headers( + $api_key, + $personal_access_token, + $workspace_id, + ), + $guzzle_options, + $retries, + $timeout, ), - $guzzle_options, - $retries, - $timeout, - ); + ); $this->access_codes = new AccessCodesClient( $this->client, diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index a75dd841..bf8154e6 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\Http\ClientFactory; +use Seam\Http\SerializingClient; use Seam\Routes\WorkspacesClient; /** @@ -47,17 +48,18 @@ public function __construct( "timeout" => $timeout, ]); - $this->client = + $this->client = SerializingClient::wrap( $client ?? - ClientFactory::create( - Options::get_endpoint($endpoint), - Auth::get_auth_headers_without_workspace( - $personal_access_token, + ClientFactory::create( + Options::get_endpoint($endpoint), + Auth::get_auth_headers_without_workspace( + $personal_access_token, + ), + $guzzle_options, + $retries, + $timeout, ), - $guzzle_options, - $retries, - $timeout, - ); + ); $this->workspaces = new WorkspacesProxy( new WorkspacesClient($this->client, [ diff --git a/src/StrictUrlSearchParamsSerializer.php b/src/StrictUrlSearchParamsSerializer.php new file mode 100644 index 00000000..a01be993 --- /dev/null +++ b/src/StrictUrlSearchParamsSerializer.php @@ -0,0 +1,52 @@ +|\stdClass $params + * + * @throws UnserializableParamError If any param could not be serialized + */ + public static function serialize(array|\stdClass $params): string + { + $search_params = new UrlSearchParams(); + self::update($search_params, $params); + + return $search_params->to_string(); + } + + /** + * Updates existing URL search params with serialized params and strict + * API validation enabled. + * + * @param array|\stdClass $params + * + * @throws UnserializableParamError If any param could not be serialized + */ + public static function update( + UrlSearchParams $search_params, + array|\stdClass $params, + ): void { + UrlSearchParamsSerializer::update($search_params, $params); + + if (count($search_params) > 0) { + $search_params->delete("_strict"); + $search_params->append("_strict", "true"); + } + } +} diff --git a/src/UnserializableParamError.php b/src/UnserializableParamError.php new file mode 100644 index 00000000..7bc12e8c --- /dev/null +++ b/src/UnserializableParamError.php @@ -0,0 +1,26 @@ +name; + } +} diff --git a/src/UrlSearchParams.php b/src/UrlSearchParams.php new file mode 100644 index 00000000..d16f67c6 --- /dev/null +++ b/src/UrlSearchParams.php @@ -0,0 +1,241 @@ + + */ +class UrlSearchParams implements \Countable, \IteratorAggregate +{ + /** @var list */ + private array $pairs = []; + + /** + * @param string|array|list|null $init + * A query string, a map of names to values, or a list of + * name-value pairs + */ + public function __construct(string|array|null $init = null) + { + if ($init === null) { + return; + } + + if (is_string($init)) { + $query = str_starts_with($init, "?") ? substr($init, 1) : $init; + + foreach (explode("&", $query) as $pair) { + if ($pair === "") { + continue; + } + + $parts = explode("=", $pair, 2); + $this->pairs[] = [ + urldecode($parts[0]), + urldecode($parts[1] ?? ""), + ]; + } + + return; + } + + foreach ($init as $name => $value) { + if (is_array($value) && is_int($name)) { + $this->pairs[] = [(string) $value[0], (string) $value[1]]; + } else { + $this->pairs[] = [(string) $name, (string) $value]; + } + } + } + + /** + * Appends a name-value pair, keeping any existing pairs with this name. + */ + public function append(string $name, string $value): void + { + $this->pairs[] = [$name, $value]; + } + + /** + * Sets the value associated with a name. + * + * Replaces the first pair with this name and removes any others, so the + * pair keeps its position. Appends a new pair if no pair with this name + * exists. + */ + public function set(string $name, string $value): void + { + if (!$this->has($name)) { + $this->append($name, $value); + return; + } + + $pairs = []; + $is_set = false; + + foreach ($this->pairs as $pair) { + if ($pair[0] !== $name) { + $pairs[] = $pair; + } elseif (!$is_set) { + $pairs[] = [$name, $value]; + $is_set = true; + } + } + + $this->pairs = $pairs; + } + + /** + * Returns the value of the first pair with this name, or null if no pair + * with this name exists. + */ + public function get(string $name): ?string + { + foreach ($this->pairs as [$existing_name, $value]) { + if ($existing_name === $name) { + return $value; + } + } + + return null; + } + + /** + * Returns the values of all pairs with this name, in insertion order. + * + * @return list + */ + public function get_all(string $name): array + { + $values = []; + + foreach ($this->pairs as [$existing_name, $value]) { + if ($existing_name === $name) { + $values[] = $value; + } + } + + return $values; + } + + /** + * Returns whether a pair with this name exists. + */ + public function has(string $name): bool + { + foreach ($this->pairs as [$existing_name, $_]) { + if ($existing_name === $name) { + return true; + } + } + + return false; + } + + /** + * Removes all pairs with this name. + */ + public function delete(string $name): void + { + $this->pairs = array_values( + array_filter($this->pairs, fn(array $pair) => $pair[0] !== $name), + ); + } + + /** + * Sorts all pairs by name, comparing bytes. + * + * Sorting is stable, so the relative order of pairs with the same name + * is preserved, which is what keeps array element order. Byte order + * matches URLSearchParams.sort() for ASCII names; a name beyond the + * Basic Multilingual Plane may sort differently than in JavaScript. + */ + public function sort(): void + { + usort($this->pairs, fn(array $a, array $b) => strcmp($a[0], $b[0])); + } + + /** + * Serializes all pairs to a query string, without a leading `?`. + * + * Every pair gets an `=`, including empty values, e.g. `name=`. + */ + public function to_string(): string + { + return implode( + "&", + array_map( + fn(array $pair) => self::encode_form_component($pair[0]) . + "=" . + self::encode_form_component($pair[1]), + $this->pairs, + ), + ); + } + + public function __toString(): string + { + return $this->to_string(); + } + + #[\Override] + public function count(): int + { + return count($this->pairs); + } + + /** + * @return \ArrayIterator, array{string, string}> + */ + #[\Override] + public function getIterator(): \ArrayIterator + { + return new \ArrayIterator($this->pairs); + } + + /** + * Percent-encodes a string with the WHATWG + * application/x-www-form-urlencoded serializer, applied to the UTF-8 + * bytes of the string. + * + * The safe set is not the RFC 3986 unreserved set, so neither urlencode + * nor rawurlencode produces it: `*` is emitted literally and `~` is + * escaped, the exact opposite of both. + */ + private static function encode_form_component(string $value): string + { + $encoded = ""; + $length = strlen($value); + + for ($i = 0; $i < $length; $i++) { + $character = $value[$i]; + $byte = ord($character); + + $is_safe = + ($byte >= 0x30 && $byte <= 0x39) || + ($byte >= 0x41 && $byte <= 0x5a) || + ($byte >= 0x61 && $byte <= 0x7a) || + $character === "*" || + $character === "-" || + $character === "." || + $character === "_"; + + if ($is_safe) { + $encoded .= $character; + } elseif ($character === " ") { + $encoded .= "+"; + } else { + $encoded .= sprintf("%%%02X", $byte); + } + } + + return $encoded; + } +} diff --git a/src/UrlSearchParamsSerializer.php b/src/UrlSearchParamsSerializer.php new file mode 100644 index 00000000..f0fad8d4 --- /dev/null +++ b/src/UrlSearchParamsSerializer.php @@ -0,0 +1,354 @@ +|\stdClass $params + * + * @throws UnserializableParamError If any param could not be serialized + */ + public static function serialize(array|\stdClass $params): string + { + $search_params = new UrlSearchParams(); + self::update($search_params, $params); + + return $search_params->to_string(); + } + + /** + * Updates existing URL search params with serialized params. + * + * Existing params are preserved unless overwritten by a serialized + * param. All params are sorted by name. + * + * @param array|\stdClass $params + * + * @throws UnserializableParamError If any param could not be serialized + */ + public static function update( + UrlSearchParams $search_params, + array|\stdClass $params, + ): void { + self::nested_update($search_params, $params, []); + $search_params->sort(); + } + + /** + * @param array|\stdClass $params + * @param list $path + */ + private static function nested_update( + UrlSearchParams $search_params, + array|\stdClass $params, + array $path, + ): void { + $entries = + $params instanceof \stdClass ? get_object_vars($params) : $params; + + foreach ($entries as $key => $value) { + // PHP silently casts a numeric-string key to an integer, so a + // non-string key cannot be told apart from one; both are + // rejected rather than serialized ambiguously. + if (!is_string($key)) { + throw new UnserializableParamError( + (string) $key, + "is a " . + get_debug_type($key) . + " which is unsupported as a parameter name", + ); + } + + if (str_contains($key, ".")) { + throw new UnserializableParamError( + $key, + 'contains one or more dots "." in its name which is unsupported', + ); + } + + $current_path = [...$path, $key]; + + if (self::is_plain_object($value)) { + /** @var array|\stdClass $value */ + self::nested_update($search_params, $value, $current_path); + continue; + } + + $name = implode(".", $current_path); + + if ($value === null) { + continue; + } + + if ($value === "") { + continue; + } + + if (is_array($value)) { + self::update_from_array($search_params, $name, $value); + continue; + } + + $search_params->set($name, self::serialize_value($name, $value)); + } + } + + /** + * An array is a plain object when its keys are not the sequential + * integers of a list. The empty array is a list: it is the empty + * JavaScript Array, not an empty plain object. + */ + private static function is_plain_object(mixed $value): bool + { + if ($value instanceof \stdClass) { + return true; + } + + return is_array($value) && $value !== [] && !array_is_list($value); + } + + /** + * @param list $values + */ + private static function update_from_array( + UrlSearchParams $search_params, + string $name, + array $values, + ): void { + if ($values === []) { + // The one case where an empty value is meaningful: the parser + // reads `name=` as the empty array. + $search_params->set($name, ""); + return; + } + + if (count($values) === 1 && $values[0] === "") { + throw new UnserializableParamError( + $name, + "is a single element array containing the empty string which is unsupported", + ); + } + + if (in_array("", $values, true)) { + throw new UnserializableParamError( + $name, + "is an array containing the empty string which is unsupported", + ); + } + + foreach ($values as $value) { + if ($value === null || $value instanceof NullValue) { + throw new UnserializableParamError( + $name, + "is an array containing null or undefined values which is unsupported", + ); + } + } + + foreach ($values as $value) { + $search_params->append($name, self::serialize_value($name, $value)); + } + } + + /** + * @throws UnserializableParamError If the value could not be serialized + */ + private static function serialize_value(string $name, mixed $value): string + { + if ($value instanceof NullValue) { + return ""; + } + + if (is_string($value)) { + return $value; + } + + if (is_bool($value)) { + return $value ? "true" : "false"; + } + + if (is_int($value)) { + return (string) $value; + } + + if (is_float($value)) { + return self::format_number($name, $value); + } + + if ($value instanceof \DateTimeInterface) { + return self::format_datetime($value); + } + + throw new UnserializableParamError( + $name, + "is a " . get_debug_type($value), + ); + } + + /** + * Formats an instant as JavaScript's Date.prototype.toISOString does: + * always UTC, always exactly three fractional digits, always a literal + * `Z`. Sub-millisecond precision is truncated, not rounded. + */ + private static function format_datetime(\DateTimeInterface $value): string + { + $utc = \DateTimeImmutable::createFromInterface($value)->setTimezone( + new \DateTimeZone("UTC"), + ); + $milliseconds = intdiv((int) $utc->format("u"), 1000); + + return sprintf( + "%04d-%s.%03dZ", + (int) $utc->format("Y"), + $utc->format("m-d\TH:i:s"), + $milliseconds, + ); + } + + /** + * Formats a float with the ECMAScript Number::toString algorithm. + * + * PHP's own float formatting differs from it in several ways: an + * integral float renders as `1.0` rather than `1`, the exponent + * threshold is not at 1e21 and 1e-7, and exponents are spelled `E+21` + * rather than `e+21`. + */ + private static function format_number(string $name, float $value): string + { + if (is_nan($value)) { + throw new UnserializableParamError($name, "is NaN"); + } + + if (is_infinite($value)) { + throw new UnserializableParamError( + $name, + $value > 0 ? "is Infinity" : "is -Infinity", + ); + } + + if ($value === 0.0) { + return "0"; + } + + $sign = $value < 0 ? "-" : ""; + [$digits, $point] = self::shortest_digits(abs($value)); + + return $sign . self::format_digits($digits, $point); + } + + /** + * Returns the shortest digit string that round-trips to the float, with + * the position of the decimal point relative to those digits, as the + * ECMAScript Number::toString algorithm requires. + * + * @return array{string, int} + */ + private static function shortest_digits(float $value): array + { + // At -1, PHP's float-to-string conversion produces the shortest + // string that round-trips. Restored because the setting also affects + // the caller's own serialize() and json_encode() calls. + $precision = ini_set("serialize_precision", "-1"); + + try { + $repr = var_export($value, true); + } finally { + if ($precision !== false) { + ini_set("serialize_precision", $precision); + } + } + + if ( + preg_match( + '/^(\d+)(?:\.(\d+))?(?:E([+-]?\d+))?$/i', + $repr, + $matches, + ) !== 1 + ) { + throw new \RuntimeException( + "Could not parse the PHP float representation: {$repr}", + ); + } + + $digits = $matches[1] . ($matches[2] ?? ""); + $point = strlen($matches[1]) + (int) ($matches[3] ?? "0"); + + $stripped = ltrim($digits, "0"); + $point -= strlen($digits) - strlen($stripped); + $digits = rtrim($stripped, "0"); + + return [$digits, $point]; + } + + /** + * Formats digits and a decimal point position per ECMAScript + * Number::toString. The four branches and the constants 21 and -6 are + * the specification. + * + * @param string $digits Significant digits, without trailing zeros + * @param int $point Position of the decimal point relative to the digits + */ + private static function format_digits(string $digits, int $point): string + { + $count = strlen($digits); + + if ($count <= $point && $point <= 21) { + return $digits . str_repeat("0", $point - $count); + } + + if (0 < $point && $point <= 21) { + return substr($digits, 0, $point) . "." . substr($digits, $point); + } + + if (-6 < $point && $point <= 0) { + return "0." . str_repeat("0", -$point) . $digits; + } + + $exponent = $point - 1; + $exponent_sign = $exponent >= 0 ? "+" : "-"; + $mantissa = + $count === 1 ? $digits : $digits[0] . "." . substr($digits, 1); + + return $mantissa . "e" . $exponent_sign . abs($exponent); + } +} diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index 6afecd90..520d08f9 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -35,7 +35,10 @@ public function testSendsDefaultHeaders(): void $request = $recorder->request(); $this->assertSame("/devices/get", $request->getUri()->getPath()); - $this->assertSame("device_id=d1", $request->getUri()->getQuery()); + $this->assertSame( + "device_id=d1&_strict=true", + $request->getUri()->getQuery(), + ); $this->assertSame( "Bearer seam_apikey_token", diff --git a/tests/NullValueTest.php b/tests/NullValueTest.php new file mode 100644 index 00000000..4dacdf76 --- /dev/null +++ b/tests/NullValueTest.php @@ -0,0 +1,78 @@ +assertInstanceOf(NullValue::class, NullValue::NULL); + $this->assertNotInstanceOf(NullValue::class, null); + $this->assertNotInstanceOf(NullValue::class, "NULL"); + } + + public function testIsASingleton(): void + { + $this->assertSame(NullValue::NULL, NullValue::NULL); + $this->assertCount(1, NullValue::cases()); + } + + public function testReadsAsItsOwnName(): void + { + $this->assertSame("NULL", NullValue::NULL->name); + } + + public function testReplaceReplacesTheSentinel(): void + { + $this->assertNull(NullValue::replace(NullValue::NULL)); + } + + public function testReplaceLeavesOtherValuesUnchanged(): void + { + foreach ([null, "NULL", 0, false, 20.5, ["a"]] as $value) { + $this->assertSame($value, NullValue::replace($value)); + } + } + + public function testReplaceRecursesIntoArrays(): void + { + $this->assertSame( + [ + "name" => null, + "codes" => [null, "1234"], + "nested" => ["code" => null], + ], + NullValue::replace([ + "name" => NullValue::NULL, + "codes" => [NullValue::NULL, "1234"], + "nested" => ["code" => NullValue::NULL], + ]), + ); + } + + public function testReplaceCopiesStdClassObjectsWithoutMutatingThem(): void + { + $payload = (object) [ + "name" => NullValue::NULL, + "nested" => (object) ["code" => NullValue::NULL], + ]; + + $replaced = NullValue::replace($payload); + + $this->assertNotSame($payload, $replaced); + $this->assertNull($replaced->name); + $this->assertNull($replaced->nested->code); + $this->assertSame(NullValue::NULL, $payload->name); + $this->assertSame(NullValue::NULL, $payload->nested->code); + } + + public function testReplaceDoesNotDescendIntoStrings(): void + { + $this->assertSame("a NULL b", NullValue::replace("a NULL b")); + } +} diff --git a/tests/SearchParamsTest.php b/tests/SearchParamsTest.php new file mode 100644 index 00000000..eda7adbb --- /dev/null +++ b/tests/SearchParamsTest.php @@ -0,0 +1,267 @@ + []]; + private const DEVICE = ["device" => ["device_id" => "device1"]]; + + private function seam(RecordingClient $recording): Seam + { + return Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: $recording->guzzle_options(), + ); + } + + public function testClientSerializesSearchParams(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + + $this->seam($recording)->client->request("GET", "/devices/list", [ + "query" => [ + "device_ids" => ["device1", "device2"], + "custom_metadata_has" => ["tag" => "front", "floor" => 2], + "limit" => 20, + ], + ]); + + $this->assertSame( + "custom_metadata_has.floor=2" . + "&custom_metadata_has.tag=front" . + "&device_ids=device1" . + "&device_ids=device2" . + "&limit=20" . + "&_strict=true", + $recording->request()->getUri()->getQuery(), + ); + $this->assertSame("GET", $recording->request()->getMethod()); + $this->assertSame( + "/devices/list", + $recording->request()->getUri()->getPath(), + ); + } + + public function testClientDoesNotReencodeTheSerializedSearchParams(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + + $this->seam($recording)->client->request("GET", "/devices/list", [ + "query" => ["search" => "a *~ b"], + ]); + + $this->assertSame( + "search=a+*%7E+b&_strict=true", + $recording->request()->getUri()->getQuery(), + ); + } + + public function testClientSerializesEmptyArraysToAnEmptyValue(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + + $this->seam($recording)->client->request("GET", "/devices/list", [ + "query" => ["device_ids" => []], + ]); + + $this->assertSame( + "device_ids=&_strict=true", + $recording->request()->getUri()->getQuery(), + ); + } + + public function testClientOmitsSearchParamsSetToNull(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + + $this->seam($recording)->client->request("GET", "/devices/list", [ + "query" => ["search" => null, "limit" => 20], + ]); + + $this->assertSame( + "limit=20&_strict=true", + $recording->request()->getUri()->getQuery(), + ); + } + + public function testClientSerializesSearchParamsSetToNullValue(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + + $this->seam($recording)->client->request("GET", "/devices/list", [ + "query" => ["search" => NullValue::NULL, "limit" => 20], + ]); + + $this->assertSame( + "limit=20&search=&_strict=true", + $recording->request()->getUri()->getQuery(), + ); + } + + public function testClientSendsNoQueryStringWithoutSearchParams(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + $seam = $this->seam($recording); + + $seam->client->request("GET", "/devices/list", ["query" => []]); + $seam->client->request("GET", "/devices/list", [ + "query" => ["search" => null], + ]); + $seam->client->request("GET", "/devices/list"); + + foreach ([0, 1, 2] as $index) { + $this->assertSame( + "/devices/list", + $recording->request($index)->getRequestTarget(), + ); + } + } + + public function testClientSerializesSearchParamsOfEveryVerb(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + $seam = $this->seam($recording); + + $verbs = ["GET", "POST", "PUT", "PATCH", "DELETE"]; + + foreach ($verbs as $verb) { + $seam->client->request($verb, "/devices/list", [ + "query" => ["sync" => true], + ]); + } + + foreach ($verbs as $index => $verb) { + $this->assertSame($verb, $recording->request($index)->getMethod()); + $this->assertSame( + "sync=true&_strict=true", + $recording->request($index)->getUri()->getQuery(), + ); + } + } + + public function testClientPassesSearchParamsItDidNotSerializeToGuzzle(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + + $this->seam($recording)->client->request("GET", "/devices/list", [ + "query" => "device_ids=device1", + ]); + + $this->assertSame( + "device_ids=device1", + $recording->request()->getUri()->getQuery(), + ); + } + + public function testClientRejectsASearchParamItCannotSerialize(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICES), + ); + $seam = $this->seam($recording); + + try { + $seam->client->request("GET", "/devices/list", [ + "query" => ["search" => new \SplStack()], + ]); + $this->fail("Expected an UnserializableParamError"); + } catch (UnserializableParamError $error) { + $this->assertSame("search", $error->getName()); + } + + $this->assertSame(0, $recording->request_count()); + } + + public function testClientSerializesNullValueInAJsonBodyToNull(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICE), + ); + + $this->seam($recording)->client->request("POST", "/devices/update", [ + "json" => (object) [ + "device_id" => "device1", + "name" => NullValue::NULL, + "properties" => ["code" => NullValue::NULL], + ], + ]); + + $this->assertSame("POST", $recording->request()->getMethod()); + $this->assertSame( + [ + "device_id" => "device1", + "name" => null, + "properties" => ["code" => null], + ], + json_decode((string) $recording->request()->getBody(), true), + ); + } + + public function testClientLeavesAJsonBodyWithoutNullValueUnchanged(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICE), + ); + + $body = [ + "device_id" => "device1", + "name" => "Front Door", + "limit" => 20, + "sync" => true, + ]; + + $this->seam($recording)->client->request("POST", "/devices/update", [ + "json" => (object) $body, + ]); + + $this->assertSame( + $body, + json_decode((string) $recording->request()->getBody(), true), + ); + } + + public function testClientSerializesTheSearchParamsOfAGeneratedRoute(): void + { + $recording = RecordingClient::repeating( + RecordingClient::json(200, self::DEVICE), + ); + + $this->seam($recording)->devices->get(name: "Front Door"); + + $this->assertSame("GET", $recording->request()->getMethod()); + $this->assertSame( + "/devices/get", + $recording->request()->getUri()->getPath(), + ); + $this->assertSame( + "name=Front+Door&_strict=true", + $recording->request()->getUri()->getQuery(), + ); + } +} diff --git a/tests/StrictUrlSearchParamsSerializerTest.php b/tests/StrictUrlSearchParamsSerializerTest.php new file mode 100644 index 00000000..e75affc3 --- /dev/null +++ b/tests/StrictUrlSearchParamsSerializerTest.php @@ -0,0 +1,45 @@ +assertSame("", StrictUrlSearchParamsSerializer::serialize([])); + $this->assertSame( + "foo=d&_strict=true", + StrictUrlSearchParamsSerializer::serialize(["foo" => "d"]), + ); + } + + public function testAppendsStrictAfterTheSortedParams(): void + { + $this->assertSame( + "B=2&a=1&_strict=true", + StrictUrlSearchParamsSerializer::serialize(["a" => 1, "B" => 2]), + ); + } + + public function testReplacesACallerSuppliedStrictParam(): void + { + $this->assertSame( + "_strict=true", + StrictUrlSearchParamsSerializer::serialize(["_strict" => false]), + ); + } + + public function testUpdateCountsExistingParamsAsNonEmpty(): void + { + $search_params = new UrlSearchParams([["foo", "bar"]]); + StrictUrlSearchParamsSerializer::update($search_params, []); + + $this->assertSame("foo=bar&_strict=true", $search_params->to_string()); + } +} diff --git a/tests/UrlSearchParamsSerializerTest.php b/tests/UrlSearchParamsSerializerTest.php new file mode 100644 index 00000000..764a4fc0 --- /dev/null +++ b/tests/UrlSearchParamsSerializerTest.php @@ -0,0 +1,645 @@ +assertSame("", self::serialize([])); + $this->assertSame("", self::serialize(new \stdClass())); + } + + public function testSerializesString(): void + { + $this->assertSame("foo=d", self::serialize(["foo" => "d"])); + $this->assertSame("foo=null", self::serialize(["foo" => "null"])); + $this->assertSame( + "foo=undefined", + self::serialize(["foo" => "undefined"]), + ); + $this->assertSame("foo=0", self::serialize(["foo" => "0"])); + } + + public function testRemovesTheEmptyString(): void + { + $this->assertSame("", self::serialize(["foo" => ""])); + $this->assertSame( + "foo=d", + self::serialize(["foo" => "d", "bar" => ""]), + ); + } + + public function testSerializesInt(): void + { + $this->assertSame("foo=1", self::serialize(["foo" => 1])); + $this->assertSame("foo=0", self::serialize(["foo" => 0])); + $this->assertSame("foo=-42", self::serialize(["foo" => -42])); + } + + public function testSerializesLargeIntWithFullPrecision(): void + { + $this->assertSame( + "foo=9007199254740993", + self::serialize(["foo" => 9007199254740993]), + ); + $this->assertSame( + "foo=9223372036854775807", + self::serialize(["foo" => PHP_INT_MAX]), + ); + } + + public function testSerializesFloat(): void + { + $this->assertSame("foo=23.8", self::serialize(["foo" => 23.8])); + $this->assertSame("foo=-23.8", self::serialize(["foo" => -23.8])); + $this->assertSame( + "foo=0.30000000000000004", + self::serialize(["foo" => 0.1 + 0.2]), + ); + } + + public function testSerializesFloatUsingTheEcmascriptNumberFormat(): void + { + $this->assertSame("foo=1", self::serialize(["foo" => 1.0])); + $this->assertSame("foo=0", self::serialize(["foo" => -0.0])); + $this->assertSame("foo=100", self::serialize(["foo" => 100.0])); + $this->assertSame( + "foo=10000000000000000", + self::serialize(["foo" => 1e16]), + ); + $this->assertSame( + "foo=100000000000000000000", + self::serialize(["foo" => 1e20]), + ); + $this->assertSame("foo=1e%2B21", self::serialize(["foo" => 1e21])); + $this->assertSame("foo=0.0001", self::serialize(["foo" => 0.0001])); + $this->assertSame("foo=0.000001", self::serialize(["foo" => 1e-6])); + $this->assertSame("foo=1e-7", self::serialize(["foo" => 1e-7])); + $this->assertSame("foo=5e-324", self::serialize(["foo" => 5e-324])); + $this->assertSame( + "foo=1.7976931348623157e%2B308", + self::serialize(["foo" => PHP_FLOAT_MAX]), + ); + } + + public function testSerializesBool(): void + { + $this->assertSame("foo=true", self::serialize(["foo" => true])); + $this->assertSame("foo=false", self::serialize(["foo" => false])); + $this->assertSame( + "bar=false&foo=true", + self::serialize(["foo" => true, "bar" => false]), + ); + } + + public function testRemovesNullParams(): void + { + $this->assertSame("", self::serialize(["bar" => null])); + $this->assertSame( + "foo=1", + self::serialize(["foo" => 1, "bar" => null]), + ); + } + + public function testSerializesNullValueParams(): void + { + $this->assertSame("bar=", self::serialize(["bar" => NullValue::NULL])); + $this->assertSame( + "bar=&foo=1", + self::serialize(["foo" => 1, "bar" => NullValue::NULL]), + ); + } + + public function testRemovesNullParamsAtAnyDepth(): void + { + $this->assertSame( + "foo.baz=1", + self::serialize(["foo" => ["bar" => null, "baz" => 1]]), + ); + $this->assertSame("", self::serialize(["foo" => ["bar" => null]])); + } + + public function testSerializesEmptyArrayParams(): void + { + $this->assertSame("bar=", self::serialize(["bar" => []])); + $this->assertSame( + "bar=&foo=1", + self::serialize(["foo" => 1, "bar" => []]), + ); + } + + public function testSerializesArrayParamsWithOneValue(): void + { + $this->assertSame("bar=a", self::serialize(["bar" => ["a"]])); + $this->assertSame( + "bar=a&foo=1", + self::serialize(["foo" => 1, "bar" => ["a"]]), + ); + } + + public function testSerializesArrayParamsWithManyValues(): void + { + $this->assertSame( + "bar=a&bar=2&foo=1", + self::serialize(["foo" => 1, "bar" => ["a", "2"]]), + ); + $this->assertSame( + "bar=null&bar=2&bar=undefined&foo=1", + self::serialize(["foo" => 1, "bar" => ["null", "2", "undefined"]]), + ); + } + + public function testSerializesArrayParamsWithMixedValues(): void + { + $this->assertSame( + "bar=1&bar=a&bar=true&bar=1970-01-01T00%3A00%3A00.000Z", + self::serialize([ + "bar" => [ + 1, + "a", + true, + new \DateTimeImmutable("1970-01-01T00:00:00Z"), + ], + ]), + ); + } + + public function testSerializesDatetime(): void + { + $this->assertSame( + "foo=1&now=2025-02-24T18%3A44%3A39.000Z", + self::serialize([ + "foo" => 1, + "now" => new \DateTimeImmutable("2025-02-24T18:44:39Z"), + ]), + ); + } + + public function testSerializesMutableDatetime(): void + { + $now = new \DateTime("2025-02-24T18:44:39Z"); + + $this->assertSame( + "now=2025-02-24T18%3A44%3A39.000Z", + self::serialize(["now" => $now]), + ); + $this->assertSame("2025-02-24T18:44:39+00:00", $now->format("c")); + } + + public function testSerializesDatetimeWithMilliseconds(): void + { + $this->assertSame( + "now=2025-02-24T18%3A44%3A39.123Z", + self::serialize([ + "now" => new \DateTimeImmutable("2025-02-24T18:44:39.123Z"), + ]), + ); + } + + public function testTruncatesDatetimeMicroseconds(): void + { + $this->assertSame( + "now=2025-02-24T18%3A44%3A39.123Z", + self::serialize([ + "now" => new \DateTimeImmutable("2025-02-24T18:44:39.123999Z"), + ]), + ); + } + + public function testSerializesDatetimeAsUtc(): void + { + $this->assertSame( + "now=2025-02-24T18%3A44%3A39.000Z", + self::serialize([ + "now" => new \DateTimeImmutable("2025-02-24T13:44:39-05:00"), + ]), + ); + } + + public function testSerializesDatetimeBeforeTheEpoch(): void + { + $this->assertSame( + "then=1969-12-31T23%3A59%3A59.000Z", + self::serialize([ + "then" => new \DateTimeImmutable("1969-12-31T23:59:59Z"), + ]), + ); + } + + public function testZeroPadsTheYearToFourDigits(): void + { + $this->assertSame( + "then=0050-01-02T03%3A04%3A05.000Z", + self::serialize([ + "then" => new \DateTimeImmutable("0050-01-02T03:04:05Z"), + ]), + ); + } + + public function testSerializesNestedParams(): void + { + $this->assertSame( + "bar.baz=a&foo=1", + self::serialize(["foo" => 1, "bar" => ["baz" => "a"]]), + ); + + $this->assertSame( + "bar.baz.x.z=1&foo=1", + self::serialize([ + "foo" => 1, + "bar" => ["baz" => ["x" => ["z" => 1]]], + ]), + ); + + $this->assertSame( + "bar.baz.x.z=&foo=1", + self::serialize([ + "foo" => 1, + "bar" => ["baz" => ["x" => ["z" => NullValue::NULL]]], + ]), + ); + + $this->assertSame( + "bar.baz=1&bar.baz=a&foo=1", + self::serialize(["foo" => 1, "bar" => ["baz" => [1, "a"]]]), + ); + + $this->assertSame( + "bar=2", + self::serialize(["foo" => new \stdClass(), "bar" => 2]), + ); + + $this->assertSame( + "bar=2", + self::serialize(["foo" => ["x" => new \stdClass()], "bar" => 2]), + ); + + $this->assertSame( + "bar.baz.x.z=", + self::serialize([ + "foo" => new \stdClass(), + "bar" => [ + "baz" => [ + "x" => ["z" => NullValue::NULL, "t" => new \stdClass()], + "q" => new \stdClass(), + ], + ], + ]), + ); + } + + public function testSerializesStdClassParams(): void + { + $this->assertSame( + "bar.baz=a&foo=1", + self::serialize( + (object) ["foo" => 1, "bar" => (object) ["baz" => "a"]], + ), + ); + } + + public function testSortsParamsByName(): void + { + $this->assertSame( + "a=2&b=1&c=3", + self::serialize(["b" => 1, "a" => 2, "c" => 3]), + ); + $this->assertSame( + "A=2&B=4&a=3&b=1", + self::serialize(["b" => 1, "A" => 2, "a" => 3, "B" => 4]), + ); + $this->assertSame( + "a1=3&a10=1&a2=2", + self::serialize(["a10" => 1, "a2" => 2, "a1" => 3]), + ); + $this->assertSame( + "a.b=3&a.z=2&zz=1", + self::serialize(["zz" => 1, "a" => ["z" => 2, "b" => 3]]), + ); + $this->assertSame( + "a.b=2&ab=1", + self::serialize(["ab" => 1, "a" => ["b" => 2]]), + ); + } + + public function testSortingPreservesArrayOrder(): void + { + $this->assertSame( + "a=1&b=3&b=1&b=2", + self::serialize(["b" => ["3", "1", "2"], "a" => 1]), + ); + } + + public function testEncodesParamsAsFormUrlencoded(): void + { + $this->assertSame("foo=a+b", self::serialize(["foo" => "a b"])); + $this->assertSame("foo=a%2Bb", self::serialize(["foo" => "a+b"])); + $this->assertSame("foo=a%7Eb", self::serialize(["foo" => "a~b"])); + $this->assertSame("foo=a*b", self::serialize(["foo" => "a*b"])); + $this->assertSame( + "foo=abcXYZ019*-._", + self::serialize(["foo" => "abcXYZ019*-._"]), + ); + $this->assertSame("foo=a+*%7E+b", self::serialize(["foo" => "a *~ b"])); + $this->assertSame( + "foo=a%26b%3Dc%3Fd%23e%2Ff", + self::serialize(["foo" => "a&b=c?d#e/f"]), + ); + $this->assertSame("foo=100%25", self::serialize(["foo" => "100%"])); + $this->assertSame("foo=a%0Ab", self::serialize(["foo" => "a\nb"])); + } + + public function testEncodesUnicodeParams(): void + { + $this->assertSame( + "foo=h%C3%A9llo+w%C3%B6rld", + self::serialize(["foo" => "héllo wörld"]), + ); + $this->assertSame( + "foo=%E6%97%A5%E6%9C%AC%E8%AA%9E", + self::serialize(["foo" => "日本語"]), + ); + $this->assertSame("%F0%9F%94%92=a", self::serialize(["🔒" => "a"])); + $this->assertSame("a+b=1", self::serialize(["a b" => 1])); + } + + public function testCannotSerializeKeysContainingADot(): void + { + $this->expectException(UnserializableParamError::class); + + self::serialize(["foo.bar" => 1]); + } + + public function testCannotSerializeNestedKeysContainingADot(): void + { + $this->expectException(UnserializableParamError::class); + + self::serialize(["foo" => ["bar.baz" => 1]]); + } + + public function testCannotSerializeNonStringKeys(): void + { + $this->expectException(UnserializableParamError::class); + + self::serialize(["foo" => [1 => "a", "b" => "c"]]); + } + + public function testCannotSerializeClosures(): void + { + $this->expectException(UnserializableParamError::class); + + self::serialize(["foo" => fn() => null]); + } + + /** + * @dataProvider provideNonFiniteFloats + */ + public function testCannotSerializeNonFiniteFloats( + float $value, + string $message, + ): void { + try { + self::serialize(["foo" => $value]); + $this->fail("Expected an UnserializableParamError"); + } catch (UnserializableParamError $error) { + $this->assertSame( + "Could not serialize parameter: 'foo' {$message}", + $error->getMessage(), + ); + $this->assertSame("foo", $error->getName()); + } + } + + public static function provideNonFiniteFloats(): array + { + return [ + "NaN" => [NAN, "is NaN"], + "Infinity" => [INF, "is Infinity"], + "-Infinity" => [-INF, "is -Infinity"], + ]; + } + + public function testCannotSerializeArbitraryObjects(): void + { + $this->expectException(UnserializableParamError::class); + + self::serialize([ + "foo" => new class { + public string $device_id = "a"; + }, + ]); + } + + /** + * @dataProvider provideUnserializableArrays + */ + public function testCannotSerializeArrayParamsWithUnserializableValues( + array $params, + ): void { + $this->expectException(UnserializableParamError::class); + + self::serialize($params); + } + + public static function provideUnserializableArrays(): array + { + return [ + "single empty string" => [["foo" => [""]]], + "null element" => [["bar" => ["a", null]]], + "NullValue element" => [["bar" => ["a", NullValue::NULL]]], + "nested list" => [["bar" => ["a", ["s"]]]], + "nested empty list" => [["bar" => ["a", []]]], + "nested list with empty string" => [["bar" => ["a", [""]]]], + "nested object" => [["bar" => ["a", new \stdClass()]]], + "nested map" => [["bar" => ["a", ["x" => 2]]]], + "closure element" => [["bar" => ["a", fn() => null]]], + "empty strings around values" => [ + ["foo" => 1, "bar" => ["", "a", ""]], + ], + "leading empty string" => [["foo" => 1, "bar" => ["", "a", "2"]]], + "only empty strings" => [["foo" => 1, "bar" => ["", "", ""]]], + "NaN element" => [["foo" => [1, NAN]]], + ]; + } + + public function testUnserializableParamErrorMessage(): void + { + try { + self::serialize(["foo" => ["bar.baz" => 1]]); + $this->fail("Expected an UnserializableParamError"); + } catch (UnserializableParamError $error) { + $this->assertSame( + "Could not serialize parameter: 'bar.baz' contains one or " . + 'more dots "." in its name which is unsupported', + $error->getMessage(), + ); + $this->assertSame("bar.baz", $error->getName()); + } + } + + public function testUnserializableParamErrorMessageUsesTheFullPath(): void + { + try { + self::serialize(["foo" => ["bar" => NAN]]); + $this->fail("Expected an UnserializableParamError"); + } catch (UnserializableParamError $error) { + $this->assertSame( + "Could not serialize parameter: 'foo.bar' is NaN", + $error->getMessage(), + ); + } + } + + public function testUpdateUrlSearchParams(): void + { + $search_params = new UrlSearchParams(); + UrlSearchParamsSerializer::update($search_params, [ + "foo" => "d", + "bar" => 2, + ]); + + $this->assertSame("bar=2&foo=d", $search_params->to_string()); + } + + public function testUpdatePreservesExistingParams(): void + { + $search_params = new UrlSearchParams([["foo", "bar"]]); + UrlSearchParamsSerializer::update($search_params, [ + "name" => "Dax", + "age" => 27, + "is_admin" => true, + "tags" => ["cars", "planes"], + ]); + + $this->assertSame( + "age=27&foo=bar&is_admin=true&name=Dax&tags=cars&tags=planes", + $search_params->to_string(), + ); + } + + public function testUpdateOverwritesExistingParams(): void + { + $search_params = new UrlSearchParams([ + ["foo", "a"], + ["bar", "x"], + ["foo", "b"], + ]); + UrlSearchParamsSerializer::update($search_params, ["foo" => "new"]); + + $this->assertSame("bar=x&foo=new", $search_params->to_string()); + } + + public function testUpdateAppendsArrayParams(): void + { + $search_params = new UrlSearchParams([["foo", "old"]]); + UrlSearchParamsSerializer::update($search_params, ["foo" => [1, 2]]); + + $this->assertSame("foo=old&foo=1&foo=2", $search_params->to_string()); + } + + public function testUpdateKeepsExistingParamsForAbsentValues(): void + { + foreach ([null, "", new \stdClass()] as $value) { + $search_params = new UrlSearchParams([["foo", "a"]]); + UrlSearchParamsSerializer::update($search_params, [ + "foo" => $value, + ]); + + $this->assertSame("foo=a", $search_params->to_string()); + } + } + + public function testUrlSearchParamsFromQueryString(): void + { + $search_params = new UrlSearchParams( + "?a=1&b=hello+world&c=%F0%9F%94%92&d", + ); + + $this->assertSame("1", $search_params->get("a")); + $this->assertSame("hello world", $search_params->get("b")); + $this->assertSame("🔒", $search_params->get("c")); + $this->assertSame("", $search_params->get("d")); + $this->assertSame( + "a=1&b=hello+world&c=%F0%9F%94%92&d=", + $search_params->to_string(), + ); + } + + public function testUrlSearchParamsFromMap(): void + { + $this->assertSame( + "a=1&b=2", + (new UrlSearchParams(["a" => "1", "b" => "2"]))->to_string(), + ); + } + + public function testUrlSearchParamsAppendAndGet(): void + { + $search_params = new UrlSearchParams(); + $search_params->append("foo", "a"); + $search_params->append("foo", "b"); + + $this->assertSame("a", $search_params->get("foo")); + $this->assertSame(["a", "b"], $search_params->get_all("foo")); + $this->assertNull($search_params->get("bar")); + $this->assertSame([], $search_params->get_all("bar")); + $this->assertCount(2, $search_params); + $this->assertSame( + [["foo", "a"], ["foo", "b"]], + iterator_to_array($search_params), + ); + } + + public function testUrlSearchParamsSetKeepsTheFirstPairsPosition(): void + { + $search_params = new UrlSearchParams([ + ["foo", "a"], + ["bar", "x"], + ["foo", "b"], + ]); + $search_params->set("foo", "c"); + + $this->assertSame( + [["foo", "c"], ["bar", "x"]], + iterator_to_array($search_params), + ); + + $search_params->set("baz", "y"); + + $this->assertSame("y", $search_params->get("baz")); + } + + public function testUrlSearchParamsHasAndDelete(): void + { + $search_params = new UrlSearchParams([["foo", "a"], ["foo", "b"]]); + + $this->assertTrue($search_params->has("foo")); + + $search_params->delete("foo"); + + $this->assertFalse($search_params->has("foo")); + $this->assertCount(0, $search_params); + } + + public function testUrlSearchParamsCastsToString(): void + { + $this->assertSame( + "foo=a+b", + (string) new UrlSearchParams([["foo", "a b"]]), + ); + } +} From 323533edbec2d550b1c35c9f6224242fe2e1517a Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Fri, 14 Aug 2026 06:06:18 +0000 Subject: [PATCH 37/80] 4.0.0-beta.4 --- package-lock.json | 4 ++-- package.json | 2 +- src/Utils/PackageVersion.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 915b4902..bee8eb24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.3", + "version": "4.0.0-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.3", + "version": "4.0.0-beta.4", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 1d40393a..2f722833 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.3", + "version": "4.0.0-beta.4", "type": "module", "private": true, "license": "MIT", diff --git a/src/Utils/PackageVersion.php b/src/Utils/PackageVersion.php index 20bdf3b5..068616f2 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Utils/PackageVersion.php @@ -10,7 +10,7 @@ class PackageVersion * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.3"; + public const VERSION = "4.0.0-beta.4"; public static function get(): string { From 1fc25579b99fc0043b67dac99694adb3e93b4e38 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 23:48:05 -0700 Subject: [PATCH 38/80] fix: Rename PackageVersion to Version --- README.md | 4 ++-- src/Http/ClientFactory.php | 4 ++-- src/{Utils/PackageVersion.php => Version.php} | 4 ++-- tests/HeadersTest.php | 6 +++--- tests/{PackageVersionTest.php => VersionTest.php} | 10 +++++----- version.ts | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) rename src/{Utils/PackageVersion.php => Version.php} (88%) rename tests/{PackageVersionTest.php => VersionTest.php} (76%) diff --git a/README.md b/README.md index 4741a984..d4b8bd21 100644 --- a/README.md +++ b/README.md @@ -618,7 +618,7 @@ and dispatches the [Version](.github/workflows/version.yml) workflow. Run the [Version](.github/workflows/version.yml) workflow with the version to cut. It runs `npm version`, which bumps the `version` field in `package.json`, -injects that version into `Seam\Utils\PackageVersion`, creates a signed `v*` +injects that version into `Seam\Version`, creates a signed `v*` git tag and pushes it. Pushing the tag triggers the [Publish](.github/workflows/publish.yml) workflow, and [Packagist](https://packagist.org/packages/seamapi/seam) @@ -629,7 +629,7 @@ picks up the new tag from its GitHub webhook. > step in between. > This repository therefore keeps the version in `package.json`, which is a > development manifest that is not published, and injects it into the -> `Seam\Utils\PackageVersion::VERSION` constant used for the +> `Seam\Version::VERSION` constant used for the > `seam-sdk-version` header. > > The injection runs from `version.ts`, wired to the `version` lifecycle diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php index a479e305..06ad77c3 100644 --- a/src/Http/ClientFactory.php +++ b/src/Http/ClientFactory.php @@ -4,7 +4,7 @@ use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; -use Seam\Utils\PackageVersion; +use Seam\Version; /** * Builds the Guzzle client a Seam client makes its requests with. @@ -90,7 +90,7 @@ public static function create( */ private static function sdk_headers(): array { - $version = PackageVersion::get(); + $version = Version::get(); return [ "User-Agent" => "seam-php/" . $version, diff --git a/src/Utils/PackageVersion.php b/src/Version.php similarity index 88% rename from src/Utils/PackageVersion.php rename to src/Version.php index 068616f2..2ee9424e 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Version.php @@ -1,8 +1,8 @@ getHeaderLine("seam-sdk-name"), ); $this->assertSame( - PackageVersion::get(), + Version::get(), $request->getHeaderLine("seam-sdk-version"), ); $this->assertSame( @@ -57,7 +57,7 @@ public function testSendsDefaultHeaders(): void $request->getHeaderLine("seam-lts-version"), ); $this->assertSame( - "seam-php/" . PackageVersion::get(), + "seam-php/" . Version::get(), $request->getHeaderLine("User-Agent"), ); } diff --git a/tests/PackageVersionTest.php b/tests/VersionTest.php similarity index 76% rename from tests/PackageVersionTest.php rename to tests/VersionTest.php index 7d7d9fd3..b6f5c209 100644 --- a/tests/PackageVersionTest.php +++ b/tests/VersionTest.php @@ -3,9 +3,9 @@ declare(strict_types=1); use PHPUnit\Framework\TestCase; -use Seam\Utils\PackageVersion; +use Seam\Version; -final class PackageVersionTest extends TestCase +final class VersionTest extends TestCase { public function testVersionMatchesPackageJson(): void { @@ -19,8 +19,8 @@ public function testVersionMatchesPackageJson(): void $this->assertSame( $package["version"], - PackageVersion::get(), - "Seam\\Utils\\PackageVersion is out of date with package.json. " . + Version::get(), + "Seam\\Version is out of date with package.json. " . "It is injected by the version lifecycle script when a " . "version is cut and should not be edited by hand.", ); @@ -31,6 +31,6 @@ public function testVersionIsUsedAsTheSdkVersionHeader(): void $seam = new \Seam\Seam("seam_apikey1_token"); $headers = $seam->client->getConfig("headers"); - $this->assertSame(PackageVersion::get(), $headers["seam-sdk-version"]); + $this->assertSame(Version::get(), $headers["seam-sdk-version"]); } } diff --git a/version.ts b/version.ts index e64f1f83..495a2c71 100644 --- a/version.ts +++ b/version.ts @@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url' import { $ } from 'execa' -const versionFile = './src/Utils/PackageVersion.php' +const versionFile = './src/Version.php' const versionPattern = /public const VERSION = "[^"]*";/ From 7f724b80bcfc6a92c7096c69e790ef5498af42bf Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Fri, 14 Aug 2026 06:49:01 +0000 Subject: [PATCH 39/80] 4.0.0-beta.5 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index bee8eb24..04b95968 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 2f722833..3ba660bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index 2ee9424e..508db381 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.4"; + public const VERSION = "4.0.0-beta.5"; public static function get(): string { From 1a8466a872f6bf5ab5b2342581421de23dfeab87 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Fri, 14 Aug 2026 14:19:28 -0700 Subject: [PATCH 40/80] Remove SEAM_API_URL env var --- src/Options.php | 18 +----------------- tests/EnvTest.php | 43 ------------------------------------------- 2 files changed, 1 insertion(+), 60 deletions(-) diff --git a/src/Options.php b/src/Options.php index 287adfa0..265ced95 100644 --- a/src/Options.php +++ b/src/Options.php @@ -18,24 +18,8 @@ public static function get_endpoint(?string $endpoint = null): string public static function get_endpoint_from_env(): ?string { - $seam_api_url = self::get_env("SEAM_API_URL"); $seam_endpoint = self::get_env("SEAM_ENDPOINT"); - - if ($seam_api_url !== null) { - self::warn( - "Using the SEAM_API_URL environment variable is deprecated. " . - "Support will be removed in a later major version. Use SEAM_ENDPOINT instead.", - ); - } - - if ($seam_api_url !== null && $seam_endpoint !== null) { - self::warn( - "Detected both the SEAM_API_URL and SEAM_ENDPOINT environment variables. " . - "Using SEAM_ENDPOINT.", - ); - } - - return $seam_endpoint ?? $seam_api_url; + return $seam_endpoint } public static function is_seam_options_with_api_key( diff --git a/tests/EnvTest.php b/tests/EnvTest.php index 79876cce..02b25763 100644 --- a/tests/EnvTest.php +++ b/tests/EnvTest.php @@ -17,7 +17,6 @@ final class EnvTest extends FakeSeamConnectTestCase "SEAM_PERSONAL_ACCESS_TOKEN", "SEAM_WORKSPACE_ID", "SEAM_ENDPOINT", - "SEAM_API_URL", ]; /** @var array */ @@ -83,48 +82,6 @@ public function testEndpointOptionWinsOverTheEnvironment(): void ); } - public function testSeamEndpointWinsOverTheDeprecatedSeamApiUrl(): void - { - putenv("SEAM_ENDPOINT=https://endpoint.example.com"); - putenv("SEAM_API_URL=https://api-url.example.com"); - - // Both the deprecation and the precedence notice are raised. - $endpoint = @Options::get_endpoint(); - - $this->assertSame("https://endpoint.example.com", $endpoint); - } - - public function testDeprecatedSeamApiUrlIsStillHonored(): void - { - putenv("SEAM_API_URL=https://api-url.example.com"); - - $this->assertSame( - "https://api-url.example.com", - @Options::get_endpoint(), - ); - } - - public function testDeprecatedSeamApiUrlWarns(): void - { - putenv("SEAM_API_URL=https://api-url.example.com"); - - set_error_handler( - static fn( - int $severity, - string $message, - ) => throw new \ErrorException($message), - E_USER_WARNING, - ); - - try { - $this->expectException(\ErrorException::class); - $this->expectExceptionMessage("SEAM_API_URL"); - Options::get_endpoint(); - } finally { - restore_error_handler(); - } - } - public function testReadsThePersonalAccessTokenAndWorkspaceIdFromTheEnvironment(): void { putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); From fcac2b3688406133703c1d766157a146368d2051 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 07:51:26 +0000 Subject: [PATCH 41/80] docs: Add migration guides for v2 to v3 and v3 to v4 Model the guide on the Python SDK's MIGRATION.md: each major version gets a summary table of breaking changes, a section per change with before/after examples, the additions worth adopting during the migration, and a checklist. The v3 to v4 guide covers the SDK unification: the Seam\Seam client with named options, the Guzzle client as the public client, the retry policy and 30-second timeout, wait_for_action_attempt replacing poll_until_ready, the namespaced nested resource classes, local enforcement of required parameters, preferred HTTP methods with the URL search params standard, and the error handling and pagination refinements. The v2 to v3 guide covers the blueprint regeneration: the two reordered get methods, the Seam\Resources and Seam\Routes namespaces, the typed action attempt result, the alphabetical nullable resource constructors, and the removed undocumented resource classes. The README's Upgrading from 3.x section links to the full guide. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WRhzZ42JiS7LGUynykjQXP --- MIGRATION.md | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 339 insertions(+) create mode 100644 MIGRATION.md diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 00000000..929c3803 --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,337 @@ +# Migrating from seamapi/seam v3 to v4 + +This guide covers upgrading from `seamapi/seam` v3.x to v4 of the [Seam PHP SDK](https://github.com/seamapi/php). + +Version 4 unifies this SDK with the Seam SDKs for Python, Ruby, and JavaScript: it adds personal access token authentication, webhook verification, explicit retries, client-side validation, and an explicit null sentinel. Endpoint methods keep their names, arguments, and return types — code that calls endpoints with named arguments is mostly unaffected. The breaking changes are concentrated in client construction, HTTP behavior, and the layout of the generated classes. + +If you are upgrading from v2.x, apply the [v2 to v3 guide](#migrating-from-seamapiseam-v2-to-v3) first (or both guides together). + +## Installation + +```sh +composer require "seamapi/seam:^4" +``` + +## Summary of breaking changes + +| Change | Affects you if... | +| ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| [PHP 8.2+ required](#php-82-or-later-is-required) | You run PHP 8.0 or 8.1 | +| [`Seam\Seam` replaces `Seam\SeamClient`](#seamseam-replaces-seamseamclient) | You construct the client (everyone) | +| [`$seam->client` is the Guzzle client](#seam-client-is-the-guzzle-client) | You use `$seam->client`, `$seam->request()`, or the removed public properties | +| [Requests are retried and time out sooner](#requests-are-retried-and-time-out-sooner) | You depend on requests never being retried, or on the 60-second timeout | +| [`poll_until_ready` is replaced](#poll_until_ready-is-replaced-by-wait_for_action_attempt) | You call `$seam->action_attempts->poll_until_ready()` or rely on its 20 s/0.4 s timing | +| [Nested resource classes are namespaced](#nested-resource-classes-are-namespaced) | You type-hint nested classes such as `Seam\Resources\DeviceProperties` | +| [Missing required parameters fail locally](#missing-required-parameters-fail-locally) | You call endpoints with missing parameters and rely on the server's 400 response | +| [Preferred HTTP methods and URL search params](#endpoints-use-preferred-http-methods) | You inspect traffic in a proxy, mock server, or firewall rules | +| [Error handling refinements](#error-handling-refinements) | You compare `getRequestId()` to `""`, or depend on 3xx responses passing through | +| [Pagination metadata is a `Seam\Pagination`](#pagination-metadata-is-a-typed-object) | You treat the paginator's metadata as a `stdClass` | +| [`Seam\Version` replaces `Seam\Utils\PackageVersion`](#seamversion-replaces-packageversion) | You read the package version programmatically | + +## PHP 8.2 or later is required + +Version 3 declared support for PHP 8.0. Version 4 requires PHP >= 8.2, since PHP 8.1 reached end of life in December 2025. The SDK also gains two dependencies: `caseyamcl/guzzle_retry_middleware` and `svix/svix`. + +## `Seam\Seam` replaces `Seam\SeamClient` + +The client class is renamed from `Seam\SeamClient` to `Seam\Seam`; there is no compatibility alias. The constructor takes named options, so `$endpoint` is no longer the second positional argument, and `$throw_http_errors` is removed — an API error always raises a Seam exception: + +```php +// v3 +$seam = new Seam\SeamClient("your-api-key", "https://example.com"); + +// v4 +$seam = new Seam\Seam(api_key: "your-api-key", endpoint: "https://example.com"); +``` + +Static factories are available as an alternative to the constructor: `Seam::from_api_key()`, `Seam::from_personal_access_token()`, and `Seam::from_client()`. + +In v3, `$throw_http_errors = true` made Guzzle throw its own `RequestException` before the SDK could map the error. If you passed it, catch the Seam error classes instead — they are unchanged (see [Error handling refinements](#error-handling-refinements)). + +## `$seam->client` is the Guzzle client + +In v3, `$seam->client` was a bare `GuzzleHttp\Client`, and the SDK's error mapping lived in the separate `$seam->request()` helper. In v4, `$seam->client` is the fully configured Guzzle client the SDK itself uses — error mapping and retries are Guzzle middleware, and query params and JSON bodies are serialized per the Seam standards on the way out. Anything Guzzle can do is available on it directly, and it throws the same Seam exceptions the endpoint methods do. + +`Seam::request()` is removed. Call the client instead and decode the PSR-7 response yourself: + +```php +// v3: returns decoded JSON +$res = $seam->request("POST", "/devices/list", json: (object) []); + +// v4: returns a PSR-7 response +$res = json_decode( + $seam->client->request("GET", "/devices/list")->getBody() +); +``` + +The property is typed `GuzzleHttp\ClientInterface` rather than `GuzzleHttp\Client`, so update any type hints. Two other public members are removed with no replacement on the instance: + +- `$seam->api_key` is gone. +- `$seam->ltsVersion` and the global `LTS_VERSION` constant are gone. Use the `Seam\Seam::LTS_VERSION` class constant. + +To configure the underlying client, pass `guzzle_options` (merged into the Guzzle client's config), or pass a preconfigured client via the `client` option. A preconfigured client carries its own endpoint and authorization, so combining it with `api_key`, `endpoint`, or any other option that would configure one raises `Seam\InvalidOptionsError` instead of being silently ignored: + +```php +$seam = new Seam\Seam( + api_key: "your-api-key", + guzzle_options: ["proxy" => "http://localhost:8125"], +); +``` + +## Requests are retried and time out sooner + +Version 3 never retried a request and timed out after 60 seconds. Version 4 makes up to three attempts by default: the initial request and two retries. Retries are limited to `GET`, `HEAD`, `OPTIONS`, `PUT`, and `DELETE` requests that fail because of a transport error, timeout, HTTP 429 response, or HTTP 5xx response. `POST` and `PATCH` requests are never retried. Retries use exponential backoff with jitter, and a `Retry-After` header is honored when it is longer than the calculated backoff. + +The timeout drops from 60 to 30 seconds, covers connecting as well as reading, and applies to each attempt rather than the whole sequence. Both behaviors are options: + +```php +$seam = new Seam\Seam( + retries: 0, // Disable retries. + timeout: 60.0, // Restore the v3 timeout, in seconds. +); +``` + +Note the interaction with the new HTTP methods: because reads are now `GET`, they are retried by default, which they were not in v3 (as `POST`). + +## `poll_until_ready` is replaced by `wait_for_action_attempt` + +`$seam->action_attempts->poll_until_ready()` is removed. Endpoints that return an [action attempt](https://docs.seam.co/latest/core-concepts/action-attempts) still wait for it by default, but the waiting is configured through `wait_for_action_attempt`, which now also accepts a `timeout` and `polling_interval` (in seconds), per request or as a client-wide default: + +```php +// v3 +$seam->locks->unlock_door(device_id: $device_id, wait_for_action_attempt: true); +$seam->action_attempts->poll_until_ready($action_attempt_id, timeout: 30.0); + +// v4 +$seam->locks->unlock_door( + device_id: $device_id, + wait_for_action_attempt: ["timeout" => 30.0, "polling_interval" => 2.0], +); + +$seam = new Seam\Seam(wait_for_action_attempt: false); // Client-wide default. +``` + +The default timing changes from a 20-second timeout with a 0.4-second polling interval to a 10-second timeout with a 1-second polling interval. Pass an explicit `timeout` if 10 seconds is too short for your devices. `Seam\ActionAttemptFailedError` and `Seam\ActionAttemptTimeoutError` are raised exactly as in v3. + +## Nested resource classes are namespaced + +Top-level resource classes are unchanged: `$seam->devices->get()` still returns a `Seam\Resources\Device`. The generated classes that type _nested_ properties move from resource-prefixed names in `Seam\Resources` to sub-namespaces mirroring the property path: + +```php +// v3 +use Seam\Resources\DeviceProperties; +use Seam\Resources\DeviceBattery; + +// v4 +use Seam\Resources\Device\Properties; +use Seam\Resources\Device\Properties\Battery; +``` + +This rename also fixes a class of bugs where two nested shapes competed for one name and the loser was silently dropped: for example, `$device->properties->battery->status` did not exist in v3 because the keypad's battery class won the name `DeviceBattery`. In v4 every nested shape has its own class, so fields that were missing on `device.properties.battery`, the climate preset ecobee metadata, and the phone session credential and entrance metadata are now present. + +Property reads are unaffected — only explicit references to the nested class names need updating. + +## Missing required parameters fail locally + +In v3, every endpoint parameter defaulted to `null`, so a call missing a required parameter was sent to the server and failed with `Seam\HttpInvalidInputError` after a round trip. In v4, required parameters have no default, so PHP itself rejects the call with an `ArgumentCountError` (or an `Error` for a missing named argument). Endpoints that require _at least one_ of their parameters throw `InvalidArgumentException` when called with none: + +```php +// v3: raises Seam\HttpInvalidInputError after a round trip to the server +// v4: raises ArgumentCountError locally +$seam->devices->get(); + +// v4: raises InvalidArgumentException("At least one parameter is required for /locks/get") +$seam->locks->get(); +``` + +If you catch `HttpInvalidInputError` around calls that could be sent incomplete, also handle these local errors (or fix the call site). + +Relatedly, call endpoint methods with [named arguments](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments). Parameter order is derived from the API definition and can change as endpoints gain parameters, so a positional call can silently start binding a value to the wrong parameter after an upgrade. Named arguments are stable. + +## Endpoints use preferred HTTP methods + +In v3, every endpoint was called with `POST` and a JSON body. In v4, endpoints use the HTTP method the Seam API prefers: + +- Read endpoints (`get`, `list`, and friends) use `GET`, with parameters sent as URL search params serialized per [Seam's URL search params standard](https://github.com/seamapi/url-search-params-serializer) (with `_strict=true` appended). +- Update endpoints use `PATCH` or `PUT`. +- Delete endpoints use `DELETE`. +- Create and action endpoints (`create`, `lock_door`, etc.) remain `POST`. + +Method signatures, arguments, and return values are unchanged — this only matters if something outside your code observes the HTTP traffic: proxy or firewall rules that allowlist methods, request logging, or test mocks registered against `POST` routes. The `User-Agent` header also changes from `Seam PHP Client ` to `seam-php/`. + +If you call the Seam API with your own HTTP client, the serializer is available as `Seam\UrlSearchParamsSerializer::serialize()`. + +## Error handling refinements + +The exception classes keep their v3 names and stay in the `Seam` namespace, so existing catch blocks keep working: `Seam\HttpApiError`, `Seam\HttpUnauthorizedError`, `Seam\HttpInvalidInputError`, `Seam\ActionAttemptError`, `Seam\ActionAttemptFailedError`, and `Seam\ActionAttemptTimeoutError`. What changes: + +- Every SDK exception now implements the `Seam\SeamException` interface, so they can be caught as a group. +- `getRequestId()` returns `null` instead of `""` when the response carries no request id, and is typed `?string`. +- A response in the 3xx range is no longer treated as successful: redirects are followed by Guzzle, and an unfollowed redirect is an error instead of an empty result. +- An error response not shaped like a Seam error — a gateway returning HTML, for example — raises the underlying Guzzle exception with the real request attached, instead of a fabricated one. +- Malformed JSON in a response raises instead of silently decoding to `null`. + +## Pagination metadata is a typed object + +`firstPage()` and `nextPage()` still return a `[$items, $pagination]` pair, but the metadata is a readonly `Seam\Pagination` object rather than a raw `stdClass`. Property reads (`has_next_page`, `next_page_cursor`, `next_page_url`) are unchanged, so typical pagination loops, `flatten()`, and `flattenToArray()` work as before. What breaks is treating it as a `stdClass`: casting, mutation, or `json_encode` round-trips of the raw envelope. + +The paginator also validates its input now: using it with an endpoint that returns no pagination metadata throws `InvalidArgumentException`, and an `on_response` callback you pass in the params is chained rather than silently replaced. + +## `Seam\Version` replaces `PackageVersion` + +`Seam\Utils\PackageVersion::get()` is renamed to `Seam\Version::get()`, and the version string is also available as the `Seam\Version::VERSION` constant. + +## New in v4 + +These are additions, not breaking changes, but they are worth adopting while you migrate. + +### Personal access tokens and `SeamWithoutWorkspace` + +The client can now authenticate with a personal access token scoped to a workspace, and the new `Seam\SeamWithoutWorkspace` client reaches the endpoints that take no workspace in scope, such as listing your workspaces: + +```php +$seam = Seam\Seam::from_personal_access_token( + "your-personal-access-token", + "your-workspace-id", +); + +$seam = new Seam\SeamWithoutWorkspace( + personal_access_token: "your-personal-access-token", +); +$workspaces = $seam->workspaces->list(); +``` + +Tokens are validated on construction: a client session token, JWT, or publishable key passed as an API key raises `Seam\InvalidTokenError` with a message naming the mistake. + +### Authentication from the environment + +`SEAM_API_KEY` was already read in v3. Version 4 also reads `SEAM_PERSONAL_ACCESS_TOKEN` and `SEAM_WORKSPACE_ID` when no explicit credentials are passed, so `new Seam\Seam()` works under either authentication method. Setting both `SEAM_API_KEY` and `SEAM_PERSONAL_ACCESS_TOKEN` is an error. The endpoint may be set with `SEAM_ENDPOINT` (`SEAM_API_URL` is honored but deprecated). + +### Webhook verification + +`Seam\SeamWebhook` verifies incoming webhooks and returns a typed `Seam\Resources\Event`: + +```php +$webhook = new Seam\SeamWebhook($_ENV["SEAM_WEBHOOK_SECRET"]); +$event = $webhook->verify($payload, $headers); +``` + +### Explicit null with `NullValue::NULL` + +The Seam API distinguishes an omitted parameter from one explicitly set to null: in an update request, an omitted parameter leaves the current value unchanged, while a null parameter unsets it. PHP's `null` keeps the safe v3 meaning of "omit"; to send an explicit null, pass the `Seam\NullValue::NULL` sentinel: + +```php +use Seam\NullValue; + +// Leaves the name unchanged (same as v3). +$seam->devices->update(device_id: $device_id, name: null); + +// Unsets the name (new in v4). +$seam->devices->update(device_id: $device_id, name: NullValue::NULL); +``` + +The other Seam SDKs spell the sentinel `NULL` and its type `Null`, but both names are reserved in PHP, so the type and the value live on one enum. Only parameters the Seam API documents as nullable are typed to accept the sentinel (e.g. `string|NullValue|null`), so passing it anywhere else fails with a `TypeError`. + +## Migration checklist + +1. Upgrade your runtime to PHP 8.2 or later. +2. Update the dependency: `composer require "seamapi/seam:^4"`. +3. Rename `Seam\SeamClient` to `Seam\Seam` and pass constructor options by name; drop `$throw_http_errors`. +4. Replace `$seam->request()` with `$seam->client->request()`, and `$seam->api_key` / `LTS_VERSION` / `$seam->ltsVersion` with your own configuration or `Seam\Seam::LTS_VERSION`. +5. Replace `poll_until_ready()` with `wait_for_action_attempt`, and review the new 10 s/1 s defaults. +6. Review the new retry policy and 30-second timeout; pass `retries: 0` or `timeout: 60.0` to keep v3 behavior. +7. Update type hints on nested resource classes (`Seam\Resources\DeviceProperties` → `Seam\Resources\Device\Properties`) and on `$seam->client` (`ClientInterface`). +8. Switch endpoint calls to named arguments, and handle `ArgumentCountError`/`InvalidArgumentException` where calls might be missing parameters. +9. If proxies, firewalls, or test mocks assume all requests are `POST`, update them for `GET`/`PATCH`/`PUT`/`DELETE`. +10. Rename `Seam\Utils\PackageVersion` to `Seam\Version`. +11. Optionally, adopt personal access tokens, `SeamWebhook`, and `NullValue::NULL`. + +# Migrating from seamapi/seam v2 to v3 + +This guide covers upgrading from `seamapi/seam` v2.x to v3 of the [Seam PHP SDK](https://github.com/seamapi/php). + +Version 3 regenerates the SDK from Seam's API blueprint, the same source of truth used by the SDKs for other languages. It is a much smaller upgrade than v4: client construction, authentication, endpoint methods, error handling, and pagination are all unchanged. The breaking changes are in the generated classes — their namespaces, constructors, and two method signatures. + +## Installation + +```sh +composer require "seamapi/seam:^3" +``` + +## Summary of breaking changes + +| Change | Affects you if... | +| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| [Two `get` methods reorder their parameters](#two-get-methods-reorder-their-parameters) | You call `$seam->events->get()` or `$seam->acs->users->get()` positionally | +| [Resource classes move to `Seam\Resources`](#resource-classes-move-to-seamresources) | You type-hint or import `Seam\Objects\...` classes | +| [Route clients move to `Seam\Routes`](#route-clients-move-to-seamroutes) | You type-hint route clients such as `Seam\DevicesClient` | +| [`ActionAttempt->result` is typed](#actionattempt-result-is-typed) | You read fields off an action attempt's raw `result` | +| [Resource constructors are alphabetical and nullable](#resource-constructors-are-alphabetical-and-nullable) | You construct resource objects yourself, or rely on non-nullable property types | +| [Undocumented resource classes are removed](#undocumented-resource-classes-are-removed) | You reference classes such as `Seam\Objects\PhoneSession` | + +## Two `get` methods reorder their parameters + +The primary resource ID is now the first parameter of every `get` method. Two endpoints change as a result, and a positional call silently sends the old first argument as the wrong parameter: + +- `$seam->events->get()`: `$event_id` is now first (was `$device_id`). +- `$seam->acs->users->get()`: `$acs_user_id` is now first (was `$acs_system_id`). + +```php +// v2 +$event = $seam->events->get($device_id); + +// v3 +$event = $seam->events->get(device_id: $device_id); +``` + +Since parameter order follows the API definition and can change again, prefer named arguments for every endpoint call, not just these two. + +## Resource classes move to `Seam\Resources` + +As of v3.3.0, the generated resource classes live in the `Seam\Resources` namespace instead of `Seam\Objects`. The class names and shapes are unchanged: + +```php +// v2 +use Seam\Objects\Device; + +// v3 +use Seam\Resources\Device; +``` + +## Route clients move to `Seam\Routes` + +As of v3.5.0, the route clients live in the `Seam\Routes` namespace instead of `Seam`: + +```php +// v2 +function listLocks(Seam\LocksClient $locks): array { ... } + +// v3 +function listLocks(Seam\Routes\LocksClient $locks): array { ... } +``` + +`$seam->devices`, `$seam->locks`, and the rest are unaffected — only explicit references to the client class names need updating. `Seam\SeamClient` itself, the exception classes, and `Seam\Paginator` stay where they were. + +## `ActionAttempt->result` is typed + +`ActionAttempt->result` is now a typed `ActionAttemptResult|null` instead of raw decoded JSON. Fields the API spec types — for example `acs_credential_on_encoder` — are still available as properties. Fields the spec defines no type for, such as `result->access_code` and `result->noise_threshold`, are no longer present; fetch the resource from its own endpoint instead. + +## Resource constructors are alphabetical and nullable + +Resource object constructor parameters are now ordered alphabetically and uniformly nullable, and the matching properties are uniformly nullable too. Objects returned by the SDK are unaffected. What breaks: + +- Code constructing resource objects positionally binds values to the wrong parameters. Construct with named arguments or `from_json()`. +- Static analysis that relied on non-nullable property types (for example `$device->device_id` being `string`) now sees `string|null`. + +## Undocumented resource classes are removed + +Classes generated for resources absent from Seam's public API documentation — for example `Seam\Objects\PhoneSession`, `Seam\Objects\Customer`, and the `Seam\Objects\UnmanagedAcs...` nested classes — are no longer generated. The endpoint methods themselves are unchanged. If you referenced one of these classes, the data is still in the API response; the SDK just no longer ships a class for it. + +## Migration checklist + +1. Update the dependency: `composer require "seamapi/seam:^3"`. +2. Switch positional endpoint calls to named arguments — at minimum, fix `$seam->events->get()` and `$seam->acs->users->get()`. +3. Rename `Seam\Objects\...` imports and type hints to `Seam\Resources\...`. +4. Rename route client type hints from `Seam\...Client` to `Seam\Routes\...Client`. +5. Update code reading untyped `ActionAttempt->result` fields. +6. Construct resource objects with named arguments, and treat resource properties as nullable. diff --git a/README.md b/README.md index d4b8bd21..ea720e5f 100644 --- a/README.md +++ b/README.md @@ -510,6 +510,8 @@ HTML, raises the underlying Guzzle exception instead. ## Upgrading from 3.x +See [MIGRATION.md](./MIGRATION.md) for the full guide, including upgrading from 2.x. In brief: + - PHP 8.2 or later is required. PHP 8.1 reached end of life in December 2025. - The client class is `Seam\Seam`, replacing `Seam\SeamClient`. - The constructor takes named options, so `$endpoint` is no longer the second From ce01493185ff93ebb23c41838d3ecefd6095adae Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 16:58:08 +0000 Subject: [PATCH 42/80] docs: Keep the README free of migration content The Upgrading from 3.x section duplicated MIGRATION.md, which covers everything it said. Readers upgrading will find the migration guide. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WRhzZ42JiS7LGUynykjQXP --- README.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/README.md b/README.md index ea720e5f..ceac15aa 100644 --- a/README.md +++ b/README.md @@ -508,26 +508,6 @@ try { A response that is not shaped like a Seam error, such as a gateway returning HTML, raises the underlying Guzzle exception instead. -## Upgrading from 3.x - -See [MIGRATION.md](./MIGRATION.md) for the full guide, including upgrading from 2.x. In brief: - -- PHP 8.2 or later is required. PHP 8.1 reached end of life in December 2025. -- The client class is `Seam\Seam`, replacing `Seam\SeamClient`. -- The constructor takes named options, so `$endpoint` is no longer the second - positional argument, and `$throw_http_errors` is gone: an API error always - raises. -- The exception classes stay in the `Seam\` namespace and now share a - `Seam\SeamException` interface. -- `$seam->action_attempts->poll_until_ready()` is gone. Use - `wait_for_action_attempt`, which also takes a `timeout` and - `polling_interval`. -- `$seam->client` is the Guzzle client, and `$seam->api_key` and the global - `LTS_VERSION` constant are gone. Use `Seam\Seam::LTS_VERSION`. -- A response in the 3xx range is no longer treated as successful. -- Requests time out after 30 seconds and are retried twice. -- Pagination metadata is a `Seam\Pagination` rather than a `stdClass`. - ## Development and Testing ### Quickstart From e1cda4a05bde113935ff92e9ca256122f10b5490 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 21:24:41 +0000 Subject: [PATCH 43/80] fix: Stop overwriting the User-Agent header Every Seam SDK sends seam-sdk-name and seam-sdk-version to identify itself; only this one also rewrote User-Agent, silently discarding a caller's own value and giving no way to override it. Drop the header from sdk_headers() so a caller-set User-Agent (via guzzle_options) or the underlying HTTP library's default passes through untouched. Also drops the migration guide's now-inaccurate note on the removed SEAM_API_URL environment variable, and updates the User-Agent section to describe the corrected behavior. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WRhzZ42JiS7LGUynykjQXP --- MIGRATION.md | 4 ++-- src/Http/ClientFactory.php | 5 ++++- tests/HeadersTest.php | 28 ++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 929c3803..752bf3ae 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -157,7 +157,7 @@ In v3, every endpoint was called with `POST` and a JSON body. In v4, endpoints u - Delete endpoints use `DELETE`. - Create and action endpoints (`create`, `lock_door`, etc.) remain `POST`. -Method signatures, arguments, and return values are unchanged — this only matters if something outside your code observes the HTTP traffic: proxy or firewall rules that allowlist methods, request logging, or test mocks registered against `POST` routes. The `User-Agent` header also changes from `Seam PHP Client ` to `seam-php/`. +Method signatures, arguments, and return values are unchanged — this only matters if something outside your code observes the HTTP traffic: proxy or firewall rules that allowlist methods, request logging, or test mocks registered against `POST` routes. The SDK also no longer sets a `User-Agent` of its own (v3 sent `Seam PHP Client `): it identifies itself with the `seam-sdk-name` and `seam-sdk-version` headers, and a `User-Agent` you set through `guzzle_options` is sent unchanged. If you call the Seam API with your own HTTP client, the serializer is available as `Seam\UrlSearchParamsSerializer::serialize()`. @@ -205,7 +205,7 @@ Tokens are validated on construction: a client session token, JWT, or publishabl ### Authentication from the environment -`SEAM_API_KEY` was already read in v3. Version 4 also reads `SEAM_PERSONAL_ACCESS_TOKEN` and `SEAM_WORKSPACE_ID` when no explicit credentials are passed, so `new Seam\Seam()` works under either authentication method. Setting both `SEAM_API_KEY` and `SEAM_PERSONAL_ACCESS_TOKEN` is an error. The endpoint may be set with `SEAM_ENDPOINT` (`SEAM_API_URL` is honored but deprecated). +`SEAM_API_KEY` was already read in v3. Version 4 also reads `SEAM_PERSONAL_ACCESS_TOKEN` and `SEAM_WORKSPACE_ID` when no explicit credentials are passed, so `new Seam\Seam()` works under either authentication method. Setting both `SEAM_API_KEY` and `SEAM_PERSONAL_ACCESS_TOKEN` is an error. The endpoint may be set with `SEAM_ENDPOINT`. ### Webhook verification diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php index 06ad77c3..9212e941 100644 --- a/src/Http/ClientFactory.php +++ b/src/Http/ClientFactory.php @@ -86,6 +86,10 @@ public static function create( } /** + * The SDK identifies itself with the seam-sdk-* headers and leaves the + * User-Agent alone: a caller's User-Agent is sent unchanged, and without + * one the HTTP library's own default applies. + * * @return array */ private static function sdk_headers(): array @@ -93,7 +97,6 @@ private static function sdk_headers(): array $version = Version::get(); return [ - "User-Agent" => "seam-php/" . $version, "seam-sdk-name" => "seamapi/php", "seam-sdk-version" => $version, "seam-lts-version" => self::LTS_VERSION, diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index ea60646c..a204d8af 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -56,12 +56,36 @@ public function testSendsDefaultHeaders(): void ClientFactory::LTS_VERSION, $request->getHeaderLine("seam-lts-version"), ); - $this->assertSame( - "seam-php/" . Version::get(), + // The SDK does not set a User-Agent of its own, so Guzzle's default + // applies. + $this->assertStringStartsWith( + "GuzzleHttp/", $request->getHeaderLine("User-Agent"), ); } + public function testSendsTheCallerUserAgentUnchanged(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, ["device" => ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: array_merge($recorder->guzzle_options(), [ + "headers" => ["User-Agent" => "acme-app/1.0"], + ]), + ); + + $seam->devices->get("d1"); + + $this->assertSame( + "acme-app/1.0", + $recorder->request()->getHeaderLine("User-Agent"), + ); + } + public function testSendsWorkspaceHeaderWithAPersonalAccessToken(): void { $recorder = new RecordingClient([ From c4fccd26212ad690bea1143f53c67ee94d3e836c Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Fri, 14 Aug 2026 14:26:59 -0700 Subject: [PATCH 44/80] Apply suggestion from @razor-x --- src/Http/ClientFactory.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php index 9212e941..0cef0ede 100644 --- a/src/Http/ClientFactory.php +++ b/src/Http/ClientFactory.php @@ -86,10 +86,6 @@ public static function create( } /** - * The SDK identifies itself with the seam-sdk-* headers and leaves the - * User-Agent alone: a caller's User-Agent is sent unchanged, and without - * one the HTTP library's own default applies. - * * @return array */ private static function sdk_headers(): array From 1945210b948ed836e61b1b681537dcb4f73a07a2 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Fri, 14 Aug 2026 14:27:15 -0700 Subject: [PATCH 45/80] Apply suggestion from @razor-x --- tests/HeadersTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index a204d8af..c8f88a43 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -56,7 +56,6 @@ public function testSendsDefaultHeaders(): void ClientFactory::LTS_VERSION, $request->getHeaderLine("seam-lts-version"), ); - // The SDK does not set a User-Agent of its own, so Guzzle's default // applies. $this->assertStringStartsWith( "GuzzleHttp/", From 21a4aee35a8840ce317de6a7260b0125929c8d8c Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Fri, 14 Aug 2026 14:27:29 -0700 Subject: [PATCH 46/80] Apply suggestion from @razor-x --- tests/HeadersTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index c8f88a43..184c9cd7 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -56,7 +56,6 @@ public function testSendsDefaultHeaders(): void ClientFactory::LTS_VERSION, $request->getHeaderLine("seam-lts-version"), ); - // applies. $this->assertStringStartsWith( "GuzzleHttp/", $request->getHeaderLine("User-Agent"), From 3925ca84dc95f6ee0bb9ad30520494402a3dbbe4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 21:31:31 +0000 Subject: [PATCH 47/80] feat: Remove the LTS version from the SDK Drop Seam\Seam::LTS_VERSION, Seam\SeamWithoutWorkspace::LTS_VERSION, Seam\Http\ClientFactory::LTS_VERSION, and the seam-lts-version request header, with no replacement. The other Seam SDKs (Python) carry no equivalent. Update the codegen template so a future generate keeps the constant out of src/Seam.php, and update the migration guide to describe the removal instead of pointing at Seam::LTS_VERSION. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WRhzZ42JiS7LGUynykjQXP --- MIGRATION.md | 4 ++-- codegen/layouts/seam-client.hbs | 5 ----- src/Http/ClientFactory.php | 3 --- src/Seam.php | 5 ----- src/SeamWithoutWorkspace.php | 2 -- tests/ClientTest.php | 5 ----- tests/HeadersTest.php | 5 ----- 7 files changed, 2 insertions(+), 27 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 752bf3ae..6180f024 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -67,7 +67,7 @@ $res = json_decode( The property is typed `GuzzleHttp\ClientInterface` rather than `GuzzleHttp\Client`, so update any type hints. Two other public members are removed with no replacement on the instance: - `$seam->api_key` is gone. -- `$seam->ltsVersion` and the global `LTS_VERSION` constant are gone. Use the `Seam\Seam::LTS_VERSION` class constant. +- `$seam->ltsVersion`, the global `LTS_VERSION` constant, and the `seam-lts-version` request header are gone, with no replacement. To configure the underlying client, pass `guzzle_options` (merged into the Guzzle client's config), or pass a preconfigured client via the `client` option. A preconfigured client carries its own endpoint and authorization, so combining it with `api_key`, `endpoint`, or any other option that would configure one raises `Seam\InvalidOptionsError` instead of being silently ignored: @@ -237,7 +237,7 @@ The other Seam SDKs spell the sentinel `NULL` and its type `Null`, but both name 1. Upgrade your runtime to PHP 8.2 or later. 2. Update the dependency: `composer require "seamapi/seam:^4"`. 3. Rename `Seam\SeamClient` to `Seam\Seam` and pass constructor options by name; drop `$throw_http_errors`. -4. Replace `$seam->request()` with `$seam->client->request()`, and `$seam->api_key` / `LTS_VERSION` / `$seam->ltsVersion` with your own configuration or `Seam\Seam::LTS_VERSION`. +4. Replace `$seam->request()` with `$seam->client->request()`. Remove any use of `$seam->api_key`, `LTS_VERSION`, `$seam->ltsVersion`, or the `seam-lts-version` header — there is no replacement. 5. Replace `poll_until_ready()` with `wait_for_action_attempt`, and review the new 10 s/1 s defaults. 6. Review the new retry policy and 30-second timeout; pass `retries: 0` or `timeout: 60.0` to keep v3 behavior. 7. Update type hints on nested resource classes (`Seam\Resources\DeviceProperties` → `Seam\Resources\Device\Properties`) and on `$seam->client` (`ClientInterface`). diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index e3813c6d..fa1faede 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -27,11 +27,6 @@ class Seam public {{clientName}}Client ${{namespace}}; {{/each}} - /** - * The long term support version of the Seam API this SDK targets. - */ - public const LTS_VERSION = ClientFactory::LTS_VERSION; - /** * The Guzzle client this instance makes its requests with. * diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php index 0cef0ede..9ba1451f 100644 --- a/src/Http/ClientFactory.php +++ b/src/Http/ClientFactory.php @@ -11,8 +11,6 @@ */ final class ClientFactory { - public const LTS_VERSION = "1.0.0"; - /** * How many times a failed request is retried unless the caller says * otherwise. @@ -95,7 +93,6 @@ private static function sdk_headers(): array return [ "seam-sdk-name" => "seamapi/php", "seam-sdk-version" => $version, - "seam-lts-version" => self::LTS_VERSION, ]; } } diff --git a/src/Seam.php b/src/Seam.php index 43a5ba4c..4ad10234 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -61,11 +61,6 @@ class Seam public WebhooksClient $webhooks; public WorkspacesClient $workspaces; - /** - * The long term support version of the Seam API this SDK targets. - */ - public const LTS_VERSION = ClientFactory::LTS_VERSION; - /** * The Guzzle client this instance makes its requests with. * diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index bf8154e6..69e3cc7b 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -18,8 +18,6 @@ */ class SeamWithoutWorkspace { - public const LTS_VERSION = ClientFactory::LTS_VERSION; - /** * The Guzzle client this instance makes its requests with. */ diff --git a/tests/ClientTest.php b/tests/ClientTest.php index b289232a..b565c9c1 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -240,9 +240,4 @@ public function testWaitForActionAttemptDefaultCanBeDisabled(): void $this->assertFalse($seam->defaults["wait_for_action_attempt"]); } - - public function testLtsVersionIsExposed(): void - { - $this->assertSame("1.0.0", Seam::LTS_VERSION); - } } diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index 184c9cd7..35eb3632 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -5,7 +5,6 @@ namespace Tests; use PHPUnit\Framework\TestCase; -use Seam\Http\ClientFactory; use Seam\Seam; use Seam\Version; use Tests\Support\RecordingClient; @@ -52,10 +51,6 @@ public function testSendsDefaultHeaders(): void Version::get(), $request->getHeaderLine("seam-sdk-version"), ); - $this->assertSame( - ClientFactory::LTS_VERSION, - $request->getHeaderLine("seam-lts-version"), - ); $this->assertStringStartsWith( "GuzzleHttp/", $request->getHeaderLine("User-Agent"), From bf4b5c5941622de0665c33cacae259158ee13a2d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 22:02:03 +0000 Subject: [PATCH 48/80] fix: Restore the missing semicolon in get_endpoint_from_env Removing the SEAM_API_URL env var left the return statement without its semicolon, so src/Options.php did not parse. Every job failed on it: Lint at the syntax pass, Test and Install with a ParseError as soon as the class autoloaded, and Format and Generate when prettier could not parse the file. Return the lookup directly rather than by way of a variable that is now only used once. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WRhzZ42JiS7LGUynykjQXP --- src/Options.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Options.php b/src/Options.php index 265ced95..35c75743 100644 --- a/src/Options.php +++ b/src/Options.php @@ -18,8 +18,7 @@ public static function get_endpoint(?string $endpoint = null): string public static function get_endpoint_from_env(): ?string { - $seam_endpoint = self::get_env("SEAM_ENDPOINT"); - return $seam_endpoint + return self::get_env("SEAM_ENDPOINT"); } public static function is_seam_options_with_api_key( From 068c39d49d52a194d41f4d073f7370b465fd127d Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Fri, 14 Aug 2026 22:07:06 +0000 Subject: [PATCH 49/80] 4.0.0-beta.6 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04b95968..89afdb4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.5", + "version": "4.0.0-beta.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.5", + "version": "4.0.0-beta.6", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 3ba660bb..6f91170b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.5", + "version": "4.0.0-beta.6", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index 508db381..f3ab3c1e 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.5"; + public const VERSION = "4.0.0-beta.6"; public static function get(): string { From bd1e08897476292d3189dae275223c6d011fcae0 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Mon, 17 Aug 2026 10:24:28 -0700 Subject: [PATCH 50/80] Remove Seam LTS version badge from README Removed the Seam LTS version badge from the README. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ceac15aa..c6f88f99 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Seam PHP SDK [![Packagist](https://img.shields.io/packagist/v/seamapi/seam.svg)](https://packagist.org/packages/seamapi/seam) -[![Seam LTS Version](https://img.shields.io/badge/Seam_LTS-1.0.0-blue)](https://docs.seam.co/lts) [![GitHub Actions](https://github.com/seamapi/php/actions/workflows/check.yml/badge.svg)](https://github.com/seamapi/php/actions/workflows/check.yml) PHP SDK for the Seam API. From 45da3777d9e56a88d71e94f268cd87b74d6c7cd3 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Tue, 18 Aug 2026 16:27:40 -0700 Subject: [PATCH 51/80] Support Blueprint boolean constraints (#476) Support Blueprint boolean constraints --- codegen/lib/map-php-type.ts | 6 +- codegen/lib/merge-properties.ts | 13 ++++ package-lock.json | 8 +-- package.json | 2 +- src/Resources/AccessCode.php | 4 +- src/Resources/AcsAccessGroup.php | 2 +- src/Resources/AcsCredential.php | 2 +- src/Resources/AcsUser.php | 2 +- src/Resources/Device.php | 6 +- src/Resources/UnmanagedAccessCode.php | 8 +-- src/Resources/UnmanagedDevice.php | 2 +- src/Routes/AccessGrantsUnmanagedClient.php | 4 +- src/Routes/DevicesUnmanagedClient.php | 4 +- src/Routes/UserIdentitiesUnmanagedClient.php | 4 +- tests/LiteralBooleanTypesTest.php | 72 ++++++++++++++++++++ 15 files changed, 113 insertions(+), 26 deletions(-) create mode 100644 tests/LiteralBooleanTypesTest.php diff --git a/codegen/lib/map-php-type.ts b/codegen/lib/map-php-type.ts index 7ca5e2c6..7afc07a4 100644 --- a/codegen/lib/map-php-type.ts +++ b/codegen/lib/map-php-type.ts @@ -13,8 +13,10 @@ export const getPhpType = (schema: Parameter | Property): string => { case 'number': return 'float' - case 'boolean': - return 'bool' + case 'boolean': { + const values = [...new Set(schema.values)] + return values.length === 1 ? String(values[0]) : 'bool' + } case 'array': return 'array' diff --git a/codegen/lib/merge-properties.ts b/codegen/lib/merge-properties.ts index 70c7bd8e..f5b13bbc 100644 --- a/codegen/lib/merge-properties.ts +++ b/codegen/lib/merge-properties.ts @@ -57,6 +57,19 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { ) } + if (first.format === 'boolean') { + const booleans = occurrences as Array< + Extract + > + const values = booleans.some(({ values }) => values == null) + ? undefined + : [...new Set(booleans.flatMap(({ values }) => values ?? []))] + const merged = { ...first, ...docs } + if (values == null) delete merged.values + else merged.values = values + return merged + } + if (first.format === 'object') { return { ...first, diff --git a/package-lock.json b/package-lock.json index 89afdb4a..84ed5b5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.5.1", + "@seamapi/blueprint": "^1.6.0", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1001.0", @@ -806,9 +806,9 @@ "license": "MIT" }, "node_modules/@seamapi/blueprint": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.5.1.tgz", - "integrity": "sha512-rXHMNDGmaE7OK+tg3e64DnpMHQypN4BuSS+PkVqGUoYc9f+xBMONqPscsHAGi9iU/iyZOJZKEOKOXvMmWaLoLA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.6.0.tgz", + "integrity": "sha512-+lIriSgog0E0pUjXz/tdRZiVECiSiBcnZ3uU4JRN45RTLs2BQVbR+Ris6/mXwBV1wkpMetdVD3CP1BB1agf6Lg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 6f91170b..2067c0bf 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.5.1", + "@seamapi/blueprint": "^1.6.0", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1001.0", diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index ad39cb12..b003257f 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -114,7 +114,7 @@ public function __construct( /** * Indicates whether Seam manages the access code. */ - public bool|null $is_managed, + public true|null $is_managed, /** * Indicates whether the access code is intended for use in offline scenarios. If `true`, this code can be created on a device without a network connection. */ @@ -273,7 +273,7 @@ public function __construct( /** * Indicates that this is an access code error. */ - public bool|null $is_access_code_error, + public true|null $is_access_code_error, /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index 387acf79..9507728e 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -98,7 +98,7 @@ public function __construct( /** * Indicates whether Seam manages the access group. */ - public bool|null $is_managed, + public true|null $is_managed, /** * Name of the access group. */ diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index 51f87ee3..9ae96219 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -151,7 +151,7 @@ public function __construct( /** * Indicates whether Seam manages the credential. */ - public bool|null $is_managed, + public true|null $is_managed, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index 880d5331..1f0559e4 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -122,7 +122,7 @@ public function __construct( /** * Indicates whether Seam manages the access system user. */ - public bool|null $is_managed, + public true|null $is_managed, /** * Indicates whether the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is currently [suspended](https://docs.seam.co/low-level-apis/access-systems/user-management/suspending-and-unsuspending-users). */ diff --git a/src/Resources/Device.php b/src/Resources/Device.php index 4e960104..d813da04 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -202,7 +202,7 @@ public function __construct( /** * Indicates whether Seam manages the device. See also [Managed and Unmanaged Devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). */ - public bool|null $is_managed, + public true|null $is_managed, /** * Location information for the device. */ @@ -3177,7 +3177,7 @@ public function __construct( /** * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. */ - public bool|null $matching_start_end_time = null, + public true|null $matching_start_end_time = null, /** * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. */ @@ -3241,7 +3241,7 @@ public function __construct( /** * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. */ - public bool|null $matching_start_end_time = null, + public true|null $matching_start_end_time = null, /** * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. */ diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 87d2566b..d0e2ce0b 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -62,11 +62,11 @@ public function __construct( /** * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. */ - public bool|null $cannot_be_managed = null, + public true|null $cannot_be_managed = null, /** * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. */ - public bool|null $cannot_delete_unmanaged_access_code = null, + public true|null $cannot_delete_unmanaged_access_code = null, /** * Code used for access. Typically, a numeric or alphanumeric string. */ @@ -94,7 +94,7 @@ public function __construct( /** * Indicates that Seam does not manage the access code. */ - public bool|null $is_managed, + public false|null $is_managed, /** * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). */ @@ -229,7 +229,7 @@ public function __construct( /** * Indicates that this is an access code error. */ - public bool|null $is_access_code_error, + public true|null $is_access_code_error, /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index be566c4a..9ad5b995 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -179,7 +179,7 @@ public function __construct( /** * Indicates that Seam does not manage the device. */ - public bool|null $is_managed, + public false|null $is_managed, /** * Location information for the device. */ diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index e872978d..d7b716c7 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -112,13 +112,13 @@ public function list( * When converting an unmanaged access grant to managed, all associated access methods will also be converted to managed. * * @param string $access_grant_id ID of the unmanaged Access Grant to update. - * @param bool $is_managed Must be set to true to convert the unmanaged access grant to managed. + * @param true $is_managed Must be set to true to convert the unmanaged access grant to managed. * @param string $access_grant_key Unique key for the access grant. If not provided, the existing key will be preserved. * @return void OK */ public function update( string $access_grant_id, - bool $is_managed, + true $is_managed, ?string $access_grant_key = null, ): void { $request_payload = []; diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 436f55d8..bd89a73e 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -160,13 +160,13 @@ public function list( * * @param string $device_id ID of the unmanaged device that you want to update. * @param mixed $custom_metadata Custom metadata that you want to associate with the device. Supports up to 50 JSON key:value pairs. - * @param bool $is_managed Indicates whether the device is managed. Set this parameter to `true` to convert an unmanaged device to managed. + * @param true $is_managed Indicates whether the device is managed. Set this parameter to `true` to convert an unmanaged device to managed. * @return void OK */ public function update( string $device_id, mixed $custom_metadata = null, - ?bool $is_managed = null, + ?true $is_managed = null, ): void { $request_payload = []; diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 58540237..c794d2cb 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -99,13 +99,13 @@ public function list( * * This endpoint can only be used to convert unmanaged user identities to managed ones by setting `is_managed` to `true`. It cannot be used to convert managed user identities back to unmanaged. * - * @param bool $is_managed Must be set to true to convert the unmanaged user identity to managed. + * @param true $is_managed Must be set to true to convert the unmanaged user identity to managed. * @param string $user_identity_id ID of the unmanaged user identity that you want to update. * @param string $user_identity_key Unique key for the user identity. If not provided, the existing key will be preserved. * @return void OK */ public function update( - bool $is_managed, + true $is_managed, string $user_identity_id, ?string $user_identity_key = null, ): void { diff --git a/tests/LiteralBooleanTypesTest.php b/tests/LiteralBooleanTypesTest.php new file mode 100644 index 00000000..d1e74e9f --- /dev/null +++ b/tests/LiteralBooleanTypesTest.php @@ -0,0 +1,72 @@ +assertSame( + "?true", + (string) (new ReflectionProperty( + AccessCode::class, + "is_managed", + ))->getType(), + ); + $this->assertSame( + "?false", + (string) (new ReflectionProperty( + UnmanagedAccessCode::class, + "is_managed", + ))->getType(), + ); + + foreach ( + [AccessCode\Errors::class, UnmanagedAccessCode\Errors::class] + as $class + ) { + $this->assertSame( + "?true", + (string) (new ReflectionProperty( + $class, + "is_access_code_error", + ))->getType(), + ); + $this->assertSame( + "?bool", + (string) (new ReflectionProperty( + $class, + "is_connected_account_error", + ))->getType(), + ); + } + + $this->assertSame( + "?bool", + (string) (new ReflectionProperty( + ActionAttempt\Result::class, + "is_managed", + ))->getType(), + ); + } + + public function testUnmanagedAccessCodeRejectsManagedLiteral(): void + { + $code = (new ReflectionClass( + UnmanagedAccessCode::class, + ))->newInstanceWithoutConstructor(); + + $this->expectException(TypeError::class); + $code->is_managed = true; + } +} From a894d79d197c497a89c08502a0e5478dddded5a9 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Tue, 18 Aug 2026 17:08:43 -0700 Subject: [PATCH 52/80] feat: Support Blueprint record value types (#477) --- codegen/lib/class-model.ts | 1 + codegen/lib/handlebars-helpers.ts | 16 +++++- codegen/lib/layouts/resource.ts | 11 ++++ codegen/lib/layouts/route.ts | 21 ++++++-- codegen/lib/map-php-type.ts | 38 ++++++++++++++ codegen/lib/merge-properties.ts | 19 +++++++ codegen/lib/resource-model.ts | 16 +++++- codegen/lib/routes.ts | 3 +- package-lock.json | 8 +-- package.json | 2 +- src/Resources/ActionAttempt.php | 10 +++- src/Resources/ConnectWebview.php | 4 +- src/Resources/ConnectedAccount.php | 4 +- src/Resources/Device.php | 4 +- src/Resources/Event.php | 26 +++++++--- src/Resources/Phone.php | 4 +- src/Resources/UnmanagedDevice.php | 4 +- src/Routes/ConnectWebviewsClient.php | 8 +-- src/Routes/ConnectedAccountsClient.php | 8 +-- src/Routes/DevicesClient.php | 8 +-- src/Routes/DevicesUnmanagedClient.php | 4 +- tests/RecordTypesTest.php | 70 ++++++++++++++++++++++++++ 22 files changed, 247 insertions(+), 42 deletions(-) create mode 100644 tests/RecordTypesTest.php diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index ebb8e5aa..187754c6 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -4,6 +4,7 @@ export interface PhpClientMethodParameter { name: string type: string + phpDocType: string description: string isOptional: boolean isNullable: boolean diff --git a/codegen/lib/handlebars-helpers.ts b/codegen/lib/handlebars-helpers.ts index 33da9362..316620f6 100644 --- a/codegen/lib/handlebars-helpers.ts +++ b/codegen/lib/handlebars-helpers.ts @@ -4,6 +4,7 @@ export interface DeprecatedPhpDocContext { description: string isDeprecated: boolean deprecationMessage: string + phpDocType?: string } export interface MethodPhpDocContext extends DeprecatedPhpDocContext { @@ -24,10 +25,21 @@ export const resourcePhpDoc = (context: DeprecatedPhpDocContext): string => createPhpDoc(context.description, deprecatedTag(context), ' ') export const hasPhpDoc = (context: DeprecatedPhpDocContext): boolean => - context.description.trim() !== '' || context.isDeprecated + context.description.trim() !== '' || + context.isDeprecated || + (context.phpDocType != null && context.phpDocType !== '') export const propertyPhpDoc = (context: DeprecatedPhpDocContext): string => - createPhpDoc(context.description, deprecatedTag(context), ' ') + createPhpDoc( + context.description, + [ + ...(context.phpDocType == null || context.phpDocType === '' + ? [] + : [`@var ${context.phpDocType}`]), + ...deprecatedTag(context), + ], + ' ', + ) export const methodPhpDoc = (context: MethodPhpDocContext): string => createPhpDoc( diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index 72b424d1..f192cdfa 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -24,6 +24,7 @@ export interface ClassLayoutContext { export interface ConstructorParamLayoutContext { declaration: string + phpDocType: string description: string isDeprecated: boolean deprecationMessage: string @@ -48,6 +49,9 @@ const generateFromJsonProp = (property: ResourceClassProperty): string => { case 'listReference': return `${name}: array_map(fn ($${name[0]}) => ${property.referenceName}::from_json($${name[0]}), $json->${name} ?? []),` + case 'record': + return `${name}: $json->${name} ?? null,` + case 'value': return `${name}: $json->${name} ?? null,` } @@ -57,6 +61,7 @@ const generateConstructorParam = ( property: ResourceClassProperty, ): ConstructorParamLayoutContext => { let declaration: string + let phpDocType = '' // Resource decoding remains tolerant of sparse response envelopes. Optional // properties can also be omitted when constructing a resource directly; // nullable properties retain the same null-safe representation. @@ -71,6 +76,11 @@ const generateConstructorParam = ( declaration = `public array${property.isOptional ? '|null' : ''} $${property.name}${defaultValue},` break + case 'record': + phpDocType = `${property.phpDocType}|null` + declaration = `public ${property.phpType}|null $${property.name}${defaultValue},` + break + case 'value': { const { phpType } = property const nullSuffix = phpType === 'mixed' ? '' : '|null' @@ -81,6 +91,7 @@ const generateConstructorParam = ( return { declaration, + phpDocType, description: property.description, isDeprecated: property.isDeprecated, deprecationMessage: property.deprecationMessage, diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 868640ab..8407fc4a 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -25,6 +25,7 @@ export interface MethodLayoutContext { parameters: Array<{ name: string type: string + phpDocType: string description: string required: boolean isOptional: boolean @@ -87,7 +88,8 @@ const getParameterPhpType = (parameter: { const { type, isOptional, isNullable } = parameter if (type === 'mixed') return type if (isNullable) return `${type}|NullValue${isOptional ? '|null' : ''}` - return `${isOptional ? '?' : ''}${type}` + if (!isOptional) return type + return type.includes('|') ? `${type}|null` : `?${type}` } const getMethodLayoutContext = ( @@ -122,16 +124,25 @@ const getMethodLayoutContext = ( .concat(usesOnResponse ? ['?callable $on_response = null'] : []) .join(', ') - const documentedEndpointParameters = sortedParameters.map( - ({ name, type, description, isOptional, isNullable }) => ({ + const endpointParameters = sortedParameters.map( + ({ name, type, phpDocType, description, isOptional, isNullable }) => ({ name, - type: isNullable && type !== 'mixed' ? `${type}|NullValue` : type, + type, + phpDocType, description, required: !isOptional, isOptional, isNullable, }), ) + const documentedEndpointParameters = endpointParameters.map( + ({ name, type, phpDocType, description, isNullable }) => ({ + name, + type: + isNullable && type !== 'mixed' ? `${phpDocType}|NullValue` : phpDocType, + description, + }), + ) return { methodName, @@ -142,7 +153,7 @@ const getMethodLayoutContext = ( isDeprecated: method.isDeprecated, deprecationMessage: method.deprecationMessage, // The request payload is built from the endpoint parameters alone. - parameters: documentedEndpointParameters, + parameters: endpointParameters, // The SDK level parameters are documented alongside them so editors // surface all of them, but they never reach the payload. documentedParameters: [ diff --git a/codegen/lib/map-php-type.ts b/codegen/lib/map-php-type.ts index 7afc07a4..0ea504a7 100644 --- a/codegen/lib/map-php-type.ts +++ b/codegen/lib/map-php-type.ts @@ -3,7 +3,14 @@ import type { Parameter, Property } from '@seamapi/blueprint' +type RecordValueType = NonNullable< + Extract['valueTypes'] +>[number] + export const getPhpType = (schema: Parameter | Property): string => { + if (schema.format === 'record' && !('resourceType' in schema)) { + return 'array|\\stdClass' + } if (schema.format === 'number' && schema.isInt) return 'int' switch (schema.jsonType) { @@ -25,3 +32,34 @@ export const getPhpType = (schema: Parameter | Property): string => { return 'mixed' } } + +export const getPhpDocType = (schema: Parameter | Property): string => { + if (schema.format !== 'record' || 'resourceType' in schema) { + return getPhpType(schema) + } + + const types = + ('valueTypes' in schema ? schema.valueTypes : undefined)?.map( + getRecordValuePhpType, + ) ?? [] + return `array|\\stdClass` +} + +const getRecordValuePhpType = (type: RecordValueType): string => { + switch (type) { + case 'string': + return 'string' + case 'number': + return 'float' + case 'integer': + return 'int' + case 'boolean': + return 'bool' + case 'object': + return 'array|\\stdClass' + case 'array': + return 'list' + default: + throw new Error(`Unsupported JSON Schema type: ${type}`) + } +} diff --git a/codegen/lib/merge-properties.ts b/codegen/lib/merge-properties.ts index f5b13bbc..26caa63f 100644 --- a/codegen/lib/merge-properties.ts +++ b/codegen/lib/merge-properties.ts @@ -70,6 +70,25 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { return merged } + if (first.format === 'record' && 'valueTypes' in first) { + const valueTypes = occurrences.some( + (occurrence) => + !('valueTypes' in occurrence) || occurrence.valueTypes == null, + ) + ? undefined + : [ + ...new Set( + occurrences.flatMap((occurrence) => + 'valueTypes' in occurrence ? (occurrence.valueTypes ?? []) : [], + ), + ), + ] + const merged = { ...first, ...docs } + if (valueTypes == null) delete merged.valueTypes + else merged.valueTypes = valueTypes + return merged + } + if (first.format === 'object') { return { ...first, diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index c09688d7..5fb92983 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -15,11 +15,16 @@ import type { Blueprint, Property, Resource } from '@seamapi/blueprint' import { pascalCase } from 'change-case' -import { getPhpType } from './map-php-type.js' +import { getPhpDocType, getPhpType } from './map-php-type.js' import { mergeProperties } from './merge-properties.js' export type ResourceClassProperty = | ({ kind: 'value'; phpType: string } & ResourceClassPropertyMetadata) + | ({ + kind: 'record' + phpType: string + phpDocType: string + } & ResourceClassPropertyMetadata) | ({ kind: 'objectReference' referenceName: string @@ -272,7 +277,14 @@ const buildClass = ( ) if (nestedProperties == null) { - return { ...metadata, kind: 'value', phpType: getPhpType(property) } + return property.format === 'record' && !('resourceType' in property) + ? { + ...metadata, + kind: 'record', + phpType: getPhpType(property), + phpDocType: getPhpDocType(property), + } + : { ...metadata, kind: 'value', phpType: getPhpType(property) } } const nestedClassName = pascalCase(property.name) diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index b75aa155..3c13cc7d 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -12,7 +12,7 @@ import type { PhpClient, PhpClientMethod } from './class-model.js' import { setResourceLayoutContext } from './layouts/resource.js' import { setRouteLayoutContext } from './layouts/route.js' import { setSeamClientLayoutContext } from './layouts/seam-client.js' -import { getPhpType } from './map-php-type.js' +import { getPhpDocType, getPhpType } from './map-php-type.js' import { createResourceModel } from './resource-model.js' interface Metadata { @@ -135,6 +135,7 @@ const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { parameters: endpoint.request.parameters.map((parameter) => ({ name: parameter.name, type: getPhpType(parameter), + phpDocType: getPhpDocType(parameter), description: parameter.description, isOptional: !parameter.isRequired, isNullable: parameter.isNullable, diff --git a/package-lock.json b/package-lock.json index 84ed5b5f..e0560262 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.6.0", + "@seamapi/blueprint": "^1.7.0", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1001.0", @@ -806,9 +806,9 @@ "license": "MIT" }, "node_modules/@seamapi/blueprint": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.6.0.tgz", - "integrity": "sha512-+lIriSgog0E0pUjXz/tdRZiVECiSiBcnZ3uU4JRN45RTLs2BQVbR+Ris6/mXwBV1wkpMetdVD3CP1BB1agf6Lg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.7.0.tgz", + "integrity": "sha512-on9pREQKyFgIaS99KLcck5K6AqLEO8Iy5h1heuJ2sEC2A7wXk5s/TL8iDOB3+4eeaAwuHRPgze9/Omo84KjJkw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 2067c0bf..33c2c1b4 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ }, "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.6.0", + "@seamapi/blueprint": "^1.7.0", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1001.0", diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 009c6529..73dd218a 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -159,7 +159,10 @@ public static function from_json(mixed $json): Result|null } public function __construct( - public mixed $access_code, + /** + * @var array|\stdClass|null + */ + public array|\stdClass|null $access_code, /** * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ @@ -278,7 +281,10 @@ public function __construct( * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ public string|null $mode, - public mixed $noise_threshold, + /** + * @var array|\stdClass|null + */ + public array|\stdClass|null $noise_threshold, /** * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ diff --git a/src/Resources/ConnectWebview.php b/src/Resources/ConnectWebview.php index c857baa0..f4cb12d0 100644 --- a/src/Resources/ConnectWebview.php +++ b/src/Resources/ConnectWebview.php @@ -82,8 +82,10 @@ public function __construct( public string|null $created_at, /** * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + * + * @var array|\stdClass|null */ - public mixed $custom_metadata, + public array|\stdClass|null $custom_metadata, /** * URL to which the Connect Webview should redirect when an unexpected error occurs. */ diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index 0bd1b645..a7f5085c 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -72,8 +72,10 @@ public function __construct( public string|null $created_at = null, /** * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + * + * @var array|\stdClass|null */ - public mixed $custom_metadata, + public array|\stdClass|null $custom_metadata, /** * Your unique key for the customer associated with this connected account. */ diff --git a/src/Resources/Device.php b/src/Resources/Device.php index d813da04..93bd4b51 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -173,8 +173,10 @@ public function __construct( public string|null $created_at, /** * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + * + * @var array|\stdClass|null */ - public mixed $custom_metadata, + public array|\stdClass|null $custom_metadata, /** * ID of the device. */ diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 641212cd..d18aa79e 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -246,7 +246,10 @@ public function __construct( public string|null $climate_preset_key, public string|null $code, public string|null $connect_webview_id = null, - public mixed $connected_account_custom_metadata = null, + /** + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, /** * Errors associated with the connected account. */ @@ -281,7 +284,10 @@ public function __construct( * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. */ public float|null $desired_temperature_fahrenheit = null, - public mixed $device_custom_metadata = null, + /** + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, /** * Errors associated with the device. */ @@ -356,8 +362,10 @@ public function __construct( public string|null $method, /** * Metadata from Minut. + * + * @var array|\stdClass|null */ - public mixed $minut_metadata = null, + public array|\stdClass|null $minut_metadata = null, /** * 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. */ @@ -384,8 +392,10 @@ public function __construct( public string|null $noise_threshold_name = null, /** * Metadata from Noiseaware. + * + * @var array|\stdClass|null */ - public mixed $noiseaware_metadata = null, + public array|\stdClass|null $noiseaware_metadata = null, /** * Date and time at which the event occurred. */ @@ -827,16 +837,20 @@ public static function from_json(mixed $json): RequestedMutations|null public function __construct( /** * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + * + * @var array|\stdClass|null */ - public mixed $from = null, + public array|\stdClass|null $from = null, /** * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. */ public string|null $mutation_code, /** * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + * + * @var array|\stdClass|null */ - public mixed $to = null, + public array|\stdClass|null $to = null, ) {} } diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index 3cb31a93..e73d8f3e 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -40,8 +40,10 @@ public function __construct( public string|null $created_at, /** * Optional [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) for the phone. + * + * @var array|\stdClass|null */ - public mixed $custom_metadata, + public array|\stdClass|null $custom_metadata, /** * ID of the phone. */ diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 9ad5b995..7a6a2ad3 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -162,8 +162,10 @@ public function __construct( public string|null $created_at, /** * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + * + * @var array|\stdClass|null */ - public mixed $custom_metadata, + public array|\stdClass|null $custom_metadata, /** * ID of the device. */ diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 25caf8a0..b34ff135 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -37,7 +37,7 @@ public function __construct(ClientInterface $client, array $defaults) * @param array $accepted_capabilities List of accepted device capabilities that restrict the types of devices that can be connected through the Connect Webview. If not provided, defaults will be determined based on the accepted providers. * @param array $accepted_providers Accepted device provider keys as an alternative to `provider_category`. Use this parameter to specify accepted providers explicitly. See [Customize the Brands to Display in Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). To list all provider keys, use [`/devices/list_device_providers`](https://docs.seam.co/api/devices/list_device_providers) with no filters. * @param bool $automatically_manage_new_devices Indicates whether newly-added devices should appear as [managed devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). See also: [Customize the Behavior Settings of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-behavior-settings-of-your-connect-webviews). - * @param mixed $custom_metadata Custom metadata that you want to associate with the Connect Webview. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview) enables you to store custom information, like customer details or internal IDs from your application. The custom metadata is then transferred to any [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) that were connected using the Connect Webview, making it easy to find and filter these resources in your [workspace](https://docs.seam.co/core-concepts/workspaces). You can also [filter Connect Webviews by custom metadata](https://docs.seam.co/core-concepts/connect-webviews/filtering-connect-webviews-by-custom-metadata). + * @param array|\stdClass $custom_metadata Custom metadata that you want to associate with the Connect Webview. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview) enables you to store custom information, like customer details or internal IDs from your application. The custom metadata is then transferred to any [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) that were connected using the Connect Webview, making it easy to find and filter these resources in your [workspace](https://docs.seam.co/core-concepts/workspaces). You can also [filter Connect Webviews by custom metadata](https://docs.seam.co/core-concepts/connect-webviews/filtering-connect-webviews-by-custom-metadata). * @param string $custom_redirect_failure_url Alternative URL that you want to redirect the user to on an error. If you do not set this parameter, the Connect Webview falls back to the `custom_redirect_url`. * @param string $custom_redirect_url URL that you want to redirect the user to after the provider login is complete. * @param string $customer_key Associate the Connect Webview, the connected account, and all resources under the connected account with a customer. If the connected account already exists, it will be associated with the customer. If the connected account already exists, but is already associated with a customer, the Connect Webview will show an error. @@ -50,7 +50,7 @@ public function create( ?array $accepted_capabilities = null, ?array $accepted_providers = null, ?bool $automatically_manage_new_devices = null, - mixed $custom_metadata = null, + array|\stdClass|null $custom_metadata = null, ?string $custom_redirect_failure_url = null, ?string $custom_redirect_url = null, ?string $customer_key = null, @@ -151,7 +151,7 @@ public function get(string $connect_webview_id): ConnectWebview /** * Returns a list of all [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews). * - * @param mixed $custom_metadata_has Custom metadata pairs by which you want to [filter Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/filtering-connect-webviews-by-custom-metadata). Returns Connect Webviews with `custom_metadata` that contains all of the provided key:value pairs. + * @param array|\stdClass $custom_metadata_has Custom metadata pairs by which you want to [filter Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/filtering-connect-webviews-by-custom-metadata). Returns Connect Webviews with `custom_metadata` that contains all of the provided key:value pairs. * @param string $customer_key Customer key for which you want to list connect webviews. * @param float $limit Maximum number of records to return per page. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. @@ -161,7 +161,7 @@ public function get(string $connect_webview_id): ConnectWebview * @return array OK */ public function list( - mixed $custom_metadata_has = null, + array|\stdClass|null $custom_metadata_has = null, ?string $customer_key = null, ?float $limit = null, string|NullValue|null $page_cursor = null, diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index c706e9fa..ce997b4f 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -87,7 +87,7 @@ public function get( /** * Returns a list of all [connected accounts](https://docs.seam.co/core-concepts/connected-accounts). * - * @param mixed $custom_metadata_has Custom metadata pairs by which you want to filter connected accounts. Returns connected accounts with `custom_metadata` that contains all of the provided key:value pairs. + * @param array|\stdClass $custom_metadata_has Custom metadata pairs by which you want to filter connected accounts. Returns connected accounts with `custom_metadata` that contains all of the provided key:value pairs. * @param string $customer_key Customer key by which you want to filter connected accounts. * @param int $limit Maximum number of records to return per page. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. @@ -98,7 +98,7 @@ public function get( * @return array OK */ public function list( - mixed $custom_metadata_has = null, + array|\stdClass|null $custom_metadata_has = null, ?string $customer_key = null, ?int $limit = null, string|NullValue|null $page_cursor = null, @@ -170,7 +170,7 @@ public function sync(string $connected_account_id): void * @param string $connected_account_id ID of the connected account that you want to update. * @param array $accepted_capabilities List of accepted device capabilities that restrict the types of devices that can be connected through this connected account. Valid values are `lock`, `thermostat`, `noise_sensor`, and `access_control`. * @param bool $automatically_manage_new_devices Indicates whether newly-added devices should appear as [managed devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). - * @param mixed $custom_metadata Custom metadata that you want to associate with the connected account. Entirely replaces the existing custom metadata object. If a new Connect Webview contains custom metadata and is used to reconnect a connected account, the custom metadata from the Connect Webview will entirely replace the entire custom metadata object on the connected account. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account) enables you to store custom information, like customer details or internal IDs from your application. Then, you can [filter connected accounts by the desired metadata](https://docs.seam.co/core-concepts/connected-accounts/filtering-connected-accounts-by-custom-metadata). + * @param array|\stdClass $custom_metadata Custom metadata that you want to associate with the connected account. Entirely replaces the existing custom metadata object. If a new Connect Webview contains custom metadata and is used to reconnect a connected account, the custom metadata from the Connect Webview will entirely replace the entire custom metadata object on the connected account. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account) enables you to store custom information, like customer details or internal IDs from your application. Then, you can [filter connected accounts by the desired metadata](https://docs.seam.co/core-concepts/connected-accounts/filtering-connected-accounts-by-custom-metadata). * @param string $customer_key The customer key to associate with this connected account. If provided, the connected account and all resources under the connected account will be moved to this customer. May only be provided if the connected account is not already associated with a customer. * @param string $display_name Human-readable name for the connected account, shown in the dashboard. For example, `Booking from Airbnb House 1`. * @return void OK @@ -179,7 +179,7 @@ public function update( string $connected_account_id, ?array $accepted_capabilities = null, ?bool $automatically_manage_new_devices = null, - mixed $custom_metadata = null, + array|\stdClass|null $custom_metadata = null, ?string $customer_key = null, ?string $display_name = null, ): void { diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index ef905a9d..680a02c4 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -70,7 +70,7 @@ public function get(?string $device_id = null, ?string $name = null): Device * @param string $connected_account_id ID of the connected account for which you want to list devices. * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. + * @param array|\stdClass $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type for which you want to list devices. @@ -90,7 +90,7 @@ public function list( ?string $connected_account_id = null, ?array $connected_account_ids = null, ?string $created_before = null, - mixed $custom_metadata_has = null, + array|\stdClass|null $custom_metadata_has = null, ?string $customer_key = null, ?array $device_ids = null, ?string $device_type = null, @@ -223,7 +223,7 @@ public function report_provider_metadata(array $devices): void * * @param string $device_id ID of the device that you want to update. * @param bool $backup_access_code_pool_enabled Indicates whether the device's [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is enabled. Set to `false` to disable the pool: Seam stops refilling it and removes any backup codes that have not yet been pulled into active use. - * @param mixed $custom_metadata Custom metadata that you want to associate with the device. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) enables you to store custom information, like customer details or internal IDs from your application. Then, you can [filter devices by the desired metadata](https://docs.seam.co/core-concepts/devices/filtering-devices-by-custom-metadata). + * @param array|\stdClass $custom_metadata Custom metadata that you want to associate with the device. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) enables you to store custom information, like customer details or internal IDs from your application. Then, you can [filter devices by the desired metadata](https://docs.seam.co/core-concepts/devices/filtering-devices-by-custom-metadata). * @param bool $is_managed Indicates whether the device is managed. To unmanage a device, set `is_managed` to `false`. * @param string|NullValue $name Name for the device. * @param mixed $properties @@ -232,7 +232,7 @@ public function report_provider_metadata(array $devices): void public function update( string $device_id, ?bool $backup_access_code_pool_enabled = null, - mixed $custom_metadata = null, + array|\stdClass|null $custom_metadata = null, ?bool $is_managed = null, string|NullValue|null $name = null, mixed $properties = null, diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index bd89a73e..ba52cfa0 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -159,13 +159,13 @@ public function list( * 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](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) on an unmanaged device are unmanaged. To control an unmanaged device with Seam, [convert it to a managed device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices#convert-an-unmanaged-device-to-managed). * * @param string $device_id ID of the unmanaged device that you want to update. - * @param mixed $custom_metadata Custom metadata that you want to associate with the device. Supports up to 50 JSON key:value pairs. + * @param array|\stdClass $custom_metadata Custom metadata that you want to associate with the device. Supports up to 50 JSON key:value pairs. * @param true $is_managed Indicates whether the device is managed. Set this parameter to `true` to convert an unmanaged device to managed. * @return void OK */ public function update( string $device_id, - mixed $custom_metadata = null, + array|\stdClass|null $custom_metadata = null, ?true $is_managed = null, ): void { $request_payload = []; diff --git a/tests/RecordTypesTest.php b/tests/RecordTypesTest.php new file mode 100644 index 00000000..9ee40b22 --- /dev/null +++ b/tests/RecordTypesTest.php @@ -0,0 +1,70 @@ +getDocComment(); + $methodDoc = (new ReflectionMethod( + DevicesClient::class, + "update", + ))->getDocComment(); + + $this->assertIsString($propertyDoc); + $this->assertStringContainsString( + "@var array|\\stdClass|null", + $propertyDoc, + ); + $this->assertStringContainsString( + "@var array|\\stdClass|null", + (string) (new ReflectionProperty( + Event::class, + "connected_account_custom_metadata", + ))->getDocComment(), + ); + $this->assertStringContainsString( + "@var array|\\stdClass|null", + (string) (new ReflectionProperty( + Event::class, + "minut_metadata", + ))->getDocComment(), + ); + $this->assertIsString($methodDoc); + $this->assertStringContainsString( + '@param array|\stdClass $custom_metadata', + $methodDoc, + ); + } + + public function testRecordResponsesPreserveJsonObjects(): void + { + $device = Device::from_json( + (object) [ + "custom_metadata" => (object) [ + "label" => "front door", + "active" => true, + ], + ], + ); + + $this->assertNotNull($device); + $this->assertEquals( + (object) ["label" => "front door", "active" => true], + $device->custom_metadata, + ); + } +} From 1d9c5f1d68b4d5687c5ef9464c071251112f905f Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 00:09:23 +0000 Subject: [PATCH 53/80] 4.0.0-beta.7 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0560262..3284f14d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.6", + "version": "4.0.0-beta.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.6", + "version": "4.0.0-beta.7", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 33c2c1b4..111e78fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.6", + "version": "4.0.0-beta.7", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index f3ab3c1e..ff11aee9 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.6"; + public const VERSION = "4.0.0-beta.7"; public static function get(): string { From 22aedf602d8de93faaa125140460c0c809c5b994 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Tue, 18 Aug 2026 21:38:54 -0700 Subject: [PATCH 54/80] feat: Use the semantic method for all endpoints (#478) * feat: Use semantic method for all endpoints * ci: Generate code * test: Update tests for GET routes --------- Co-authored-by: Seam Bot --- package-lock.json | 38 +++++----------- package.json | 4 +- src/Routes/AccessCodesClient.php | 4 +- src/Routes/AccessGrantsClient.php | 8 ++-- src/Routes/AccessMethodsClient.php | 4 +- src/Routes/AcsEncodersClient.php | 4 +- src/Routes/AcsEntrancesClient.php | 8 ++-- src/Routes/ActionAttemptsClient.php | 4 +- src/Routes/ConnectWebviewsClient.php | 4 +- src/Routes/ConnectedAccountsClient.php | 4 +- src/Routes/CustomersClient.php | 4 +- src/Routes/DevicesClient.php | 4 +- src/Routes/DevicesUnmanagedClient.php | 4 +- src/Routes/EventsClient.php | 4 +- src/Routes/LocksClient.php | 4 +- src/Routes/NoiseSensorsClient.php | 4 +- src/Routes/SpacesClient.php | 12 ++--- src/Routes/ThermostatsClient.php | 4 +- src/Routes/UserIdentitiesClient.php | 4 +- tests/RetryTest.php | 61 ++++++-------------------- 20 files changed, 68 insertions(+), 119 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3284f14d..b65fac1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.7.0", - "@seamapi/fake-seam-connect": "1.86.0", + "@seamapi/blueprint": "^1.8.0", + "@seamapi/fake-seam-connect": "2.0.4", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", @@ -806,9 +806,9 @@ "license": "MIT" }, "node_modules/@seamapi/blueprint": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.7.0.tgz", - "integrity": "sha512-on9pREQKyFgIaS99KLcck5K6AqLEO8Iy5h1heuJ2sEC2A7wXk5s/TL8iDOB3+4eeaAwuHRPgze9/Omo84KjJkw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.8.0.tgz", + "integrity": "sha512-NUghBmYaKreBeBxwPIB2O9hjIFZtEjVj73tAsuKdJR8t4BxlKK1I0XDQXxo3ZsH2QezNlbdo9/0MoX/33VXuhQ==", "dev": true, "license": "MIT", "dependencies": { @@ -820,38 +820,20 @@ "npm": ">=10.0.0" } }, - "node_modules/@seamapi/fake-devicedb": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@seamapi/fake-devicedb/-/fake-devicedb-1.6.1.tgz", - "integrity": "sha512-w4Ar/s2kPnE5ExJSlpD3sKL8lkF+rLHRROArIRxtR2reHfnDSVwnDt9TzBYkHqgMP/x4o7LUzlRRpobn2xn24A==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18.12.0", - "npm": ">= 9.0.0" - }, - "optionalDependencies": { - "zod": "^3.21.4", - "zustand": "^4.3.7", - "zustand-hoist": "^2.0.0" - } - }, "node_modules/@seamapi/fake-seam-connect": { - "version": "1.86.0", - "resolved": "https://registry.npmjs.org/@seamapi/fake-seam-connect/-/fake-seam-connect-1.86.0.tgz", - "integrity": "sha512-iO5fwtSIPhzmIiLxrFDtCYF/7HTb+ywcGmc3WzWt8Sr0bLQlmwCyTJ8YYZy++Hx0FsnNpa5AmlJuJGul9Y5gZA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@seamapi/fake-seam-connect/-/fake-seam-connect-2.0.4.tgz", + "integrity": "sha512-pgPUhIMW462B3jIWTuhH/aK2wUxRuOLjhsjB7YxdXw2g+hTVRJ5w4t01spETu1g2bch3Rzffd0whUVLeC4vjwQ==", "dev": true, "license": "MIT", "bin": { "fake-seam-connect": "dist/server.js" }, "engines": { - "node": ">=18.12.0", - "npm": ">= 9.0.0" + "node": ">=22.12.0", + "npm": ">=10.0.0" }, "optionalDependencies": { - "@seamapi/fake-devicedb": ">=1.0.0-rc.0", "zustand": "^4.3.7", "zustand-hoist": "^2.0.0" } diff --git a/package.json b/package.json index 111e78fb..4fc65e1e 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ }, "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.7.0", - "@seamapi/fake-seam-connect": "1.86.0", + "@seamapi/blueprint": "^1.8.0", + "@seamapi/fake-seam-connect": "2.0.4", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index d5fd5a91..7b6f3638 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -419,8 +419,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_codes/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/access_codes/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 9d9074ff..46ae3690 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -215,8 +215,8 @@ public function get_related( } $res = Body::decode( - $this->client->request("POST", "/access_grants/get_related", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/access_grants/get_related", [ + "query" => $request_payload, ]), ); @@ -301,8 +301,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_grants/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/access_grants/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 561c9cd6..e36c36ca 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -177,8 +177,8 @@ public function get_related( } $res = Body::decode( - $this->client->request("POST", "/access_methods/get_related", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/access_methods/get_related", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index e21f8c41..0ec0f2b0 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -126,8 +126,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/encoders/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/acs/encoders/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index d3c7502f..d9fcb87e 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -145,8 +145,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/entrances/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/acs/entrances/list", [ + "query" => $request_payload, ]), ); @@ -180,9 +180,9 @@ public function list_credentials_with_access( $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/entrances/list_credentials_with_access", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 79d7eaf6..9e1e4944 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -88,8 +88,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/action_attempts/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/action_attempts/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index b34ff135..807dd4ae 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -191,8 +191,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/connect_webviews/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/connect_webviews/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index ce997b4f..673d1221 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -132,8 +132,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/connected_accounts/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/connected_accounts/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index 3f26137a..0e275dca 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -208,8 +208,8 @@ public function delete_data( $request_payload["user_keys"] = $user_keys; } - $this->client->request("POST", "/customers/delete_data", [ - "json" => (object) $request_payload, + $this->client->request("DELETE", "/customers/delete_data", [ + "query" => $request_payload, ]); } diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 680a02c4..e9cf9fae 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -156,8 +156,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/devices/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/devices/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index ba52cfa0..a4974282 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -138,8 +138,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/devices/unmanaged/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/devices/unmanaged/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index 62951f44..e8fd51d5 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -248,8 +248,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/events/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/events/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 6d13235d..96b6b9f0 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -140,8 +140,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/locks/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/locks/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index b1450aea..2bc01c76 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -71,8 +71,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/noise_sensors/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/noise_sensors/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 0b152a08..9d3fca31 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -231,8 +231,8 @@ public function get_related( } $res = Body::decode( - $this->client->request("POST", "/spaces/get_related", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/spaces/get_related", [ + "query" => $request_payload, ]), ); @@ -305,8 +305,8 @@ public function remove_acs_entrances( $request_payload["acs_entrance_ids"] = $acs_entrance_ids; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/remove_acs_entrances", [ - "json" => (object) $request_payload, + $this->client->request("DELETE", "/spaces/remove_acs_entrances", [ + "query" => $request_payload, ]); } @@ -345,8 +345,8 @@ public function remove_devices(array $device_ids, string $space_id): void $request_payload["device_ids"] = $device_ids; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/remove_devices", [ - "json" => (object) $request_payload, + $this->client->request("DELETE", "/spaces/remove_devices", [ + "query" => $request_payload, ]); } diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 1c299708..3af98e17 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -355,8 +355,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/thermostats/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/thermostats/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 7138c3f1..863a49b2 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -265,8 +265,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/user_identities/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/user_identities/list", [ + "query" => $request_payload, ]), ); diff --git a/tests/RetryTest.php b/tests/RetryTest.php index 41f3cec0..967658ba 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -51,7 +51,7 @@ public function testDoesNotRetryPostOnServiceUnavailable(): void $recorder = RecordingClient::repeating(self::service_unavailable()); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the 503 to surface"); } catch (\Throwable) { // The error mapping is covered in HttpErrorTest. @@ -74,7 +74,7 @@ public function testDoesNotRetryPostOnConnectionFailure(): void $recorder = RecordingClient::repeating_throwable($connect_error); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the connection failure to surface"); } catch (ConnectException) { // Expected. @@ -100,7 +100,7 @@ public function testDoesNotRetryPostOnTimeout(): void $recorder = RecordingClient::repeating_throwable($timeout); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the timeout to surface"); } catch (ConnectException) { // Expected. @@ -123,7 +123,7 @@ public function testDoesNotRetryPostOnTimeoutWithoutAnErrno(): void $recorder = RecordingClient::repeating_throwable($timeout); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the timeout to surface"); } catch (ConnectException) { // Expected. @@ -135,8 +135,6 @@ public function testDoesNotRetryPostOnTimeoutWithoutAnErrno(): void /** * Repeating an idempotent request is safe even when the server may have * received it, so a timeout does get retried there. - * - * TODO: Use the SDK route once generated reads use GET instead of POST. */ public function testRetriesIdempotentRequestsOnTimeout(): void { @@ -149,11 +147,9 @@ public function testRetriesIdempotentRequestsOnTimeout(): void $recorder = new RecordingClient([$timeout, $timeout, self::devices()]); - $res = Body::decode( - $this->seam($recorder)->client->request("GET", "/devices/list"), - ); + $devices = $this->seam($recorder)->devices->list(); - $this->assertSame([], $res->devices); + $this->assertSame([], $devices); $this->assertSame(3, $recorder->attempt_count()); } @@ -174,7 +170,7 @@ public function testDoesNotRetryPostOnConnectionReset(): void $recorder = RecordingClient::repeating_throwable($reset); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the connection reset to surface"); } catch (RequestException) { // Expected. @@ -183,14 +179,11 @@ public function testDoesNotRetryPostOnConnectionReset(): void $this->assertSame(1, $recorder->attempt_count()); } - /** - * TODO: Use the SDK route once generated reads use GET instead of POST. - */ public function testStopsRetryingOnceRetriesAreExhausted(): void { $connect_error = new ConnectException( "Could not resolve host", - new Request("POST", "/devices/list"), + new Request("GET", "/devices/list"), ); $recorder = RecordingClient::repeating_throwable($connect_error); @@ -198,32 +191,23 @@ public function testStopsRetryingOnceRetriesAreExhausted(): void $this->expectException(ConnectException::class); try { - $this->seam($recorder, retries: 1)->client->request( - "GET", - "/devices/list", - ); + $this->seam($recorder, retries: 1)->devices->list(); } finally { $this->assertSame(2, $recorder->attempt_count()); } } - /** - * TODO: Use the SDK route once generated reads use GET instead of POST. - */ public function testDoesNotRetryWhenRetriesAreDisabled(): void { $connect_error = new ConnectException( "Could not resolve host", - new Request("POST", "/devices/list"), + new Request("GET", "/devices/list"), ); $recorder = RecordingClient::repeating_throwable($connect_error); try { - $this->seam($recorder, retries: 0)->client->request( - "GET", - "/devices/list", - ); + $this->seam($recorder, retries: 0)->devices->list(); $this->fail("Expected the connection failure to surface"); } catch (ConnectException) { // Expected. @@ -234,12 +218,6 @@ public function testDoesNotRetryWhenRetriesAreDisabled(): void /** * Repeating a read is safe, so a 503 on one should be retried. - * - * TODO: Every SDK call currently goes over POST, where a status based - * retry is never safe, so the SDK's own reads get none and this test - * marks itself incomplete. Once the SDK issues GET for the endpoints - * that support it, planned for a followup PR, the incomplete branch - * stops matching and the real assertions take over. */ public function testSdkReadsAreRetriedOnServiceUnavailable(): void { @@ -249,14 +227,7 @@ public function testSdkReadsAreRetriedOnServiceUnavailable(): void self::devices(), ]); - try { - $devices = $this->seam($recorder)->devices->list(); - } catch (\Throwable) { - $this->assertSame(1, $recorder->attempt_count()); - $this->markTestIncomplete( - "The SDK does not use GET yet, so its reads get no status based retries.", - ); - } + $devices = $this->seam($recorder)->devices->list(); $this->assertSame([], $devices); $this->assertSame(3, $recorder->attempt_count()); @@ -266,8 +237,6 @@ public function testSdkReadsAreRetriedOnServiceUnavailable(): void * Building a client must not mutate a handler stack the caller may * reuse: a second client built from the same options would otherwise * stack the retry middleware twice and multiply the retries. - * - * TODO: Use the SDK route once generated reads use GET instead of POST. */ public function testBuildingASecondClientDoesNotStackRetries(): void { @@ -298,10 +267,8 @@ public function testBuildingASecondClientDoesNotStackRetries(): void } /** - * The SDK itself only issues POSTs, but a caller reaching for the client - * directly with an idempotent method does get status based retries. - * - * TODO: Use the SDK route once generated reads use GET instead of POST. + * A caller reaching for the client directly with an idempotent method + * gets status based retries. */ public function testRetriesIdempotentRequestsOnServiceUnavailable(): void { From 7deea6df5defd1d6e1f61d82dc5c7886de41b5b1 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 04:39:36 +0000 Subject: [PATCH 55/80] 4.0.0-beta.8 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b65fac1f..8647861a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.7", + "version": "4.0.0-beta.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.7", + "version": "4.0.0-beta.8", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 4fc65e1e..9ffa8810 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.7", + "version": "4.0.0-beta.8", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index ff11aee9..a9c6b4a6 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.7"; + public const VERSION = "4.0.0-beta.8"; public static function get(): string { From 71b415e2b5eeeea424054b1ce2c363cc592732e5 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 14:06:13 -0700 Subject: [PATCH 56/80] fix: Give every action attempt wait at least one poll (#465) * fix: Give every action attempt wait at least one poll The poll loop checked whether a whole polling interval would fit before the deadline, so it gave up one full interval early. With a timeout shorter than the interval it raised immediately, having polled zero times and consumed none of its budget: timeout 30 with polling_interval 60 never made a single request. Check the deadline itself instead, and cap the sleep at the time left. A pending attempt is now always polled at least once, and the wait no longer overruns the deadline by up to an interval. Also validate the options up front. A polling_interval of 0 would poll without pause, and a negative one reached usleep() and raised a bare ValueError; both now raise InvalidOptionsError, as does a negative timeout. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG * refactor: Drop explanatory comments Comments that restate what the code and tests already say, or that argue a point at a reviewer, do not earn their keep once the review is over. --------- Co-authored-by: Claude --- src/Http/ResolveActionAttempt.php | 29 +++++++++-- tests/WaitForActionAttemptTest.php | 79 ++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index 3d51c6c1..4999d833 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -5,6 +5,7 @@ use GuzzleHttp\ClientInterface; use Seam\ActionAttemptFailedError; use Seam\ActionAttemptTimeoutError; +use Seam\InvalidOptionsError; use Seam\Resources\ActionAttempt; /** @@ -21,6 +22,8 @@ final class ResolveActionAttempt /** * @param bool|array{timeout?: float, polling_interval?: float}|null $wait_for_action_attempt + * + * @throws InvalidOptionsError If timeout is negative or polling_interval is not greater than zero. */ public static function resolve_action_attempt( ActionAttempt $action_attempt, @@ -35,11 +38,27 @@ public static function resolve_action_attempt( ? $wait_for_action_attempt : []; + $timeout = (float) ($options["timeout"] ?? self::TIMEOUT); + $polling_interval = + (float) ($options["polling_interval"] ?? self::POLLING_INTERVAL); + + if ($timeout < 0.0) { + throw new InvalidOptionsError( + "The timeout option must not be negative, got {$timeout}", + ); + } + + if ($polling_interval <= 0.0) { + throw new InvalidOptionsError( + "The polling_interval option must be greater than zero, got {$polling_interval}", + ); + } + return self::poll( $action_attempt, $client, - (float) ($options["timeout"] ?? self::TIMEOUT), - (float) ($options["polling_interval"] ?? self::POLLING_INTERVAL), + $timeout, + $polling_interval, ); } @@ -60,11 +79,13 @@ private static function poll( throw new ActionAttemptFailedError($action_attempt); } - if (self::now() + $polling_interval > $deadline) { + $remaining = $deadline - self::now(); + + if ($remaining <= 0.0) { throw new ActionAttemptTimeoutError($action_attempt, $timeout); } - usleep((int) ($polling_interval * 1000000.0)); + usleep((int) (min($polling_interval, $remaining) * 1000000.0)); $action_attempt = self::get_action_attempt( $client, diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index 7f4696ed..84eeb9f3 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -7,6 +7,7 @@ use Seam\ActionAttemptError; use Seam\ActionAttemptFailedError; use Seam\ActionAttemptTimeoutError; +use Seam\InvalidOptionsError; use Seam\Resources\ActionAttempt; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; @@ -276,6 +277,84 @@ public function testTimesOutWhileTheActionAttemptIsPending(): void } } + public function testPollsOnceWhenTheIntervalOutlastsTheTimeout(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + $action_attempt = $this->pending_action_attempt($seam); + + $started_at = microtime(true); + + try { + $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: [ + "timeout" => 0.3, + "polling_interval" => 30.0, + ], + ); + $this->fail("Expected ActionAttemptTimeoutError"); + } catch (ActionAttemptTimeoutError) { + $elapsed = microtime(true) - $started_at; + + $this->assertGreaterThanOrEqual(0.3, $elapsed); + $this->assertLessThan(5.0, $elapsed); + } + } + + public function testResolvesWhenTheIntervalOutlastsTheTimeout(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + $action_attempt = $this->pending_action_attempt($seam); + $this->set_status($seam, $action_attempt, "success"); + + $resolved = $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: [ + "timeout" => 0.3, + "polling_interval" => 30.0, + ], + ); + + $this->assertSame("success", $resolved->status); + } + + /** + * @dataProvider invalidWaitOptions + */ + public function testRejectsInvalidWaitOptions( + array $wait_for_action_attempt, + string $expected_message, + ): void { + $seam = $this->seam(wait_for_action_attempt: false); + $action_attempt = $this->pending_action_attempt($seam); + + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage($expected_message); + + $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: $wait_for_action_attempt, + ); + } + + public static function invalidWaitOptions(): array + { + return [ + "zero polling_interval" => [ + ["polling_interval" => 0], + "polling_interval option must be greater than zero", + ], + "negative polling_interval" => [ + ["polling_interval" => -5], + "polling_interval option must be greater than zero", + ], + "negative timeout" => [ + ["timeout" => -1], + "timeout option must not be negative", + ], + ]; + } + /** * Resolving fetches the action attempt through the HTTP client rather * than the route client, so enabling the option on the route that reads From 6002aec20484e6560c999cec2c680c6595c28aa7 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 21:06:58 +0000 Subject: [PATCH 57/80] 4.0.0-beta.9 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8647861a..7c9c26dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.8", + "version": "4.0.0-beta.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.8", + "version": "4.0.0-beta.9", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 9ffa8810..08471dd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.8", + "version": "4.0.0-beta.9", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index a9c6b4a6..118055f2 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.8"; + public const VERSION = "4.0.0-beta.9"; public static function get(): string { From 4a4fcbc5919f9bee1e448eb095dbfbbd54c8cad3 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 14:08:35 -0700 Subject: [PATCH 58/80] fix: Accept the null sentinel on the workspaces proxy (#468) * fix: Accept the null sentinel on the workspaces proxy WorkspacesProxy hand-copies the generated workspace route signatures, and its copy of create had narrowed connect_partner_name to ?string where the generated client declares string|NullValue|null. So NullValue::NULL worked through Seam and raised a TypeError through SeamWithoutWorkspace, even though the README presents the sentinel as a general rule. Widen the parameter to match and restore the docblock the copy dropped. The proxy already forwards by name to survive a parameter being reordered, but nothing guarded against a type drifting, and psalm does not analyze src/Routes. Add a test that reflects over both signatures and compares every parameter name, type, and default, so the next drift fails with a diff rather than reaching a caller. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG * refactor: Drop explanatory comments Comments that restate what the code and tests already say, or that argue a point at a reviewer, do not earn their keep once the review is over. --------- Co-authored-by: Claude --- src/WorkspacesProxy.php | 13 ++++- tests/WorkspacesProxyTest.php | 89 +++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tests/WorkspacesProxyTest.php diff --git a/src/WorkspacesProxy.php b/src/WorkspacesProxy.php index e1d64fb1..145c910c 100644 --- a/src/WorkspacesProxy.php +++ b/src/WorkspacesProxy.php @@ -26,10 +26,21 @@ public function list(): array return $this->workspaces->list(); } + /** + * Creates a new [workspace](https://docs.seam.co/core-concepts/workspaces). + * + * @param string $name Name of the new workspace. + * @param string $company_name Company name for the new workspace. + * @param string|NullValue $connect_partner_name Connect partner name for the new workspace. + * @param mixed $connect_webview_customization [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews) customizations for the new workspace. + * @param bool $is_sandbox Indicates whether the new workspace is a [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces). + * @param string $organization_id ID of the organization to associate with the new workspace. + * @return Workspace OK + */ public function create( string $name, ?string $company_name = null, - ?string $connect_partner_name = null, + string|NullValue|null $connect_partner_name = null, mixed $connect_webview_customization = null, ?bool $is_sandbox = null, ?string $organization_id = null, diff --git a/tests/WorkspacesProxyTest.php b/tests/WorkspacesProxyTest.php new file mode 100644 index 00000000..53d4ea64 --- /dev/null +++ b/tests/WorkspacesProxyTest.php @@ -0,0 +1,89 @@ +assertSame( + self::describe($generated), + self::describe($proxied), + "WorkspacesProxy::{$method} has drifted from WorkspacesClient::{$method}", + ); + } + + public static function proxiedMethods(): array + { + return [ + "create" => ["create"], + "list" => ["list"], + ]; + } + + /** + * @return array + */ + private static function describe(\ReflectionMethod $method): array + { + $described = [ + "@return" => (string) $method->getReturnType(), + ]; + + foreach ($method->getParameters() as $parameter) { + $described[$parameter->getName()] = sprintf( + "%s%s", + (string) $parameter->getType(), + $parameter->isDefaultValueAvailable() ? " = default" : "", + ); + } + + return $described; + } + + public function testCreateForwardsTheNullSentinel(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, [ + "workspace" => [ + "workspace_id" => "ws_1", + "name" => "Sentinel Workspace", + ], + ]), + ]); + + $seam = SeamWithoutWorkspace::from_personal_access_token( + "seam_at_token", + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + ); + + $workspace = $seam->workspaces->create( + name: "Sentinel Workspace", + connect_partner_name: NullValue::NULL, + ); + + $this->assertSame("Sentinel Workspace", $workspace->name); + + $body = $recorder->body(); + + $this->assertSame("Sentinel Workspace", $body->name); + $this->assertNull($body->connect_partner_name); + $this->assertTrue(property_exists($body, "connect_partner_name")); + } +} From 0e68949970e1b886179eda51e9273ffcbc540757 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 21:09:26 +0000 Subject: [PATCH 59/80] 4.0.0-beta.10 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c9c26dc..848f9446 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.9", + "version": "4.0.0-beta.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.9", + "version": "4.0.0-beta.10", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 08471dd7..dc5880b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.9", + "version": "4.0.0-beta.10", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index 118055f2..03c71cfb 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.9"; + public const VERSION = "4.0.0-beta.10"; public static function get(): string { From 958c9b10cb45dfa0c1610a3c26950f4dcf673124 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 14:10:50 -0700 Subject: [PATCH 60/80] fix: Stop the paginator when a cursor repeats (#466) * fix: Stop the paginator when a cursor repeats flatten and flattenToArray looped on has_next_page alone, with no bound on the number of pages and no memory of the cursors already fetched. A server or cache that handed back a cursor it had already given out sent the walk around forever, refetching the same page while flattenToArray grew the result array until the process ran out of memory. Nothing stopped it: the request timeout resets on every page. Walk the pages through one generator that remembers the cursors it has followed and ends when one comes back, so both flatten and flattenToArray terminate. Signal the first page with a null cursor rather than the reserved string "FIRST_PAGE". A real cursor equal to that literal used to pass the next-page checks and then be dropped, silently refetching page one. Keep the pagination of the page in flight in a single field instead of a map keyed by cursor that was written once, read once, and never cleared. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG * refactor: Drop explanatory comments Comments that restate what the code and tests already say, or that argue a point at a reviewer, do not earn their keep once the review is over. --------- Co-authored-by: Claude --- src/Paginator.php | 71 ++++++++++++++++++++++------------------- tests/PaginatorTest.php | 64 +++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 33 deletions(-) diff --git a/src/Paginator.php b/src/Paginator.php index d98dfdc8..b9582d8d 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -12,8 +12,7 @@ class Paginator { private $request; private array $params; - private array $pagination_cache = []; - private const FIRST_PAGE = "FIRST_PAGE"; + private ?Pagination $pagination = null; public function __construct(callable $request, array $params = []) { @@ -26,7 +25,7 @@ public function __construct(callable $request, array $params = []) */ public function firstPage(): array { - return $this->fetchPage(self::FIRST_PAGE); + return $this->fetchPage(null); } /** @@ -46,12 +45,12 @@ public function nextPage(?string $next_page_cursor): array /** * @return array{0: array, 1: Pagination} */ - private function fetchPage(string $cursor): array + private function fetchPage(?string $cursor): array { $request = $this->request; $params = $this->params; - if ($cursor !== self::FIRST_PAGE) { + if ($cursor !== null) { $params["page_cursor"] = $cursor; } @@ -59,11 +58,10 @@ private function fetchPage(string $cursor): array // through the params still fires. $on_response = $params["on_response"] ?? null; - $params["on_response"] = function ($response) use ( - $cursor, - $on_response, - ): void { - $this->cachePagination($response, $cursor); + $this->pagination = null; + + $params["on_response"] = function ($response) use ($on_response): void { + $this->readPagination($response); if (is_callable($on_response)) { $on_response($response); @@ -72,16 +70,16 @@ private function fetchPage(string $cursor): array $data = $request($params); - if (!array_key_exists($cursor, $this->pagination_cache)) { + if ($this->pagination === null) { throw new \InvalidArgumentException( "Cannot use a paginator with an unpaginated endpoint", ); } - return [$data, $this->pagination_cache[$cursor]]; + return [$data, $this->pagination]; } - private function cachePagination($response, string $cursor): void + private function readPagination($response): void { if (!is_object($response) || !isset($response->pagination)) { throw new \InvalidArgumentException( @@ -89,22 +87,14 @@ private function cachePagination($response, string $cursor): void ); } - $this->pagination_cache[$cursor] = Pagination::from_json( - $response->pagination, - ); + $this->pagination = Pagination::from_json($response->pagination); } public function flattenToArray(): array { $items = []; - [$response, $pagination] = $this->firstPage(); - $items = array_merge($items, $response); - - while ($pagination->has_next_page) { - [$response, $pagination] = $this->nextPage( - $pagination->next_page_cursor, - ); + foreach ($this->walk() as [$response]) { $items = array_merge($items, $response); } @@ -113,20 +103,35 @@ public function flattenToArray(): array public function flatten() { - [$current, $pagination] = $this->firstPage(); - - foreach ($current as $item) { - yield $item; + foreach ($this->walk() as [$response]) { + foreach ($response as $item) { + yield $item; + } } + } - while ($pagination->has_next_page) { - [$current, $pagination] = $this->nextPage( - $pagination->next_page_cursor, - ); + /** + * @return \Generator + */ + private function walk(): \Generator + { + $page = $this->firstPage(); + $seen = []; - foreach ($current as $item) { - yield $item; + yield $page; + + while ($page[1]->has_next_page) { + $cursor = $page[1]->next_page_cursor; + + if ($cursor === null || isset($seen[$cursor])) { + return; } + + $seen[$cursor] = true; + + $page = $this->nextPage($cursor); + + yield $page; } } } diff --git a/tests/PaginatorTest.php b/tests/PaginatorTest.php index c81460dd..a71d1095 100644 --- a/tests/PaginatorTest.php +++ b/tests/PaginatorTest.php @@ -6,7 +6,9 @@ use Seam\Pagination; use Seam\Paginator; +use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; +use Tests\Support\RecordingClient; final class PaginatorTest extends FakeSeamConnectTestCase { @@ -135,4 +137,66 @@ public function testEndpointWithoutPaginationIsRejected(): void ->createPaginator(fn($p) => $seam->workspaces->list()) ->firstPage(); } + + private function pinned_cursor_paginator(string $cursor): Paginator + { + $recorder = RecordingClient::repeating( + RecordingClient::json(200, [ + "connected_accounts" => [ + ["connected_account_id" => "ca_1"], + ["connected_account_id" => "ca_2"], + ], + "pagination" => [ + "has_next_page" => true, + "next_page_cursor" => $cursor, + ], + "ok" => true, + ]), + times: 200, + ); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + retries: 0, + ); + + return $seam->createPaginator( + fn($p) => $seam->connected_accounts->list(...$p), + ["limit" => 2], + ); + } + + public function testFlattenToArrayStopsWhenTheCursorRepeats(): void + { + $all = $this->pinned_cursor_paginator("stuck")->flattenToArray(); + + $this->assertCount(4, $all); + } + + public function testFlattenStopsWhenTheCursorRepeats(): void + { + $ids = []; + + foreach ($this->pinned_cursor_paginator("stuck")->flatten() as $item) { + $ids[] = $item->connected_account_id; + $this->assertLessThan(10, count($ids), "flatten did not terminate"); + } + + $this->assertCount(4, $ids); + } + + public function testACursorNamedFirstPageStillAdvances(): void + { + $pages = $this->pinned_cursor_paginator("FIRST_PAGE"); + + [, $pagination] = $pages->firstPage(); + $this->assertSame("FIRST_PAGE", $pagination->next_page_cursor); + + [$second] = $pages->nextPage($pagination->next_page_cursor); + + $this->assertNotEmpty($second); + $this->assertCount(4, $pages->flattenToArray()); + } } From d771ac091174c90ef68d4017e1c3fb351127f694 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 21:11:40 +0000 Subject: [PATCH 61/80] 4.0.0-beta.11 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 848f9446..08e6d717 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.10", + "version": "4.0.0-beta.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.10", + "version": "4.0.0-beta.11", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index dc5880b1..2d1a9eb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.10", + "version": "4.0.0-beta.11", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index 03c71cfb..d2c6d29f 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.10"; + public const VERSION = "4.0.0-beta.11"; public static function get(): string { From 8ae9d8ada43caa51b55bb0cfb50fd66cb6405db9 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 14:12:14 -0700 Subject: [PATCH 62/80] fix: Format search param floats without touching global ini state (#471) * fix: Format search param floats without touching global ini state shortest_digits flipped serialize_precision to -1 around every float it formatted, then restored it. The window is small but it is process wide: anything else that formats a float inside it, a fiber or coroutine that runs there, a tick function, a signal handler calling json_encode, sees the altered setting. The comment on the call already named the hazard it only partly avoids. Widen a printf conversion until the result reads back as the same float instead. That is what "shortest that round-trips" means, it depends on no configuration, and it is safe to re-enter. Output is unchanged; the existing float vectors cover it. Also fix the expanded year. The ISO format the port targets switches to a signed six digit year outside 0000-9999, which %04d never produced: year 12345 came out as "12345" where the reference gives "+012345", and year -1 came out as "-001", because the sign counted against the field width, which is not a valid date in any format. Both were reachable only through setDate or a large timestamp, since parsing such a year throws, which is how they survived. Document what StrictUrlSearchParamsSerializer::update does with the strict flag: it follows the query rather than the call, so params already present count. That matches the empty query rule and is left as is; only the docblock was silent. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG * refactor: Drop explanatory comments Comments that restate what the code and tests already say, or that argue a point at a reviewer, do not earn their keep once the review is over. --------- Co-authored-by: Claude --- src/UrlSearchParamsSerializer.php | 46 ++++++++++------- tests/UrlSearchParamsSerializerTest.php | 68 +++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 18 deletions(-) diff --git a/src/UrlSearchParamsSerializer.php b/src/UrlSearchParamsSerializer.php index f0fad8d4..592c20d2 100644 --- a/src/UrlSearchParamsSerializer.php +++ b/src/UrlSearchParamsSerializer.php @@ -238,13 +238,27 @@ private static function format_datetime(\DateTimeInterface $value): string $milliseconds = intdiv((int) $utc->format("u"), 1000); return sprintf( - "%04d-%s.%03dZ", - (int) $utc->format("Y"), + "%s-%s.%03dZ", + self::format_year((int) $utc->format("Y")), $utc->format("m-d\TH:i:s"), $milliseconds, ); } + /** + * Renders the year of a date time string: four digits in the ordinary + * range, and otherwise the expanded year, which is always signed and + * always six digits. + */ + private static function format_year(int $year): string + { + if ($year >= 0 && $year <= 9999) { + return sprintf("%04d", $year); + } + + return sprintf("%s%06d", $year < 0 ? "-" : "+", abs($year)); + } + /** * Formats a float with the ECMAScript Number::toString algorithm. * @@ -285,33 +299,29 @@ private static function format_number(string $name, float $value): string */ private static function shortest_digits(float $value): array { - // At -1, PHP's float-to-string conversion produces the shortest - // string that round-trips. Restored because the setting also affects - // the caller's own serialize() and json_encode() calls. - $precision = ini_set("serialize_precision", "-1"); - - try { - $repr = var_export($value, true); - } finally { - if ($precision !== false) { - ini_set("serialize_precision", $precision); + $repr = sprintf("%.16E", $value); + + for ($precision = 0; $precision < 16; $precision++) { + $candidate = sprintf("%.{$precision}E", $value); + + if ((float) $candidate === $value) { + $repr = $candidate; + break; } } if ( - preg_match( - '/^(\d+)(?:\.(\d+))?(?:E([+-]?\d+))?$/i', - $repr, - $matches, - ) !== 1 + preg_match('/^(\d)(?:\.(\d+))?E([+-]\d+)$/i', $repr, $matches) !== 1 ) { throw new \RuntimeException( "Could not parse the PHP float representation: {$repr}", ); } + // %E normalizes to one digit before the point, so the exponent + // places the point directly. $digits = $matches[1] . ($matches[2] ?? ""); - $point = strlen($matches[1]) + (int) ($matches[3] ?? "0"); + $point = (int) $matches[3] + 1; $stripped = ltrim($digits, "0"); $point -= strlen($digits) - strlen($stripped); diff --git a/tests/UrlSearchParamsSerializerTest.php b/tests/UrlSearchParamsSerializerTest.php index 764a4fc0..b516ac46 100644 --- a/tests/UrlSearchParamsSerializerTest.php +++ b/tests/UrlSearchParamsSerializerTest.php @@ -96,6 +96,45 @@ public function testSerializesFloatUsingTheEcmascriptNumberFormat(): void ); } + /** + * @dataProvider serializePrecisionSettings + */ + public function testSerializesFloatIndependentlyOfSerializePrecision( + string $setting, + ): void { + $original = ini_get("serialize_precision"); + + try { + ini_set("serialize_precision", $setting); + + $this->assertSame("foo=23.8", self::serialize(["foo" => 23.8])); + $this->assertSame( + "foo=0.30000000000000004", + self::serialize(["foo" => 0.1 + 0.2]), + ); + $this->assertSame( + "foo=1.7976931348623157e%2B308", + self::serialize(["foo" => PHP_FLOAT_MAX]), + ); + + $this->assertSame($setting, ini_get("serialize_precision")); + } finally { + if ($original !== false) { + ini_set("serialize_precision", $original); + } + } + } + + public static function serializePrecisionSettings(): array + { + return [ + "shortest round trip" => ["-1"], + "seventeen digits" => ["17"], + "php default precision" => ["14"], + "very low" => ["3"], + ]; + } + public function testSerializesBool(): void { $this->assertSame("foo=true", self::serialize(["foo" => true])); @@ -240,6 +279,35 @@ public function testSerializesDatetimeBeforeTheEpoch(): void ); } + /** + * @dataProvider expandedYears + */ + public function testSerializesTheExpandedYear( + int $year, + string $expected, + ): void { + $date = (new \DateTimeImmutable("2000-01-02T03:04:05Z"))->setDate( + $year, + 1, + 2, + ); + + $this->assertSame( + "then=" . rawurlencode($expected), + self::serialize(["then" => $date]), + ); + } + + public static function expandedYears(): array + { + return [ + "five digits" => [12345, "+012345-01-02T03:04:05.000Z"], + "six digits" => [275760, "+275760-01-02T03:04:05.000Z"], + "negative" => [-1, "-000001-01-02T03:04:05.000Z"], + "negative four digits" => [-2024, "-002024-01-02T03:04:05.000Z"], + ]; + } + public function testZeroPadsTheYearToFourDigits(): void { $this->assertSame( From 53e62c875e41cb7e792a44fa3a516ff275068434 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 21:13:04 +0000 Subject: [PATCH 63/80] 4.0.0-beta.12 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08e6d717..86bd03df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.11", + "version": "4.0.0-beta.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.11", + "version": "4.0.0-beta.12", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 2d1a9eb0..6f1a4532 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.11", + "version": "4.0.0-beta.12", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index d2c6d29f..bf12a75e 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.11"; + public const VERSION = "4.0.0-beta.12"; public static function get(): string { From 513236f172c015619e1bbe1788aba14ce3d93219 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 14:13:48 -0700 Subject: [PATCH 64/80] fix: Stop the search params map form from corrupting values (#470) * fix: Stop the search params map form from corrupting values The map form of the UrlSearchParams constructor cast every value with (string). An array under a string key became the literal "Array" behind an "Array to string conversion" warning, so the documented shape for a repeated param silently dropped its contents. Booleans became "1" and "", where the serialization standard renders "true" and "false", leaving two encodings of a bool in one package. A float or a date got PHP's default formatting rather than the ECMAScript number and ISO formats the standard requires, and an object raised a bare Error. Expand a list into one pair per element, which is how the standard represents an array and what the class docblock already describes. Render the values this class can render the way the serializer renders them, and refuse the ones it cannot with UnserializableParamError, pointing at UrlSearchParamsSerializer, where that formatting lives. Also fix the README pagination resume example, which decoded the stored state with json_decode(..., false) and handed the resulting stdClass to createPaginator's array parameter, raising a TypeError as written. The object form is load bearing for the pagination half of the snippet, so cast the params half rather than decoding associatively. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG * refactor: Drop explanatory comments Comments that restate what the code and tests already say, or that argue a point at a reviewer, do not earn their keep once the review is over. --------- Co-authored-by: Claude --- README.md | 2 +- src/UrlSearchParams.php | 51 ++++++++++++++++++++++++- tests/UrlSearchParamsSerializerTest.php | 50 ++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c6f88f99..2145cf1f 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ $stored_data = json_decode( false ); -$params = $stored_data[0] ?? []; +$params = (array) ($stored_data[0] ?? []); $pagination = $stored_data[1] ?? (object) ["has_next_page" => false, "next_page_cursor" => null]; diff --git a/src/UrlSearchParams.php b/src/UrlSearchParams.php index d16f67c6..4789f902 100644 --- a/src/UrlSearchParams.php +++ b/src/UrlSearchParams.php @@ -21,6 +21,8 @@ class UrlSearchParams implements \Countable, \IteratorAggregate * @param string|array|list|null $init * A query string, a map of names to values, or a list of * name-value pairs + * + * @throws UnserializableParamError If a map value cannot be rendered */ public function __construct(string|array|null $init = null) { @@ -49,12 +51,57 @@ public function __construct(string|array|null $init = null) foreach ($init as $name => $value) { if (is_array($value) && is_int($name)) { $this->pairs[] = [(string) $value[0], (string) $value[1]]; - } else { - $this->pairs[] = [(string) $name, (string) $value]; + continue; + } + + if (is_array($value)) { + foreach ($value as $element) { + $this->pairs[] = [ + (string) $name, + self::stringify((string) $name, $element), + ]; + } + continue; } + + $this->pairs[] = [ + (string) $name, + self::stringify((string) $name, $value), + ]; } } + /** + * Renders a map value as a search param value. + * + * @throws UnserializableParamError If the value has no string form here + */ + private static function stringify(string $name, mixed $value): string + { + if ($value === null || $value instanceof NullValue) { + return ""; + } + + if (is_string($value)) { + return $value; + } + + if (is_bool($value)) { + return $value ? "true" : "false"; + } + + if (is_int($value)) { + return (string) $value; + } + + throw new UnserializableParamError( + $name, + "is a " . + get_debug_type($value) . + ", which UrlSearchParams cannot render; serialize it with UrlSearchParamsSerializer first", + ); + } + /** * Appends a name-value pair, keeping any existing pairs with this name. */ diff --git a/tests/UrlSearchParamsSerializerTest.php b/tests/UrlSearchParamsSerializerTest.php index b516ac46..c228ff24 100644 --- a/tests/UrlSearchParamsSerializerTest.php +++ b/tests/UrlSearchParamsSerializerTest.php @@ -655,6 +655,56 @@ public function testUrlSearchParamsFromMap(): void ); } + public function testUrlSearchParamsFromMapExpandsAList(): void + { + $this->assertSame( + "device_ids=d1&device_ids=d2", + (new UrlSearchParams([ + "device_ids" => ["d1", "d2"], + ]))->to_string(), + ); + } + + public function testUrlSearchParamsFromMapRendersValuesLikeTheStandard(): void + { + $params = ["t" => true, "f" => false, "n" => 42, "u" => null]; + + $this->assertSame( + "t=true&f=false&n=42&u=", + (new UrlSearchParams($params))->to_string(), + ); + + $this->assertSame( + "f=false&n=42&t=true&u=", + UrlSearchParamsSerializer::serialize([ + "t" => true, + "f" => false, + "n" => 42, + "u" => NullValue::NULL, + ]), + ); + } + + /** + * @dataProvider valuesTheMapFormCannotRender + */ + public function testUrlSearchParamsFromMapRejectsWhatItCannotRender( + mixed $value, + ): void { + $this->expectException(UnserializableParamError::class); + + new UrlSearchParams(["a" => $value]); + } + + public static function valuesTheMapFormCannotRender(): array + { + return [ + "float" => [20.5], + "date" => [new \DateTimeImmutable("2024-01-01T00:00:00Z")], + "object" => [new \stdClass()], + ]; + } + public function testUrlSearchParamsAppendAndGet(): void { $search_params = new UrlSearchParams(); From a2cd8cee447ea105ee4392158b6ba0f0ad7e3baf Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 21:14:29 +0000 Subject: [PATCH 65/80] 4.0.0-beta.13 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 86bd03df..9123de5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.12", + "version": "4.0.0-beta.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.12", + "version": "4.0.0-beta.13", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 6f1a4532..3297cac8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.12", + "version": "4.0.0-beta.13", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index bf12a75e..5d67ee38 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.12"; + public const VERSION = "4.0.0-beta.13"; public static function get(): string { From 9ebee0c2e3e290723435e58e7af0196bc1203e1c Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 14:30:36 -0700 Subject: [PATCH 66/80] fix: Emit resource constructor parameters required first (#473) Resource constructor parameters were ordered by property name alone, so an optional property, one carrying a null default, could be declared ahead of a required one. PHP deprecates that ordering, which made every optional default inert, and PHP 9 is expected to make it fatal. Loading the resource classmap emitted 409 E_DEPRECATED notices across 26 of the 32 files. None of the tooling saw them: psalm and phpunit both exclude src/Resources, and phpunit routes deprecations to a channel failOnWarning does not cover, so the suite stayed green through all of them. Nor is it only cosmetic. composer lint:syntax shells out to php -l, so it fails on any host whose error_reporting includes E_DEPRECATED, which is PHP's own default when no php.ini is loaded. Emit required properties first and optional ones after, alphabetical within each group, the same shape the route generator already produces for endpoint parameters. Which properties are required stays the blueprint's call, read from isOptional, rather than being flattened by giving everything a default. from_json passes every value by name, so it is unaffected. Turn on failOnDeprecation so this cannot come back unnoticed. Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG Co-authored-by: Claude --- MIGRATION.md | 41 +++-- codegen/lib/layouts/resource.ts | 18 +- phpunit.xml.dist | 1 + src/Resources/AccessCode.php | 100 +++++------ src/Resources/AccessGrant.php | 72 ++++---- src/Resources/AccessMethod.php | 72 ++++---- src/Resources/AcsAccessGroup.php | 24 +-- src/Resources/AcsCredential.php | 64 +++---- src/Resources/AcsEntrance.php | 48 ++--- src/Resources/AcsSystem.php | 72 ++++---- src/Resources/AcsUser.php | 80 ++++----- src/Resources/ActionAttempt.php | 134 +++++++------- src/Resources/ClientSession.php | 16 +- src/Resources/ConnectWebview.php | 8 +- src/Resources/ConnectedAccount.php | 56 +++--- src/Resources/Device.php | 228 ++++++++++++------------ src/Resources/DeviceProvider.php | 32 ++-- src/Resources/Event.php | 228 ++++++++++++------------ src/Resources/InstantKey.php | 16 +- src/Resources/NoiseThreshold.php | 8 +- src/Resources/Phone.php | 8 +- src/Resources/Space.php | 32 ++-- src/Resources/ThermostatSchedule.php | 16 +- src/Resources/UnmanagedAccessCode.php | 84 ++++----- src/Resources/UnmanagedAccessGrant.php | 48 ++--- src/Resources/UnmanagedAccessMethod.php | 48 ++--- src/Resources/UnmanagedDevice.php | 152 ++++++++-------- src/Resources/Webhook.php | 16 +- src/Resources/Workspace.php | 8 +- 29 files changed, 878 insertions(+), 852 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 6180f024..87b0926e 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -14,19 +14,20 @@ composer require "seamapi/seam:^4" ## Summary of breaking changes -| Change | Affects you if... | -| ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -| [PHP 8.2+ required](#php-82-or-later-is-required) | You run PHP 8.0 or 8.1 | -| [`Seam\Seam` replaces `Seam\SeamClient`](#seamseam-replaces-seamseamclient) | You construct the client (everyone) | -| [`$seam->client` is the Guzzle client](#seam-client-is-the-guzzle-client) | You use `$seam->client`, `$seam->request()`, or the removed public properties | -| [Requests are retried and time out sooner](#requests-are-retried-and-time-out-sooner) | You depend on requests never being retried, or on the 60-second timeout | -| [`poll_until_ready` is replaced](#poll_until_ready-is-replaced-by-wait_for_action_attempt) | You call `$seam->action_attempts->poll_until_ready()` or rely on its 20 s/0.4 s timing | -| [Nested resource classes are namespaced](#nested-resource-classes-are-namespaced) | You type-hint nested classes such as `Seam\Resources\DeviceProperties` | -| [Missing required parameters fail locally](#missing-required-parameters-fail-locally) | You call endpoints with missing parameters and rely on the server's 400 response | -| [Preferred HTTP methods and URL search params](#endpoints-use-preferred-http-methods) | You inspect traffic in a proxy, mock server, or firewall rules | -| [Error handling refinements](#error-handling-refinements) | You compare `getRequestId()` to `""`, or depend on 3xx responses passing through | -| [Pagination metadata is a `Seam\Pagination`](#pagination-metadata-is-a-typed-object) | You treat the paginator's metadata as a `stdClass` | -| [`Seam\Version` replaces `Seam\Utils\PackageVersion`](#seamversion-replaces-packageversion) | You read the package version programmatically | +| Change | Affects you if... | +| ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| [PHP 8.2+ required](#php-82-or-later-is-required) | You run PHP 8.0 or 8.1 | +| [`Seam\Seam` replaces `Seam\SeamClient`](#seamseam-replaces-seamseamclient) | You construct the client (everyone) | +| [`$seam->client` is the Guzzle client](#seam-client-is-the-guzzle-client) | You use `$seam->client`, `$seam->request()`, or the removed public properties | +| [Requests are retried and time out sooner](#requests-are-retried-and-time-out-sooner) | You depend on requests never being retried, or on the 60-second timeout | +| [`poll_until_ready` is replaced](#poll_until_ready-is-replaced-by-wait_for_action_attempt) | You call `$seam->action_attempts->poll_until_ready()` or rely on its 20 s/0.4 s timing | +| [Nested resource classes are namespaced](#nested-resource-classes-are-namespaced) | You type-hint nested classes such as `Seam\Resources\DeviceProperties` | +| [Resource constructors take required properties first](#resource-constructors-take-required-properties-first) | You construct a resource positionally rather than with named arguments | +| [Missing required parameters fail locally](#missing-required-parameters-fail-locally) | You call endpoints with missing parameters and rely on the server's 400 response | +| [Preferred HTTP methods and URL search params](#endpoints-use-preferred-http-methods) | You inspect traffic in a proxy, mock server, or firewall rules | +| [Error handling refinements](#error-handling-refinements) | You compare `getRequestId()` to `""`, or depend on 3xx responses passing through | +| [Pagination metadata is a `Seam\Pagination`](#pagination-metadata-is-a-typed-object) | You treat the paginator's metadata as a `stdClass` | +| [`Seam\Version` replaces `Seam\Utils\PackageVersion`](#seamversion-replaces-packageversion) | You read the package version programmatically | ## PHP 8.2 or later is required @@ -131,6 +132,20 @@ This rename also fixes a class of bugs where two nested shapes competed for one Property reads are unaffected — only explicit references to the nested class names need updating. +## Resource constructors take required properties first + +In v3, resource constructor parameters were ordered by property name alone. In v4 required properties come first and optional ones follow, alphabetical within each group. + +```php +// v3 +new Seam\Resources\AcsUser($access_schedule, $acs_system_id, ...); + +// v4 +new Seam\Resources\AcsUser($acs_system_id, $acs_user_id, ..., access_schedule: $schedule); +``` + +This affects only positional construction. Reading properties and `from_json` are unaffected. Construct resources with [named arguments](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments). + ## Missing required parameters fail locally In v3, every endpoint parameter defaulted to `null`, so a call missing a required parameter was sent to the server and failed with `Seam\HttpInvalidInputError` after a round trip. In v4, required parameters have no default, so PHP itself rejects the call with an `ArgumentCountError` (or an `Error` for a missing named argument). Endpoints that require _at least one_ of their parameters throw `InvalidArgumentException` when called with none: diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index f192cdfa..03ea8c16 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -3,9 +3,10 @@ // into one braced namespace block per namespace. Each class contributes its // from_json body lines and constructor parameter lines. // -// The blueprint does not track which resource properties are required, so -// every property is optional: from_json falls back to null for missing values -// and the constructor parameters are nullable. +// Whether a property is required is carried on isOptional: an optional one +// gets a null default, a required one does not. Constructor parameters are +// emitted required first, since PHP deprecates an optional parameter +// declared before a required one. import type { ResourceClassProperty, @@ -105,16 +106,25 @@ const getClassLayoutContext = ( a.name.localeCompare(b.name), ) + const parameterOrder = sortRequiredFirst(sorted) + return { className: schema.name, description: schema.description, isDeprecated: schema.isDeprecated, deprecationMessage: schema.deprecationMessage, fromJsonProps: sorted.map(generateFromJsonProp), - constructorParams: sorted.map(generateConstructorParam), + constructorParams: parameterOrder.map(generateConstructorParam), } } +const sortRequiredFirst = ( + properties: ResourceClassProperty[], +): ResourceClassProperty[] => [ + ...properties.filter(({ isOptional }) => !isOptional), + ...properties.filter(({ isOptional }) => isOptional), +] + export const setResourceLayoutContext = ( resource: ResourceSchema, ): ResourceLayoutContext => { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f29c67a2..d086a803 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,6 +6,7 @@ cacheDirectory=".phpunit.cache" colors="true" beStrictAboutTestsThatDoNotTestAnything="true" + failOnDeprecation="true" failOnRisky="true" failOnWarning="true" > diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index b003257f..bbdb20bc 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -87,22 +87,10 @@ public function __construct( * Unique identifier for the device associated with the access code. */ public string|null $device_id, - /** - * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. - */ - public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, - /** - * Date and time after which the time-bound access code becomes inactive. - */ - public string|null $ends_at = null, /** * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). */ public array $errors, - /** - * Indicates whether the access code is a backup code. - */ - public bool|null $is_backup = null, /** * Indicates whether a backup access code is available for use if the primary access code is lost or compromised. */ @@ -123,14 +111,6 @@ public function __construct( * Indicates whether the access code can only be used once. If `true`, the code becomes invalid after the first use. */ public bool|null $is_one_time_use, - /** - * Indicates whether the code is set on the device according to a preconfigured schedule. - */ - public bool|null $is_scheduled_on_device = null, - /** - * Indicates whether the access code is waiting for a code assignment. - */ - public bool|null $is_waiting_for_code_assignment = null, /** * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). */ @@ -139,14 +119,6 @@ public function __construct( * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. */ public array $pending_mutations, - /** - * Identifier of the pulled backup access code. Used to associate the pulled backup access code with the original access code. - */ - public string|null $pulled_backup_access_code_id = null, - /** - * Date and time at which the time-bound access code becomes active. - */ - public string|null $starts_at = null, /** * Current status of the access code within the operational lifecycle. Values are `setting`, a transitional phase that indicates that the code is being configured or activated; `set`, which indicates that the code is active and operational; `unset`, which indicates a deactivated or unused state, either before activation or after deliberate deactivation; `removing`, which indicates a transitional period in which the code is being deleted or made inactive; and `unknown`, which indicates an indeterminate state, due to reasons such as system errors or incomplete data, that highlights a potential need for system review or troubleshooting. See also [Lifecycle of Access Codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes). */ @@ -163,6 +135,34 @@ public function __construct( * Unique identifier for the Seam workspace associated with the access code. */ public string|null $workspace_id, + /** + * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. + */ + public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, + /** + * Date and time after which the time-bound access code becomes inactive. + */ + public string|null $ends_at = null, + /** + * Indicates whether the access code is a backup code. + */ + public bool|null $is_backup = null, + /** + * Indicates whether the code is set on the device according to a preconfigured schedule. + */ + public bool|null $is_scheduled_on_device = null, + /** + * Indicates whether the access code is waiting for a code assignment. + */ + public bool|null $is_waiting_for_code_assignment = null, + /** + * Identifier of the pulled backup access code. Used to associate the pulled backup access code with the original access code. + */ + public string|null $pulled_backup_access_code_id = null, + /** + * Date and time at which the time-bound access code becomes active. + */ + public string|null $starts_at = null, ) {} } } @@ -258,14 +258,6 @@ public static function from_json(mixed $json): Errors|null } public function __construct( - /** - * 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. - */ - public string|null $change_type = null, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at = null, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ @@ -274,20 +266,28 @@ public function __construct( * Indicates that this is an access code error. */ public true|null $is_access_code_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * 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. + */ + public string|null $change_type = null, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ public bool|null $is_bridge_error = null, - public bool|null $is_connected_account_error, - public bool|null $is_device_error, /** * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. */ public string|null $managed_access_code_id = null, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, /** * List of fields that were changed externally, with their previous and new values. */ @@ -365,6 +365,14 @@ public static function from_json(mixed $json): Warnings|null } public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, /** * 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. */ @@ -373,18 +381,10 @@ public function __construct( * Date and time at which Seam created the warning. */ public string|null $created_at = null, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, /** * List of fields that were changed externally, with their previous and new values. */ public array|null $modified_fields = null, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, ) {} } } diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index 9d2d9448..c2b7ae5c 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -53,26 +53,14 @@ public function __construct( * ID of the Access Grant. */ public string|null $access_grant_id, - /** - * Unique key for the access grant within the workspace. - */ - public string|null $access_grant_key = null, /** * IDs of the access methods created for the Access Grant. */ public array|null $access_method_ids, - /** - * Client Session Token. Only returned if the Access Grant has a mobile_key access method. - */ - public string|null $client_session_token = null, /** * Date and time at which the Access Grant was created. */ public string|null $created_at, - /** - * ID of the customization profile associated with the Access Grant. - */ - public string|null $customization_profile_id = null, /** * Display name of the Access Grant. */ @@ -85,10 +73,6 @@ public function __construct( * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). */ public array $errors, - /** - * Instant Key URL. Only returned if the Access Grant has a single mobile_key access_method. - */ - public string|null $instant_key_url = null, /** * @deprecated Use `space_ids`. */ @@ -105,10 +89,6 @@ public function __construct( * Access methods that the user requested for the Access Grant. */ public array $requested_access_methods, - /** - * Reservation key for the access grant. - */ - public string|null $reservation_key = null, /** * IDs of the spaces to which the Access Grant gives access. */ @@ -129,6 +109,26 @@ public function __construct( * ID of the Seam workspace associated with the Access Grant. */ public string|null $workspace_id, + /** + * Unique key for the access grant within the workspace. + */ + public string|null $access_grant_key = null, + /** + * Client Session Token. Only returned if the Access Grant has a mobile_key access method. + */ + public string|null $client_session_token = null, + /** + * ID of the customization profile associated with the Access Grant. + */ + public string|null $customization_profile_id = null, + /** + * Instant Key URL. Only returned if the Access Grant has a single mobile_key access_method. + */ + public string|null $instant_key_url = null, + /** + * Reservation key for the access grant. + */ + public string|null $reservation_key = null, ) {} } } @@ -239,10 +239,6 @@ public static function from_json( } public function __construct( - /** - * Specific PIN code to use for this access method. Only applicable when mode is 'code'. - */ - public string|null $code = null, /** * IDs of the access methods created for the requested access method. */ @@ -255,14 +251,18 @@ public function __construct( * Display name of the access method. */ public string|null $display_name, - /** - * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. - */ - public int|null $instant_key_max_use_count = null, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ public string|null $mode, + /** + * Specific PIN code to use for this access method. Only applicable when mode is 'code'. + */ + public string|null $code = null, + /** + * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. + */ + public int|null $instant_key_max_use_count = null, ) {} } @@ -302,10 +302,6 @@ public function __construct( */ public string|null $created_at, public string|null $device_id, - /** - * 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). - */ - public array|null $failed_devices = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -326,6 +322,10 @@ public function __construct( * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ public string|null $warning_code, + /** + * 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). + */ + public array|null $failed_devices = null, ) {} } } @@ -377,10 +377,6 @@ public static function from_json(mixed $json): To|null } public function __construct( - /** - * Common code key to ensure PIN code reuse across devices. - */ - public string|null $common_code_key = null, /** * New device IDs where access codes should be created. */ @@ -393,6 +389,10 @@ public function __construct( * New start time for access. */ public string|null $starts_at, + /** + * Common code key to ensure PIN code reuse across devices. + */ + public string|null $common_code_key = null, ) {} } } diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index f7946c5b..f6ae0845 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -48,22 +48,10 @@ public function __construct( * ID of the access method. */ public string|null $access_method_id, - /** - * Token of the client session associated with the access method. - */ - public string|null $client_session_token = null, - /** - * The actual PIN code for code access methods. - */ - public string|null $code = null, /** * Date and time at which the access method was created. */ public string|null $created_at, - /** - * ID of the customization profile associated with the access method. - */ - public string|null $customization_profile_id = null, /** * Display name of the access method. */ @@ -72,30 +60,10 @@ public function __construct( * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). */ public array $errors, - /** - * URL of the Instant Key for mobile key access methods. - */ - public string|null $instant_key_url = null, - /** - * 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. - */ - public bool|null $is_assignment_required = null, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. - */ - public bool|null $is_encoding_required = null, /** * Indicates whether the access method has been issued. */ public bool|null $is_issued, - /** - * 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. - */ - public bool|null $is_ready_for_assignment = null, - /** - * 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. - */ - public bool|null $is_ready_for_encoding = null, /** * Date and time at which the access method was issued. */ @@ -116,6 +84,38 @@ public function __construct( * ID of the Seam workspace associated with the access method. */ public string|null $workspace_id, + /** + * Token of the client session associated with the access method. + */ + public string|null $client_session_token = null, + /** + * The actual PIN code for code access methods. + */ + public string|null $code = null, + /** + * ID of the customization profile associated with the access method. + */ + public string|null $customization_profile_id = null, + /** + * URL of the Instant Key for mobile key access methods. + */ + public string|null $instant_key_url = null, + /** + * 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. + */ + public bool|null $is_assignment_required = null, + /** + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + */ + public bool|null $is_encoding_required = null, + /** + * 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. + */ + public bool|null $is_ready_for_assignment = null, + /** + * 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. + */ + public bool|null $is_ready_for_encoding = null, ) {} } } @@ -220,14 +220,14 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ public string|null $warning_code, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id = null, ) {} } } diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index 9507728e..fb38046e 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -59,10 +59,6 @@ public function __construct( * @deprecated Use `external_type_display_name`. */ public string|null $access_group_type_display_name, - /** - * `starts_at` and `ends_at` timestamps for the access group's access. - */ - public AcsAccessGroup\AccessSchedule|null $access_schedule = null, /** * ID of the access group. */ @@ -115,6 +111,10 @@ public function __construct( * ID of the workspace that contains the access group. */ public string|null $workspace_id, + /** + * `starts_at` and `ends_at` timestamps for the access group's access. + */ + public AcsAccessGroup\AccessSchedule|null $access_schedule = null, ) {} } } @@ -293,14 +293,14 @@ public function __construct( * Ending time for the access schedule. */ public string|null $ends_at, - /** - * Name of the access group. - */ - public string|null $name = null, /** * Starting time for the access schedule. */ public string|null $starts_at, + /** + * Name of the access group. + */ + public string|null $name = null, ) {} } @@ -333,14 +333,14 @@ public function __construct( * Ending time for the access schedule. */ public string|null $ends_at, - /** - * Name of the access group. - */ - public string|null $name = null, /** * Starting time for the access schedule. */ public string|null $starts_at, + /** + * Name of the access group. + */ + public string|null $name = null, ) {} } } diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index 9ae96219..e18bc0bc 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -84,14 +84,42 @@ public function __construct( * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $acs_credential_id, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $acs_system_id, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $display_name, + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $errors, + /** + * Indicates whether Seam manages the credential. + */ + public true|null $is_managed, + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $warnings, + /** + * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $workspace_id, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id = null, /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -112,26 +140,10 @@ public function __construct( * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $code = null, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $display_name, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ public string|null $ends_at = null, - /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $errors, /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ @@ -148,10 +160,6 @@ public function __construct( * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ public bool|null $is_latest_desired_state_synced_with_provider = null, - /** - * Indicates whether Seam manages the credential. - */ - public true|null $is_managed, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ @@ -184,14 +192,6 @@ public function __construct( * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public AcsCredential\VisionlineMetadata|null $visionline_metadata = null, - /** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $warnings, - /** - * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $workspace_id, ) {} } } diff --git a/src/Resources/AcsEntrance.php b/src/Resources/AcsEntrance.php index 9c20b698..116c81c7 100644 --- a/src/Resources/AcsEntrance.php +++ b/src/Resources/AcsEntrance.php @@ -110,6 +110,30 @@ public function __construct( * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ public string|null $acs_system_id, + /** + * ID of the [connected account](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $connected_account_id, + /** + * Date and time at which the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) was created. + */ + public string|null $created_at, + /** + * Display name for the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $display_name, + /** + * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public array $errors, + /** + * IDs of the spaces that the entrance is in. + */ + public array|null $space_ids, + /** + * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public array $warnings, /** * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -146,18 +170,6 @@ public function __construct( * Indicates whether the ACS entrance can be unlocked with mobile key credentials. */ public bool|null $can_unlock_with_mobile_key = null, - /** - * ID of the [connected account](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $connected_account_id, - /** - * Date and time at which the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) was created. - */ - public string|null $created_at, - /** - * Display name for the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $display_name, /** * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -166,10 +178,6 @@ public function __construct( * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ public AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata = null, - /** - * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public array $errors, /** * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -190,18 +198,10 @@ public function __construct( * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ public AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata = null, - /** - * IDs of the spaces that the entrance is in. - */ - public array|null $space_ids, /** * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ public AcsEntrance\VisionlineMetadata|null $visionline_metadata = null, - /** - * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public array $warnings, ) {} } } diff --git a/src/Resources/AcsSystem.php b/src/Resources/AcsSystem.php index 642d242d..2bbc80c4 100644 --- a/src/Resources/AcsSystem.php +++ b/src/Resources/AcsSystem.php @@ -55,18 +55,10 @@ public static function from_json(mixed $json): AcsSystem|null } public function __construct( - /** - * Number of access groups in the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public float|null $acs_access_group_count = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ public string|null $acs_system_id, - /** - * Number of users in the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public float|null $acs_user_count = null, /** * ID of the connected account associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -81,22 +73,10 @@ public function __construct( * Date and time at which the [access control system](https://docs.seam.co/low-level-apis/access-systems) was created. */ public string|null $created_at, - /** - * ID of the default credential manager `acs_system` for this [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $default_credential_manager_acs_system_id = null, /** * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ public array $errors, - /** - * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. - */ - public string|null $external_type = null, - /** - * Display name that corresponds to the brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. - */ - public string|null $external_type_display_name = null, /** * Alternative text for the [access control system](https://docs.seam.co/low-level-apis/access-systems) image. */ @@ -117,6 +97,34 @@ public function __construct( * Name of the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ public string|null $name, + /** + * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public array $warnings, + /** + * ID of the workspace that contains the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $workspace_id, + /** + * Number of access groups in the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public float|null $acs_access_group_count = null, + /** + * Number of users in the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public float|null $acs_user_count = null, + /** + * ID of the default credential manager `acs_system` for this [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $default_credential_manager_acs_system_id = null, + /** + * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. + */ + public string|null $external_type = null, + /** + * Display name that corresponds to the brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. + */ + public string|null $external_type_display_name = null, /** * @deprecated Use `external_type`. */ @@ -129,14 +137,6 @@ public function __construct( * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ public AcsSystem\VisionlineMetadata|null $visionline_metadata = null, - /** - * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public array $warnings, - /** - * ID of the workspace that contains the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $workspace_id, ) {} } } @@ -169,14 +169,14 @@ public function __construct( * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ public string|null $error_code, - /** - * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error = null, ) {} } @@ -262,14 +262,14 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - /** - * @deprecated this field is deprecated. - */ - public array|null $misconfigured_acs_entrance_ids = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ public string|null $warning_code, + /** + * @deprecated this field is deprecated. + */ + public array|null $misconfigured_acs_entrance_ids = null, ) {} } } diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index 1f0559e4..4ed77aa4 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -67,10 +67,6 @@ public static function from_json(mixed $json): AcsUser|null } public function __construct( - /** - * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. - */ - public AcsUser\AccessSchedule|null $access_schedule = null, /** * ID of the [access system](https://docs.seam.co/low-level-apis/access-systems) that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ @@ -91,6 +87,26 @@ public function __construct( * Display name for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ public string|null $display_name, + /** + * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public array $errors, + /** + * Indicates whether Seam manages the access system user. + */ + public true|null $is_managed, + /** + * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public array $warnings, + /** + * ID of the workspace that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $workspace_id, + /** + * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. + */ + public AcsUser\AccessSchedule|null $access_schedule = null, /** * @deprecated use email_address. */ @@ -99,10 +115,6 @@ public function __construct( * Email address of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ public string|null $email_address = null, - /** - * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public array $errors, /** * Brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. */ @@ -119,10 +131,6 @@ public function __construct( * ID of the HID access control system associated with the user. */ public string|null $hid_acs_system_id = null, - /** - * Indicates whether Seam manages the access system user. - */ - public true|null $is_managed, /** * Indicates whether the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is currently [suspended](https://docs.seam.co/low-level-apis/access-systems/user-management/suspending-and-unsuspending-users). */ @@ -159,14 +167,6 @@ public function __construct( * Phone number of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). */ public string|null $user_identity_phone_number = null, - /** - * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public array $warnings, - /** - * ID of the workspace that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $workspace_id, ) {} } } @@ -271,15 +271,15 @@ public function __construct( */ public string|null $message, public string|null $mutation_code, - /** - * Optional: When the user creation is scheduled to occur. - */ - public string|null $scheduled_at = null, public PendingMutations\To|null $to, /** * Whether the user is scheduled to be added to or removed from the access group. */ public string|null $variant, + /** + * Optional: When the user creation is scheduled to occur. + */ + public string|null $scheduled_at = null, ) {} } @@ -393,26 +393,26 @@ public function __construct( */ public string|null $acs_credential_id, /** - * Email address of the access system user. + * Starting time for the access schedule. */ - public string|null $email_address = null, + public string|null $ends_at, + public bool|null $is_suspended, /** * Starting time for the access schedule. */ - public string|null $ends_at, + public string|null $starts_at, + /** + * Email address of the access system user. + */ + public string|null $email_address = null, /** * Full name of the access system user. */ public string|null $full_name = null, - public bool|null $is_suspended, /** * Phone number of the access system user. */ public string|null $phone_number = null, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, ) {} } @@ -445,26 +445,26 @@ public function __construct( */ public string|null $acs_credential_id, /** - * Email address of the access system user. + * Starting time for the access schedule. */ - public string|null $email_address = null, + public string|null $ends_at, + public bool|null $is_suspended, /** * Starting time for the access schedule. */ - public string|null $ends_at, + public string|null $starts_at, + /** + * Email address of the access system user. + */ + public string|null $email_address = null, /** * Full name of the access system user. */ public string|null $full_name = null, - public bool|null $is_suspended, /** * Phone number of the access system user. */ public string|null $phone_number = null, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, ) {} } } diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 73dd218a..783e7d1d 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -183,14 +183,39 @@ public function __construct( * Corresponding credential data as stored on Seam and the access system. */ public Result\AcsCredentialOnSeam|null $acs_credential_on_seam, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $acs_system_id, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + public string|null $created_at, + public string|null $display_name, + public array $errors, + /** + * Indicates whether Seam manages the credential. + */ + public bool|null $is_managed, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + /** + * @var array|\stdClass|null + */ + public array|\stdClass|null $noise_threshold, + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + public array $pending_mutations, + public array $warnings, + public string|null $workspace_id, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id = null, /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -212,21 +237,14 @@ public function __construct( */ public string|null $client_session_token = null, public string|null $code = null, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - public string|null $created_at, /** * ID of the customization profile associated with the access method. */ public string|null $customization_profile_id = null, - public string|null $display_name, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ public string|null $ends_at = null, - public array $errors, /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ @@ -252,10 +270,6 @@ public function __construct( * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ public bool|null $is_latest_desired_state_synced_with_provider = null, - /** - * Indicates whether Seam manages the credential. - */ - public bool|null $is_managed, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ @@ -277,22 +291,10 @@ public function __construct( * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. */ public string|null $latest_desired_state_synced_with_provider_at = null, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - /** - * @var array|\stdClass|null - */ - public array|\stdClass|null $noise_threshold, /** * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $parent_acs_credential_id = null, - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - public array $pending_mutations, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. */ @@ -305,9 +307,7 @@ public function __construct( * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public Result\VisionlineMetadata|null $visionline_metadata = null, - public array $warnings, public bool|null $was_confirmed_by_device = null, - public string|null $workspace_id, ) {} } } @@ -443,14 +443,39 @@ public function __construct( * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $acs_credential_id, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $acs_system_id, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $display_name, + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $errors, + public bool|null $is_managed, + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $warnings, + /** + * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $workspace_id, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id = null, /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -471,26 +496,10 @@ public function __construct( * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $code = null, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $display_name, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ public string|null $ends_at = null, - /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $errors, /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ @@ -507,7 +516,6 @@ public function __construct( * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ public bool|null $is_latest_desired_state_synced_with_provider = null, - public bool|null $is_managed, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ @@ -540,14 +548,6 @@ public function __construct( * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata = null, - /** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $warnings, - /** - * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $workspace_id, ) {} } @@ -786,6 +786,11 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + public string|null $warning_code, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $warning_message, /** * The PIN code that was assigned instead. */ @@ -798,11 +803,6 @@ public function __construct( * The originally requested PIN code that could not be used. */ public string|null $original_code = null, - public string|null $warning_code, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $warning_message, ) {} } } @@ -1073,6 +1073,10 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, /** * The PIN code that was assigned instead. */ @@ -1081,10 +1085,6 @@ public function __construct( * The originally requested PIN code that could not be used. */ public string|null $original_code = null, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, ) {} } } diff --git a/src/Resources/ClientSession.php b/src/Resources/ClientSession.php index 88083915..f481f7c6 100644 --- a/src/Resources/ClientSession.php +++ b/src/Resources/ClientSession.php @@ -52,10 +52,6 @@ public function __construct( * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) was created. */ public string|null $created_at, - /** - * Customer key associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public string|null $customer_key = null, /** * Number of devices associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). */ @@ -72,10 +68,6 @@ public function __construct( * Your user ID for the user associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). */ public string|null $user_identifier_key, - /** - * ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. - */ - public string|null $user_identity_id = null, /** * IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. * @@ -86,6 +78,14 @@ public function __construct( * ID of the workspace associated with the client session. */ public string|null $workspace_id, + /** + * Customer key associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public string|null $customer_key = null, + /** + * ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. + */ + public string|null $user_identity_id = null, ) {} } } diff --git a/src/Resources/ConnectWebview.php b/src/Resources/ConnectWebview.php index f4cb12d0..494aadc3 100644 --- a/src/Resources/ConnectWebview.php +++ b/src/Resources/ConnectWebview.php @@ -94,10 +94,6 @@ public function __construct( * URL to which the Connect Webview should redirect when the user successfully pairs a device or system. If you do not set the `custom_redirect_failure_url`, the Connect Webview redirects to the `custom_redirect_url` when an unexpected error occurs. */ public string|null $custom_redirect_url, - /** - * The customer key associated with this webview, if any. - */ - public string|null $customer_key = null, /** * Device selection mode of the Connect Webview. Supported values: `none`, `single`, `multiple`. */ @@ -126,6 +122,10 @@ public function __construct( * ID of the workspace that contains the Connect Webview. */ public string|null $workspace_id, + /** + * The customer key associated with this webview, if any. + */ + public string|null $customer_key = null, ) {} } } diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index a7f5085c..77d3076f 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -50,10 +50,6 @@ public function __construct( * List of capabilities that were accepted during the account connection process. */ public array|null $accepted_capabilities, - /** - * Type of connected account. - */ - public string|null $account_type = null, /** * Display name for the connected account type. */ @@ -66,16 +62,32 @@ public function __construct( * ID of the connected account. */ public string|null $connected_account_id, - /** - * Date and time at which the connected account was created. - */ - public string|null $created_at = null, /** * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. * * @var array|\stdClass|null */ public array|\stdClass|null $custom_metadata, + /** + * Display name for the connected account. + */ + public string|null $display_name, + /** + * Errors associated with the connected account. + */ + public array $errors, + /** + * Warnings associated with the connected account. + */ + public array $warnings, + /** + * Type of connected account. + */ + public string|null $account_type = null, + /** + * Date and time at which the connected account was created. + */ + public string|null $created_at = null, /** * Your unique key for the customer associated with this connected account. */ @@ -88,14 +100,6 @@ public function __construct( * Default reservation check-out time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration. */ public string|null $default_checkout_time = null, - /** - * Display name for the connected account. - */ - public string|null $display_name, - /** - * Errors associated with the connected account. - */ - public array $errors, /** * For iCal connected accounts, the platform that produced the feed (for example, `airbnb`, `vrbo`, or `booking`), or `unknown` when it could not be determined. Intended for rendering the source platform's logo. */ @@ -118,10 +122,6 @@ public function __construct( * @deprecated Use `display_name` instead. */ public ConnectedAccount\UserIdentifier|null $user_identifier = null, - /** - * Warnings associated with the connected account. - */ - public array $warnings, ) {} } } @@ -161,14 +161,6 @@ public function __construct( * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error = null, - /** - * Indicates whether the error is related specifically to the connected account. - */ - public bool|null $is_connected_account_error = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -177,6 +169,14 @@ public function __construct( * Salto KS metadata associated with the connected account that has an error. */ public Errors\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error = null, + /** + * Indicates whether the error is related specifically to the connected account. + */ + public bool|null $is_connected_account_error = null, ) {} } diff --git a/src/Resources/Device.php b/src/Resources/Device.php index 93bd4b51..ee7ed893 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -79,6 +79,60 @@ public static function from_json(mixed $json): Device|null } public function __construct( + /** + * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). + */ + public array|null $capabilities_supported, + /** + * Unique identifier for the account associated with the device. + */ + public string|null $connected_account_id, + /** + * Date and time at which the device object was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $custom_metadata, + /** + * ID of the device. + */ + public string|null $device_id, + /** + * Type of the device. + */ + public string|null $device_type, + /** + * Display name of the device, defaults to nickname (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the device easily, especially when there are numerous devices. + */ + public string|null $display_name, + /** + * 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. + */ + public array $errors, + /** + * Indicates whether Seam manages the device. See also [Managed and Unmanaged Devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). + */ + public true|null $is_managed, + /** + * Properties of the device. + */ + public Device\Properties|null $properties, + /** + * IDs of the spaces the device is in. + */ + public array|null $space_ids, + /** + * 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. + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the device. + */ + public string|null $workspace_id, /** * Indicates whether the lock supports configuring automatic locking. */ @@ -159,28 +213,6 @@ public function __construct( * Indicates whether the lock supports unlocking with an access code. */ public bool|null $can_unlock_with_code = null, - /** - * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). - */ - public array|null $capabilities_supported, - /** - * Unique identifier for the account associated with the device. - */ - public string|null $connected_account_id, - /** - * Date and time at which the device object was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - * - * @var array|\stdClass|null - */ - public array|\stdClass|null $custom_metadata, - /** - * ID of the device. - */ - public string|null $device_id, /** * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. */ @@ -189,22 +221,6 @@ public function __construct( * Provider of the device. Represents the third-party service through which the device is controlled. */ public Device\DeviceProvider|null $device_provider = null, - /** - * Type of the device. - */ - public string|null $device_type, - /** - * Display name of the device, defaults to nickname (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the device easily, especially when there are numerous devices. - */ - public string|null $display_name, - /** - * 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. - */ - public array $errors, - /** - * Indicates whether Seam manages the device. See also [Managed and Unmanaged Devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). - */ - public true|null $is_managed, /** * Location information for the device. */ @@ -213,22 +229,6 @@ public function __construct( * Optional nickname to describe the device, settable through Seam. */ public string|null $nickname = null, - /** - * Properties of the device. - */ - public Device\Properties|null $properties, - /** - * IDs of the spaces the device is in. - */ - public array|null $space_ids, - /** - * 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. - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the device. - */ - public string|null $workspace_id, ) {} } } @@ -256,14 +256,14 @@ public function __construct( * Display name for the manufacturer, such as `August`, `Yale`, `Salto`, and so on. */ public string|null $display_name, - /** - * Image URL for the manufacturer logo. - */ - public string|null $image_url = null, /** * Manufacturer identifier, such as `august`, `yale`, `salto`, and so on. */ public string|null $manufacturer, + /** + * Image URL for the manufacturer logo. + */ + public string|null $image_url = null, ) {} } @@ -294,14 +294,14 @@ public function __construct( * Display name for the device provider type. */ public string|null $display_name, - /** - * Image URL for the device provider. - */ - public string|null $image_url = null, /** * Provider category. Indicates the third-party provider type, such as `stable`, for stable integrations, or `internal`, for internal integrations. */ public string|null $provider_category, + /** + * Image URL for the device provider. + */ + public string|null $image_url = null, ) {} } @@ -335,16 +335,16 @@ public function __construct( * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error = null, ) {} } @@ -728,6 +728,24 @@ public static function from_json(mixed $json): Properties|null } public function __construct( + /** + * Appearance-related properties, as reported by the device. + */ + public Properties\Appearance|null $appearance, + /** + * Device model-related properties. + */ + public Properties\Model|null $model, + /** + * Name of the device. + * + * @deprecated use device.display_name instead + */ + public string|null $name, + /** + * Indicates whether the device is online. + */ + public bool|null $online, /** * Accessory keypad properties and state. */ @@ -746,10 +764,6 @@ public function __construct( * Metadata for an Akiles device. */ public Properties\AkilesMetadata|null $akiles_metadata = null, - /** - * Appearance-related properties, as reported by the device. - */ - public Properties\Appearance|null $appearance, /** * Metadata for an Aqara device. */ @@ -990,16 +1004,6 @@ public function __construct( * Metadata for a Minut device. */ public Properties\MinutMetadata|null $minut_metadata = null, - /** - * Device model-related properties. - */ - public Properties\Model|null $model, - /** - * Name of the device. - * - * @deprecated use device.display_name instead - */ - public string|null $name, /** * Metadata for a Google Nest device. */ @@ -1030,10 +1034,6 @@ public function __construct( * Metadata for an Omnitec device. */ public Properties\OmnitecMetadata|null $omnitec_metadata = null, - /** - * Indicates whether the device is online. - */ - public bool|null $online, /** * Indicates whether it is currently possible to use online access codes for the device. * @@ -1227,14 +1227,14 @@ public static function from_json(mixed $json): AccessoryKeypad|null } public function __construct( - /** - * Keypad battery properties. - */ - public AccessoryKeypad\Battery|null $battery = null, /** * Indicates if an accessory keypad is connected to the device. */ public bool|null $is_connected, + /** + * Keypad battery properties. + */ + public AccessoryKeypad\Battery|null $battery = null, ) {} } @@ -1314,6 +1314,14 @@ public static function from_json(mixed $json): Model|null } public function __construct( + /** + * Display name of the device model. + */ + public string|null $display_name, + /** + * Display name that corresponds to the manufacturer-specific terminology for the device. + */ + public string|null $manufacturer_display_name, /** * @deprecated use device.properties.model.can_connect_accessory_keypad */ @@ -1322,18 +1330,10 @@ public function __construct( * Indicates whether the device can connect a accessory keypad. */ public bool|null $can_connect_accessory_keypad = null, - /** - * Display name of the device model. - */ - public string|null $display_name, /** * Indicates whether the device has a built in accessory keypad. */ public bool|null $has_built_in_keypad = null, - /** - * Display name that corresponds to the manufacturer-specific terminology for the device. - */ - public string|null $manufacturer_display_name, /** * @deprecated use device.can_program_offline_access_codes. */ @@ -3320,14 +3320,6 @@ public function __construct( * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ public array $errors, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. - */ - public bool|null $is_override_allowed = null, - /** - * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - */ - public int|null $max_override_period_minutes = null, /** * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ @@ -3344,6 +3336,14 @@ public function __construct( * ID of the workspace that contains the thermostat schedule. */ public string|null $workspace_id, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. + */ + public bool|null $is_override_allowed = null, + /** + * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + */ + public int|null $max_override_period_minutes = null, ) {} } @@ -3403,6 +3403,16 @@ public function __construct( * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ public string|null $climate_preset_key, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed, /** * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. */ @@ -3415,10 +3425,6 @@ public function __construct( * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ public float|null $cooling_set_point_fahrenheit = null, - /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $display_name, /** * Metadata specific to the Ecobee climate, if applicable. */ @@ -3439,12 +3445,6 @@ public function __construct( * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ public string|null $hvac_mode_setting = null, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' - */ - public bool|null $manual_override_allowed, /** * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ diff --git a/src/Resources/DeviceProvider.php b/src/Resources/DeviceProvider.php index 044ae59c..41b60ac9 100644 --- a/src/Resources/DeviceProvider.php +++ b/src/Resources/DeviceProvider.php @@ -47,6 +47,22 @@ public static function from_json(mixed $json): DeviceProvider|null } public function __construct( + /** + * Name of the device provider. + */ + public string|null $device_provider_name, + /** + * Display name for the device provider. + */ + public string|null $display_name, + /** + * Image URL for the device provider. + */ + public string|null $image_url, + /** + * List of provider categories to which the device provider belongs, such as `stable`, `consumer_smartlocks`, `thermostats`, and so on. + */ + public array|null $provider_categories, /** * Indicates whether the lock supports configuring automatic locking. */ @@ -127,22 +143,6 @@ public function __construct( * Indicates whether the lock supports unlocking with an access code. */ public bool|null $can_unlock_with_code = null, - /** - * Name of the device provider. - */ - public string|null $device_provider_name, - /** - * Display name for the device provider. - */ - public string|null $display_name, - /** - * Image URL for the device provider. - */ - public string|null $image_url, - /** - * List of provider categories to which the device provider belongs, such as `stable`, `consumer_smartlocks`, `thermostats`, and so on. - */ - public array|null $provider_categories, ) {} } } diff --git a/src/Resources/Event.php b/src/Resources/Event.php index d18aa79e..a7119251 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -149,10 +149,6 @@ public function __construct( */ public array $access_code_errors, public string|null $access_code_id, - /** - * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. - */ - public bool|null $access_code_is_managed = null, /** * Warnings associated with the access code. */ @@ -165,14 +161,6 @@ public function __construct( * IDs of the access grants associated with this access method. */ public array|null $access_grant_ids, - /** - * Key of the affected Access Grant (if present). - */ - public string|null $access_grant_key = null, - /** - * Keys of the access grants associated with this access method (if present). - */ - public array|null $access_grant_keys = null, /** * ID of the affected access method. */ @@ -228,14 +216,6 @@ public function __construct( * Battery status of the affected device, calculated from the numeric `battery_level` value. */ public string|null $battery_status, - /** - * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). - */ - public string|null $change_reason = null, - /** - * List of properties that changed on the access code. - */ - public array|null $changed_properties = null, /** * ID of the affected client session. */ @@ -245,11 +225,6 @@ public function __construct( */ public string|null $climate_preset_key, public string|null $code, - public string|null $connect_webview_id = null, - /** - * @var array|\stdClass|null - */ - public array|\stdClass|null $connected_account_custom_metadata = null, /** * Errors associated with the connected account. */ @@ -259,50 +234,24 @@ public function __construct( * Warnings associated with the connected account. */ public array $connected_account_warnings, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius = null, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit = null, /** * Date and time at which the event was created. */ public string|null $created_at, - public string|null $customer_key = null, /** * Human-readable description of the change and its source. */ public string|null $description, - /** - * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. - */ - public float|null $desired_temperature_celsius = null, - /** - * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. - */ - public float|null $desired_temperature_fahrenheit = null, - /** - * @var array|\stdClass|null - */ - public array|\stdClass|null $device_custom_metadata = null, /** * Errors associated with the device. */ public array $device_errors, public string|null $device_id, public array|null $device_ids, - public string|null $device_name = null, /** * Warnings associated with the device. */ public array $device_warnings, - /** - * The new end time for the access grant. - */ - public string|null $ends_at = null, /** * Error code associated with the disconnection event, if any. */ @@ -311,10 +260,6 @@ public function __construct( * Description of why the access methods could not be created. */ public string|null $error_message, - /** - * 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. - */ - public string|null $event_description = null, /** * ID of the event. */ @@ -323,11 +268,120 @@ public function __construct( * Type of the event. */ public string|null $event_type, + public Event\From|null $from, + /** + * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. + */ + public bool|null $is_fallback_climate_preset, + /** + * Lower temperature limit, in °C, defined by the set threshold. + */ + public float|null $lower_limit_celsius, + /** + * Lower temperature limit, in °F, defined by the set threshold. + */ + public float|null $lower_limit_fahrenheit, + public string|null $method, + /** + * Date and time at which the event occurred. + */ + public string|null $occurred_at, + /** + * Array of mutations requested on the access code, each containing the mutation type and from/to values. + */ + public array $requested_mutations, + /** + * ID of the affected space. + */ + public string|null $space_id, + /** + * Status of the action. + */ + public string|null $status, + /** + * Temperature, in °C, reported by the affected thermostat. + */ + public float|null $temperature_celsius, + /** + * Temperature, in °F, reported by the affected thermostat. + */ + public float|null $temperature_fahrenheit, + /** + * ID of the thermostat schedule that prompted the affected climate preset to be activated. + */ + public string|null $thermostat_schedule_id, + public Event\To|null $to, + /** + * Upper temperature limit, in °C, defined by the set threshold. + */ + public float|null $upper_limit_celsius, + /** + * Upper temperature limit, in °F, defined by the set threshold. + */ + public float|null $upper_limit_fahrenheit, + /** + * ID of the workspace associated with the event. + */ + public string|null $workspace_id, + /** + * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + */ + public bool|null $access_code_is_managed = null, + /** + * Key of the affected Access Grant (if present). + */ + public string|null $access_grant_key = null, + /** + * Keys of the access grants associated with this access method (if present). + */ + public array|null $access_grant_keys = null, + /** + * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). + */ + public string|null $change_reason = null, + /** + * List of properties that changed on the access code. + */ + public array|null $changed_properties = null, + public string|null $connect_webview_id = null, + /** + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius = null, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit = null, + public string|null $customer_key = null, + /** + * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. + */ + public float|null $desired_temperature_celsius = null, + /** + * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. + */ + public float|null $desired_temperature_fahrenheit = null, + /** + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + public string|null $device_name = null, + /** + * The new end time for the access grant. + */ + public string|null $ends_at = null, + /** + * 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. + */ + public string|null $event_description = null, /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ public string|null $fan_mode_setting = null, - public Event\From|null $from, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ @@ -345,21 +399,8 @@ public function __construct( * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). */ public bool|null $is_backup_code = null, - /** - * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. - */ - public bool|null $is_fallback_climate_preset, public bool|null $is_via_bluetooth = null, public bool|null $is_via_nfc = null, - /** - * Lower temperature limit, in °C, defined by the set threshold. - */ - public float|null $lower_limit_celsius, - /** - * Lower temperature limit, in °F, defined by the set threshold. - */ - public float|null $lower_limit_fahrenheit, - public string|null $method, /** * Metadata from Minut. * @@ -396,22 +437,10 @@ public function __construct( * @var array|\stdClass|null */ public array|\stdClass|null $noiseaware_metadata = null, - /** - * Date and time at which the event occurred. - */ - public string|null $occurred_at, /** * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. */ public Event\Reason|null $reason = null, - /** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. - */ - public array $requested_mutations, - /** - * ID of the affected space. - */ - public string|null $space_id, /** * Unique key for the space within the workspace. */ @@ -420,36 +449,7 @@ public function __construct( * The new start time for the access grant. */ public string|null $starts_at = null, - /** - * Status of the action. - */ - public string|null $status, - /** - * Temperature, in °C, reported by the affected thermostat. - */ - public float|null $temperature_celsius, - /** - * Temperature, in °F, reported by the affected thermostat. - */ - public float|null $temperature_fahrenheit, - /** - * ID of the thermostat schedule that prompted the affected climate preset to be activated. - */ - public string|null $thermostat_schedule_id, - public Event\To|null $to, - /** - * Upper temperature limit, in °C, defined by the set threshold. - */ - public float|null $upper_limit_celsius, - /** - * Upper temperature limit, in °F, defined by the set threshold. - */ - public float|null $upper_limit_fahrenheit, public string|null $video_url = null, - /** - * ID of the workspace associated with the event. - */ - public string|null $workspace_id, ) {} } } @@ -835,16 +835,16 @@ public static function from_json(mixed $json): RequestedMutations|null } public function __construct( + /** + * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. + */ + public string|null $mutation_code, /** * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. * * @var array|\stdClass|null */ public array|\stdClass|null $from = null, - /** - * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. - */ - public string|null $mutation_code, /** * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. * diff --git a/src/Resources/InstantKey.php b/src/Resources/InstantKey.php index f7a93404..f24095a0 100644 --- a/src/Resources/InstantKey.php +++ b/src/Resources/InstantKey.php @@ -38,14 +38,6 @@ public function __construct( * Date and time at which the Instant Key was created. */ public string|null $created_at, - /** - * Customization applied to the Instant Key UI. - */ - public InstantKey\Customization|null $customization = null, - /** - * ID of the customization profile associated with the Instant Key. - */ - public string|null $customization_profile_id = null, /** * Date and time at which the Instant Key expires. */ @@ -66,6 +58,14 @@ public function __construct( * ID of the workspace that contains the Instant Key. */ public string|null $workspace_id, + /** + * Customization applied to the Instant Key UI. + */ + public InstantKey\Customization|null $customization = null, + /** + * ID of the customization profile associated with the Instant Key. + */ + public string|null $customization_profile_id = null, ) {} } } diff --git a/src/Resources/NoiseThreshold.php b/src/Resources/NoiseThreshold.php index 0545ab0f..24d7e7e0 100644 --- a/src/Resources/NoiseThreshold.php +++ b/src/Resources/NoiseThreshold.php @@ -44,14 +44,14 @@ public function __construct( * Unique identifier for the noise threshold. */ public string|null $noise_threshold_id, - /** - * Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for [Noiseaware sensors](https://docs.seam.co/device-and-system-integration-guides/noiseaware-sensors). - */ - public float|null $noise_threshold_nrs = null, /** * Time at which the noise threshold should become active daily. */ public string|null $starts_daily_at, + /** + * Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for [Noiseaware sensors](https://docs.seam.co/device-and-system-integration-guides/noiseaware-sensors). + */ + public float|null $noise_threshold_nrs = null, ) {} } } diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index e73d8f3e..c56b8b5e 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -60,10 +60,6 @@ public function __construct( * Errors associated with the phone. */ public array $errors, - /** - * Optional nickname to describe the phone, settable through Seam. - */ - public string|null $nickname = null, /** * Properties of the phone. */ @@ -76,6 +72,10 @@ public function __construct( * ID of the workspace that contains the phone. */ public string|null $workspace_id, + /** + * Optional nickname to describe the phone, settable through Seam. + */ + public string|null $nickname = null, ) {} } } diff --git a/src/Resources/Space.php b/src/Resources/Space.php index c5acd6e5..f5d9a754 100644 --- a/src/Resources/Space.php +++ b/src/Resources/Space.php @@ -39,14 +39,6 @@ public function __construct( * Date and time at which the space was created. */ public string|null $created_at, - /** - * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). - */ - public Space\CustomerData|null $customer_data = null, - /** - * Customer key associated with the space. - */ - public string|null $customer_key = null, /** * Number of devices in the space. */ @@ -55,10 +47,6 @@ public function __construct( * Display name for the space. */ public string|null $display_name, - /** - * Geographic coordinates (latitude and longitude) of the space. - */ - public Space\Geolocation|null $geolocation = null, /** * Name of the space. */ @@ -67,14 +55,26 @@ public function __construct( * ID of the space. */ public string|null $space_id, - /** - * Unique key for the space within the workspace. - */ - public string|null $space_key = null, /** * ID of the workspace associated with the space. */ public string|null $workspace_id, + /** + * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). + */ + public Space\CustomerData|null $customer_data = null, + /** + * Customer key associated with the space. + */ + public string|null $customer_key = null, + /** + * Geographic coordinates (latitude and longitude) of the space. + */ + public Space\Geolocation|null $geolocation = null, + /** + * Unique key for the space within the workspace. + */ + public string|null $space_key = null, ) {} } } diff --git a/src/Resources/ThermostatSchedule.php b/src/Resources/ThermostatSchedule.php index e2bc5991..0ebf3370 100644 --- a/src/Resources/ThermostatSchedule.php +++ b/src/Resources/ThermostatSchedule.php @@ -51,14 +51,6 @@ public function __construct( * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ public array $errors, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. - */ - public bool|null $is_override_allowed = null, - /** - * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - */ - public int|null $max_override_period_minutes = null, /** * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ @@ -75,6 +67,14 @@ public function __construct( * ID of the workspace that contains the thermostat schedule. */ public string|null $workspace_id, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. + */ + public bool|null $is_override_allowed = null, + /** + * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + */ + public int|null $max_override_period_minutes = null, ) {} } } diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index d0e2ce0b..61863afe 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -59,14 +59,6 @@ public function __construct( * Unique identifier for the access code. */ public string|null $access_code_id, - /** - * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. - */ - public true|null $cannot_be_managed = null, - /** - * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. - */ - public true|null $cannot_delete_unmanaged_access_code = null, /** * Code used for access. Typically, a numeric or alphanumeric string. */ @@ -79,14 +71,6 @@ public function __construct( * Unique identifier for the device associated with the access code. */ public string|null $device_id, - /** - * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. - */ - public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, - /** - * Date and time after which the time-bound access code becomes inactive. - */ - public string|null $ends_at = null, /** * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). */ @@ -99,10 +83,6 @@ public function __construct( * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). */ public string|null $name, - /** - * Date and time at which the time-bound access code becomes active. - */ - public string|null $starts_at = null, /** * Current status of the access code within the operational lifecycle. `set` indicates that the code is active and operational. `unset` indicates that the code exists on the provider but is not usable on the device. */ @@ -119,6 +99,26 @@ public function __construct( * Unique identifier for the Seam workspace associated with the access code. */ public string|null $workspace_id, + /** + * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. + */ + public true|null $cannot_be_managed = null, + /** + * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. + */ + public true|null $cannot_delete_unmanaged_access_code = null, + /** + * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. + */ + public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, + /** + * Date and time after which the time-bound access code becomes inactive. + */ + public string|null $ends_at = null, + /** + * Date and time at which the time-bound access code becomes active. + */ + public string|null $starts_at = null, ) {} } } @@ -214,14 +214,6 @@ public static function from_json(mixed $json): Errors|null } public function __construct( - /** - * 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. - */ - public string|null $change_type = null, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at = null, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ @@ -230,20 +222,28 @@ public function __construct( * Indicates that this is an access code error. */ public true|null $is_access_code_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * 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. + */ + public string|null $change_type = null, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ public bool|null $is_bridge_error = null, - public bool|null $is_connected_account_error, - public bool|null $is_device_error, /** * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. */ public string|null $managed_access_code_id = null, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, /** * List of fields that were changed externally, with their previous and new values. */ @@ -278,6 +278,14 @@ public static function from_json(mixed $json): Warnings|null } public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, /** * 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. */ @@ -286,18 +294,10 @@ public function __construct( * Date and time at which Seam created the warning. */ public string|null $created_at = null, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, /** * List of fields that were changed externally, with their previous and new values. */ public array|null $modified_fields = null, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, ) {} } } diff --git a/src/Resources/UnmanagedAccessGrant.php b/src/Resources/UnmanagedAccessGrant.php index 7a898a81..03726d3b 100644 --- a/src/Resources/UnmanagedAccessGrant.php +++ b/src/Resources/UnmanagedAccessGrant.php @@ -90,10 +90,6 @@ public function __construct( * Access methods that the user requested for the Access Grant. */ public array $requested_access_methods, - /** - * Reservation key for the access grant. - */ - public string|null $reservation_key = null, /** * IDs of the spaces to which the Access Grant gives access. */ @@ -102,10 +98,6 @@ public function __construct( * Date and time at which the Access Grant starts. */ public string|null $starts_at, - /** - * ID of user identity to which the Access Grant gives access. - */ - public string|null $user_identity_id = null, /** * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). */ @@ -114,6 +106,14 @@ public function __construct( * ID of the Seam workspace associated with the Access Grant. */ public string|null $workspace_id, + /** + * Reservation key for the access grant. + */ + public string|null $reservation_key = null, + /** + * ID of user identity to which the Access Grant gives access. + */ + public string|null $user_identity_id = null, ) {} } } @@ -224,10 +224,6 @@ public static function from_json( } public function __construct( - /** - * Specific PIN code to use for this access method. Only applicable when mode is 'code'. - */ - public string|null $code = null, /** * IDs of the access methods created for the requested access method. */ @@ -240,14 +236,18 @@ public function __construct( * Display name of the access method. */ public string|null $display_name, - /** - * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. - */ - public int|null $instant_key_max_use_count = null, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ public string|null $mode, + /** + * Specific PIN code to use for this access method. Only applicable when mode is 'code'. + */ + public string|null $code = null, + /** + * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. + */ + public int|null $instant_key_max_use_count = null, ) {} } @@ -287,10 +287,6 @@ public function __construct( */ public string|null $created_at, public string|null $device_id, - /** - * 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). - */ - public array|null $failed_devices = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -311,6 +307,10 @@ public function __construct( * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ public string|null $warning_code, + /** + * 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). + */ + public array|null $failed_devices = null, ) {} } } @@ -362,10 +362,6 @@ public static function from_json(mixed $json): To|null } public function __construct( - /** - * Common code key to ensure PIN code reuse across devices. - */ - public string|null $common_code_key = null, /** * New device IDs where access codes should be created. */ @@ -378,6 +374,10 @@ public function __construct( * New start time for access. */ public string|null $starts_at, + /** + * Common code key to ensure PIN code reuse across devices. + */ + public string|null $common_code_key = null, ) {} } } diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index acfc5177..b59d7248 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -47,10 +47,6 @@ public function __construct( * ID of the access method. */ public string|null $access_method_id, - /** - * The actual PIN code for code access methods. - */ - public string|null $code = null, /** * Date and time at which the access method was created. */ @@ -63,26 +59,10 @@ public function __construct( * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). */ public array $errors, - /** - * 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. - */ - public bool|null $is_assignment_required = null, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. - */ - public bool|null $is_encoding_required = null, /** * Indicates whether the access method has been issued. */ public bool|null $is_issued, - /** - * 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. - */ - public bool|null $is_ready_for_assignment = null, - /** - * 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. - */ - public bool|null $is_ready_for_encoding = null, /** * Date and time at which the access method was issued. */ @@ -103,6 +83,26 @@ public function __construct( * ID of the Seam workspace associated with the access method. */ public string|null $workspace_id, + /** + * The actual PIN code for code access methods. + */ + public string|null $code = null, + /** + * 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. + */ + public bool|null $is_assignment_required = null, + /** + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + */ + public bool|null $is_encoding_required = null, + /** + * 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. + */ + public bool|null $is_ready_for_assignment = null, + /** + * 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. + */ + public bool|null $is_ready_for_encoding = null, ) {} } } @@ -207,14 +207,14 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ public string|null $warning_code, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id = null, ) {} } } diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 7a6a2ad3..55727698 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -68,6 +68,52 @@ public static function from_json(mixed $json): UnmanagedDevice|null } public function __construct( + /** + * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). + */ + public array|null $capabilities_supported, + /** + * Unique identifier for the account associated with the device. + */ + public string|null $connected_account_id, + /** + * Date and time at which the device object was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $custom_metadata, + /** + * ID of the device. + */ + public string|null $device_id, + /** + * Type of the device. + */ + public string|null $device_type, + /** + * 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. + */ + public array $errors, + /** + * Indicates that Seam does not manage the device. + */ + public false|null $is_managed, + /** + * properties of the device. + */ + public UnmanagedDevice\Properties|null $properties, + /** + * 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. + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the device. + */ + public string|null $workspace_id, /** * Indicates whether the lock supports configuring automatic locking. */ @@ -148,56 +194,10 @@ public function __construct( * Indicates whether the lock supports unlocking with an access code. */ public bool|null $can_unlock_with_code = null, - /** - * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). - */ - public array|null $capabilities_supported, - /** - * Unique identifier for the account associated with the device. - */ - public string|null $connected_account_id, - /** - * Date and time at which the device object was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - * - * @var array|\stdClass|null - */ - public array|\stdClass|null $custom_metadata, - /** - * ID of the device. - */ - public string|null $device_id, - /** - * Type of the device. - */ - public string|null $device_type, - /** - * 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. - */ - public array $errors, - /** - * Indicates that Seam does not manage the device. - */ - public false|null $is_managed, /** * Location information for the device. */ public UnmanagedDevice\Location|null $location = null, - /** - * properties of the device. - */ - public UnmanagedDevice\Properties|null $properties, - /** - * 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. - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the device. - */ - public string|null $workspace_id, ) {} } } @@ -233,16 +233,16 @@ public function __construct( * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error = null, ) {} } @@ -322,6 +322,20 @@ public static function from_json(mixed $json): Properties|null } public function __construct( + /** + * Device model-related properties. + */ + public Properties\Model|null $model, + /** + * Name of the device. + * + * @deprecated use device.display_name instead + */ + public string|null $name, + /** + * Indicates whether the device is online. + */ + public bool|null $online, /** * Accessory keypad properties and state. */ @@ -346,26 +360,12 @@ public function __construct( * 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. */ public string|null $manufacturer = null, - /** - * Device model-related properties. - */ - public Properties\Model|null $model, - /** - * Name of the device. - * - * @deprecated use device.display_name instead - */ - public string|null $name, /** * Indicates whether it is currently possible to use offline access codes for the device. * * @deprecated use device.can_program_offline_access_codes */ public bool|null $offline_access_codes_enabled = null, - /** - * Indicates whether the device is online. - */ - public bool|null $online, /** * Indicates whether it is currently possible to use online access codes for the device. * @@ -441,14 +441,14 @@ public static function from_json(mixed $json): AccessoryKeypad|null } public function __construct( - /** - * Keypad battery properties. - */ - public AccessoryKeypad\Battery|null $battery = null, /** * Indicates if an accessory keypad is connected to the device. */ public bool|null $is_connected, + /** + * Keypad battery properties. + */ + public AccessoryKeypad\Battery|null $battery = null, ) {} } @@ -507,6 +507,14 @@ public static function from_json(mixed $json): Model|null } public function __construct( + /** + * Display name of the device model. + */ + public string|null $display_name, + /** + * Display name that corresponds to the manufacturer-specific terminology for the device. + */ + public string|null $manufacturer_display_name, /** * @deprecated use device.properties.model.can_connect_accessory_keypad */ @@ -515,18 +523,10 @@ public function __construct( * Indicates whether the device can connect a accessory keypad. */ public bool|null $can_connect_accessory_keypad = null, - /** - * Display name of the device model. - */ - public string|null $display_name, /** * Indicates whether the device has a built in accessory keypad. */ public bool|null $has_built_in_keypad = null, - /** - * Display name that corresponds to the manufacturer-specific terminology for the device. - */ - public string|null $manufacturer_display_name, /** * @deprecated use device.can_program_offline_access_codes. */ diff --git a/src/Resources/Webhook.php b/src/Resources/Webhook.php index 7b50da16..69c17272 100644 --- a/src/Resources/Webhook.php +++ b/src/Resources/Webhook.php @@ -20,14 +20,6 @@ public static function from_json(mixed $json): Webhook|null } public function __construct( - /** - * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. - */ - public array|null $event_types = null, - /** - * Secret associated with the [webhook](https://docs.seam.co/developer-tools/webhooks). - */ - public string|null $secret = null, /** * URL for the [webhook](https://docs.seam.co/developer-tools/webhooks). */ @@ -36,6 +28,14 @@ public function __construct( * ID of the webhook. */ public string|null $webhook_id, + /** + * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. + */ + public array|null $event_types = null, + /** + * Secret associated with the [webhook](https://docs.seam.co/developer-tools/webhooks). + */ + public string|null $secret = null, ) {} } } diff --git a/src/Resources/Workspace.php b/src/Resources/Workspace.php index 87e16359..2fb45430 100644 --- a/src/Resources/Workspace.php +++ b/src/Resources/Workspace.php @@ -62,14 +62,14 @@ public function __construct( * ID of the organization to which the workspace belongs, or `null` if the workspace is not assigned to an organization. */ public string|null $organization_id, - /** - * Publishable key for the [workspace](https://docs.seam.co/core-concepts/workspaces). This key is used to identify the workspace in client-side applications. - */ - public string|null $publishable_key = null, /** * ID of the workspace. */ public string|null $workspace_id, + /** + * Publishable key for the [workspace](https://docs.seam.co/core-concepts/workspaces). This key is used to identify the workspace in client-side applications. + */ + public string|null $publishable_key = null, ) {} } } From 96c808bf36b5f3080319189394ac80540c8178f5 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 21:31:19 +0000 Subject: [PATCH 67/80] 4.0.0-beta.14 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9123de5d..82cf1f41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.13", + "version": "4.0.0-beta.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.13", + "version": "4.0.0-beta.14", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 3297cac8..3fa4956f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.13", + "version": "4.0.0-beta.14", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index 5d67ee38..a5c3a438 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.13"; + public const VERSION = "4.0.0-beta.14"; public static function get(): string { From 17a8c32d7076b4f47babfe5a58b6be1a1b23dc03 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 15:15:52 -0700 Subject: [PATCH 68/80] fix: Send the action attempt poll id as a query (#467) The resolver polled /action_attempts/get with the id in a JSON body on a GET, while the generated route for the same endpoint sends it as a query. A GET body is not carried reliably: any proxy, CDN, or load balancer that strips one breaks every wait loop, and it breaks it after the write has already been commanded. The body also skipped the serializing client's query handling, so _strict=true was never applied to the poll. Send a query instead, matching the generated route. The fake server reads GET bodies, so the wire shape is asserted with the recording client. Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG Co-authored-by: Claude --- src/Http/ResolveActionAttempt.php | 2 +- tests/WaitForActionAttemptTest.php | 43 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index 4999d833..5139d9f9 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -100,7 +100,7 @@ private static function get_action_attempt( ): ActionAttempt { $res = Body::decode( $client->request("GET", "/action_attempts/get", [ - "json" => (object) ["action_attempt_id" => $action_attempt_id], + "query" => ["action_attempt_id" => $action_attempt_id], ]), ); diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index 84eeb9f3..601433cf 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -11,6 +11,7 @@ use Seam\Resources\ActionAttempt; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; +use Tests\Support\RecordingClient; final class WaitForActionAttemptTest extends FakeSeamConnectTestCase { @@ -360,6 +361,48 @@ public static function invalidWaitOptions(): array * than the route client, so enabling the option on the route that reads * action attempts cannot recurse. */ + public function testPollSendsTheIdAsAQueryNotABody(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, [ + "action_attempt" => [ + "action_attempt_id" => "aa_1", + "status" => "pending", + ], + ]), + RecordingClient::json(200, [ + "action_attempt" => [ + "action_attempt_id" => "aa_1", + "status" => "success", + ], + ]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + retries: 0, + ); + + $resolved = $seam->action_attempts->get("aa_1", [ + "timeout" => 5.0, + "polling_interval" => 0.01, + ]); + + $this->assertSame("success", $resolved->status); + + $poll = $recorder->request(1); + + $this->assertSame("GET", $poll->getMethod()); + $this->assertSame("/action_attempts/get", $poll->getUri()->getPath()); + $this->assertSame( + "action_attempt_id=aa_1&_strict=true", + $poll->getUri()->getQuery(), + ); + $this->assertSame("", (string) $poll->getBody()); + } + public function testActionAttemptsGetDoesNotRecurse(): void { $seam = $this->seam(wait_for_action_attempt: false); From 1fca2bad69e949c9bbf2a6bcdbf4eeea4e82a5bf Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 22:16:33 +0000 Subject: [PATCH 69/80] 4.0.0-beta.15 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 82cf1f41..255b5939 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.14", + "version": "4.0.0-beta.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.14", + "version": "4.0.0-beta.15", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 3fa4956f..a019da13 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.14", + "version": "4.0.0-beta.15", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index a5c3a438..3ed14a81 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.14"; + public const VERSION = "4.0.0-beta.15"; public static function get(): string { From 7999a86df8d7682ca05b7a5b1273439a7f232d4f Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 15:17:09 -0700 Subject: [PATCH 70/80] feat: Expose the Seam middleware for a caller built client (#472) A client passed with the client option is used exactly as given, which means it does not carry the SDK's error mapping or retries: an API error raises Guzzle's exception rather than HttpApiError, and a retryable failure is not retried. Nothing said so, and the README's own from_client example is subject to it. Leave that behavior alone and make it opt in instead. Guzzle fixes the handler stack when a client is constructed, so the middleware cannot be added afterwards; ClientFactory::add_middleware puts it on a stack the caller builds the client with. ClientFactory::create now uses the same method, so there is one definition of the order the middleware goes on in. Applying it twice would stack two sets of retries, so it is documented as once per stack rather than guarded, since the caller owns the stack. Also correct the claim that $seam->client is the Guzzle client. It is a SerializingClient implementing ClientInterface, so Guzzle specific calls on it fail. Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG Co-authored-by: Claude --- README.md | 41 +++++++++++- codegen/layouts/seam-client.hbs | 2 +- src/Http/ClientFactory.php | 27 ++++++-- src/Seam.php | 2 +- src/SeamWithoutWorkspace.php | 2 +- tests/ClientTest.php | 109 ++++++++++++++++++++++++++++++++ 6 files changed, 171 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2145cf1f..bb82b09c 100644 --- a/README.md +++ b/README.md @@ -390,10 +390,11 @@ $seam = new Seam\Seam(retries: 5); $seam = new Seam\Seam(retries: 0); ``` -#### Using the Guzzle client +#### Using the underlying client -`$seam->client` is the [Guzzle] client, already carrying the endpoint and -authorization, so it can be used to reach an endpoint the SDK does not expose. +`$seam->client` already carries the endpoint, authorization, error mapping, +and retries, so it can be used to reach an endpoint the SDK does not expose. +It wraps the [Guzzle] client and implements Guzzle's `ClientInterface`. [Guzzle]: https://docs.guzzlephp.org/ @@ -420,6 +421,40 @@ $client = new GuzzleHttp\Client([ $seam = Seam\Seam::from_client($client); ``` +The client is used exactly as given. It does not gain the SDK's error mapping +or retries, so an API error raises Guzzle's exception rather than +`Seam\HttpApiError`. To opt in, add the middleware yourself. + +#### Adding the Seam middleware to your own client + +`Seam\Http\ClientFactory::add_middleware` puts the error mapping and retry +middleware on a handler stack. Build the client with that stack, and with +`http_errors` disabled so the error middleware raises instead of Guzzle. + +```php +$handler = GuzzleHttp\HandlerStack::create(); + +Seam\Http\ClientFactory::add_middleware($handler); + +$client = new GuzzleHttp\Client([ + "base_uri" => "https://connect.getseam.com", + "headers" => ["authorization" => "Bearer " . $api_key], + "handler" => $handler, + "http_errors" => false, +]); + +$seam = Seam\Seam::from_client($client); +``` + +Pass `retries` to change how many times a failed request is retried, or `0` +to disable them: + +```php +Seam\Http\ClientFactory::add_middleware($handler, retries: 0); +``` + +Add it once per stack: applying it twice stacks two sets of retries. + #### Serializing URL search params The Seam API parses URL search params as complex types. diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index fa1faede..4e26a88f 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -28,7 +28,7 @@ class Seam {{/each}} /** - * The Guzzle client this instance makes its requests with. + * The client this instance makes its requests with. * * Query params given as a map and NullValue::NULL sentinels in JSON * bodies are serialized with the Seam standard before the request goes diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php index 9ba1451f..77bffdb2 100644 --- a/src/Http/ClientFactory.php +++ b/src/Http/ClientFactory.php @@ -51,12 +51,7 @@ public static function create( $handler = HandlerStack::create($handler); } - // Unshifted so it sits outside every other middleware and only sees - // a response none of them could act on: a redirect is followed - // rather than raised, and a retried request is judged by the - // response it finally settled on. - $handler->unshift(ErrorMiddleware::create(), "seam_error"); - RetryMiddleware::add($handler, $retries); + self::add_middleware($handler, $retries); $headers = array_merge( $auth_headers, @@ -83,6 +78,26 @@ public static function create( ); } + /** + * Adds the Seam error mapping and retry middleware to a handler stack. + * + * Build the client with the stack, and with `http_errors` disabled so + * the error middleware raises instead of Guzzle. + * + * @param int|null $retries Defaults to self::DEFAULT_RETRIES. + */ + public static function add_middleware( + HandlerStack $handler, + ?int $retries = null, + ): void { + // Unshifted so it sits outside every other middleware and only sees + // a response none of them could act on: a redirect is followed + // rather than raised, and a retried request is judged by the + // response it finally settled on. + $handler->unshift(ErrorMiddleware::create(), "seam_error"); + RetryMiddleware::add($handler, $retries ?? self::DEFAULT_RETRIES); + } + /** * @return array */ diff --git a/src/Seam.php b/src/Seam.php index 4ad10234..3f423eea 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -62,7 +62,7 @@ class Seam public WorkspacesClient $workspaces; /** - * The Guzzle client this instance makes its requests with. + * The client this instance makes its requests with. * * Query params given as a map and NullValue::NULL sentinels in JSON * bodies are serialized with the Seam standard before the request goes diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index 69e3cc7b..603ba22e 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -19,7 +19,7 @@ class SeamWithoutWorkspace { /** - * The Guzzle client this instance makes its requests with. + * The client this instance makes its requests with. */ public ClientInterface $client; diff --git a/tests/ClientTest.php b/tests/ClientTest.php index b565c9c1..0f6c5feb 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -65,6 +65,115 @@ public function testFromClientNeedsNoCredentials(): void ); } + private function foreign_client(array $options = []): \GuzzleHttp\Client + { + return new \GuzzleHttp\Client( + array_merge( + [ + "base_uri" => $this->endpoint, + "headers" => [ + "authorization" => + "Bearer " . $this->seed["seam_apikey1_token"], + ], + ], + $options, + ), + ); + } + + public function testAnInjectedClientIsUsedAsGiven(): void + { + $seam = Seam::from_client($this->foreign_client()); + + $device = $seam->devices->get($this->seed["august_device_1"]); + $this->assertSame($this->seed["august_device_1"], $device->device_id); + + $this->expectException(\GuzzleHttp\Exception\ClientException::class); + + $seam->devices->get("nonexistent-device-id"); + } + + public function testAddMiddlewareGivesAnInjectedClientSeamErrors(): void + { + $handler = \GuzzleHttp\HandlerStack::create(); + ClientFactory::add_middleware($handler); + + $seam = Seam::from_client( + $this->foreign_client([ + "handler" => $handler, + "http_errors" => false, + ]), + ); + + $this->expectException(HttpApiError::class); + + $seam->devices->get("nonexistent-device-id"); + } + + public function testAddMiddlewareGivesAnInjectedClientRetries(): void + { + $recorder = RecordingClient::repeating( + RecordingClient::json(503, [ + "error" => [ + "type" => "unavailable", + "message" => "Service Unavailable", + ], + ]), + times: 5, + ); + + $handler = \GuzzleHttp\HandlerStack::create( + $recorder->guzzle_options()["handler"], + ); + ClientFactory::add_middleware($handler); + + $seam = Seam::from_client( + $this->foreign_client([ + "handler" => $handler, + "http_errors" => false, + ]), + ); + + try { + $seam->devices->get("d1"); + $this->fail("Expected an HttpApiError"); + } catch (HttpApiError) { + $this->assertSame(3, $recorder->attempt_count()); + } + } + + public function testAddMiddlewareHonoursARetryCount(): void + { + $recorder = RecordingClient::repeating( + RecordingClient::json(503, [ + "error" => [ + "type" => "unavailable", + "message" => "Service Unavailable", + ], + ]), + times: 5, + ); + + $handler = \GuzzleHttp\HandlerStack::create( + $recorder->guzzle_options()["handler"], + ); + ClientFactory::add_middleware($handler, retries: 0); + + $seam = Seam::from_client( + $this->foreign_client([ + "handler" => $handler, + "http_errors" => false, + ]), + ); + + try { + $seam->devices->get("d1"); + $this->fail("Expected an HttpApiError"); + } catch (HttpApiError) { + $this->assertSame(1, $recorder->attempt_count()); + } + } + public function testClientOptionReusesAnotherInstancesClient(): void { $seam = new Seam(client: $this->seam()->client); From 4ee0b79b6b59be6aa7a3bd5646d81970ded0e64a Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 22:17:59 +0000 Subject: [PATCH 71/80] 4.0.0-beta.16 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 255b5939..e2cc7aba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.15", + "version": "4.0.0-beta.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.15", + "version": "4.0.0-beta.16", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index a019da13..32a4e529 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.15", + "version": "4.0.0-beta.16", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index 3ed14a81..7bf5525e 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.15"; + public const VERSION = "4.0.0-beta.16"; public static function get(): string { From 206939ed95e3fd48539e8069692d86b91263480a Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 15:34:05 -0700 Subject: [PATCH 72/80] fix: Raise a Seam error for a success response that is malformed (#475) Generated methods read the resource straight off the decoded envelope, with no guard on any of the unwrap sites. A 200 whose body had been rewritten or truncated on the way back, by a proxy, a gateway maintenance page carrying a JSON content type, or a load balancer, left the read as null and the failure surfaced from the method's return type: TypeError: DevicesClient::get(): Return value must be of type Device, null returned A list endpoint got array_map(): Argument #2 must be of type array. Both are an Error rather than an Exception, so neither is caught by catch (\Exception) nor by catch (SeamException), and neither says what was wrong with the response. Add InvalidResponseError, a SeamException, and read the envelope through Body::read and Body::read_list, which raise it naming the endpoint and the key. Keeping the guard in one place leaves the generated call sites a single call rather than a block repeated at every one. The action attempt poll read the same way and raised a bare UnexpectedValueException, which was equally invisible to a Seam catch block. It now raises the same error. The malformed-response tests covered only 500s, which never reach the unwrap. Add the malformed 200 cases: a missing key, the wrong key, a body that is not an object, an empty body, unparseable JSON, an HTML gateway page, a list key holding something that is not a list, and a malformed poll response. Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG Co-authored-by: Claude --- README.md | 6 +- codegen/layouts/partials/route-method.hbs | 6 +- src/Http/Body.php | 52 +++++++++ src/Http/ResolveActionAttempt.php | 9 +- src/InvalidResponseError.php | 37 +++++++ src/Routes/AccessCodesClient.php | 28 ++++- src/Routes/AccessCodesSimulateClient.php | 8 +- src/Routes/AccessCodesUnmanagedClient.php | 10 +- src/Routes/AccessGrantsClient.php | 22 +++- src/Routes/AccessGrantsUnmanagedClient.php | 10 +- src/Routes/AccessMethodsClient.php | 30 ++++- src/Routes/AccessMethodsUnmanagedClient.php | 10 +- src/Routes/AcsAccessGroupsClient.php | 21 +++- src/Routes/AcsCredentialsClient.php | 16 ++- src/Routes/AcsEncodersClient.php | 30 ++++- src/Routes/AcsEntrancesClient.php | 16 ++- src/Routes/AcsSystemsClient.php | 18 ++- src/Routes/AcsUsersClient.php | 19 +++- src/Routes/ActionAttemptsClient.php | 6 +- src/Routes/ClientSessionsClient.php | 18 ++- src/Routes/ConnectWebviewsClient.php | 10 +- src/Routes/ConnectedAccountsClient.php | 10 +- src/Routes/CustomersClient.php | 4 +- src/Routes/DevicesClient.php | 13 ++- src/Routes/DevicesUnmanagedClient.php | 6 +- src/Routes/EventsClient.php | 7 +- src/Routes/InstantKeysClient.php | 6 +- src/Routes/LocksClient.php | 23 +++- src/Routes/LocksSimulateClient.php | 16 ++- src/Routes/NoiseSensorsClient.php | 5 +- .../NoiseSensorsNoiseThresholdsClient.php | 22 +++- src/Routes/PhonesClient.php | 7 +- src/Routes/PhonesSimulateClient.php | 4 +- src/Routes/SpacesClient.php | 15 ++- src/Routes/ThermostatsClient.php | 49 +++++++-- src/Routes/ThermostatsDailyProgramsClient.php | 14 ++- src/Routes/ThermostatsSchedulesClient.php | 22 +++- src/Routes/UserIdentitiesClient.php | 51 +++++++-- src/Routes/UserIdentitiesUnmanagedClient.php | 10 +- src/Routes/WebhooksClient.php | 11 +- src/Routes/WorkspacesClient.php | 17 ++- tests/MalformedResponseTest.php | 103 ++++++++++++++++++ 42 files changed, 670 insertions(+), 127 deletions(-) create mode 100644 src/InvalidResponseError.php diff --git a/README.md b/README.md index bb82b09c..da94f346 100644 --- a/README.md +++ b/README.md @@ -539,8 +539,10 @@ try { } ``` -A response that is not shaped like a Seam error, such as a gateway returning -HTML, raises the underlying Guzzle exception instead. +An error response that is not shaped like a Seam error, such as a gateway +returning HTML, raises the underlying Guzzle exception instead. A successful +response that does not carry the resource the endpoint returns raises +`Seam\InvalidResponseError`, with `getPath()` and `getKey()`. ## Development and Testing diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 7ce8dfdc..0229b4dc 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -29,7 +29,7 @@ {{#if usesActionAttempt}} return ResolveActionAttempt::resolve_action_attempt( - {{returnResource}}::from_json($res->{{returnPath}}), + {{returnResource}}::from_json(Body::read($res, "{{returnPath}}", "{{path}}")), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"] ); @@ -43,9 +43,9 @@ {{#unless returnsVoid}} {{#if isArrayResponse}} - return array_map(fn ($r) => {{returnResource}}::from_json($r), $res->{{returnPath}}); + return array_map(fn ($r) => {{returnResource}}::from_json($r), Body::read_list($res, "{{returnPath}}", "{{path}}")); {{else}} - return {{returnResource}}::from_json($res->{{returnPath}}); + return {{returnResource}}::from_json(Body::read($res, "{{returnPath}}", "{{path}}")); {{/if}} {{/unless}} {{/if}} diff --git a/src/Http/Body.php b/src/Http/Body.php index 47b36eef..47ccc2d6 100644 --- a/src/Http/Body.php +++ b/src/Http/Body.php @@ -4,6 +4,7 @@ use GuzzleHttp\Utils; use Psr\Http\Message\ResponseInterface; +use Seam\InvalidResponseError; /** * Reads the JSON body of a response. @@ -38,4 +39,55 @@ public static function decode(ResponseInterface $response): mixed return null; } } + + /** + * Reads the resource an endpoint returns out of its response envelope. + * + * @throws InvalidResponseError If the envelope does not carry the key + */ + public static function read(mixed $res, string $key, string $path): mixed + { + if (!is_object($res)) { + throw new InvalidResponseError( + $path, + $key, + "got " . get_debug_type($res) . " instead of a response object", + ); + } + + if (!property_exists($res, $key)) { + throw new InvalidResponseError( + $path, + $key, + "which the response does not contain", + ); + } + + return $res->$key; + } + + /** + * Reads a list of resources out of a response envelope. + * + * @return array + * + * @throws InvalidResponseError If the envelope does not carry a list + */ + public static function read_list( + mixed $res, + string $key, + string $path, + ): array { + $value = self::read($res, $key, $path); + + if (!is_array($value)) { + throw new InvalidResponseError( + $path, + $key, + "got " . get_debug_type($value) . " instead of a list", + ); + } + + return $value; + } } diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index 5139d9f9..90c3db54 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -6,6 +6,7 @@ use Seam\ActionAttemptFailedError; use Seam\ActionAttemptTimeoutError; use Seam\InvalidOptionsError; +use Seam\InvalidResponseError; use Seam\Resources\ActionAttempt; /** @@ -105,12 +106,14 @@ private static function get_action_attempt( ); $action_attempt = ActionAttempt::from_json( - $res->action_attempt ?? null, + Body::read($res, "action_attempt", "/action_attempts/get"), ); if ($action_attempt === null) { - throw new \UnexpectedValueException( - "Seam returned no action attempt for {$action_attempt_id}", + throw new InvalidResponseError( + "/action_attempts/get", + "action_attempt", + "which was empty for {$action_attempt_id}", ); } diff --git a/src/InvalidResponseError.php b/src/InvalidResponseError.php new file mode 100644 index 00000000..59b7c989 --- /dev/null +++ b/src/InvalidResponseError.php @@ -0,0 +1,37 @@ +path; + } + + /** + * The response key that should have carried the resource. + */ + public function getKey(): string + { + return $this->key; + } +} diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 7b6f3638..c5211607 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -142,7 +142,9 @@ public function create( ]), ); - return AccessCode::from_json($res->access_code); + return AccessCode::from_json( + Body::read($res, "access_code", "/access_codes/create"), + ); } /** @@ -249,7 +251,11 @@ public function create_multiple( return array_map( fn($r) => AccessCode::from_json($r), - $res->access_codes, + Body::read_list( + $res, + "access_codes", + "/access_codes/create_multiple", + ), ); } @@ -294,7 +300,9 @@ public function generate_code(string $device_id): AccessCode ]), ); - return AccessCode::from_json($res->generated_code); + return AccessCode::from_json( + Body::read($res, "generated_code", "/access_codes/generate_code"), + ); } /** @@ -335,7 +343,9 @@ public function get( ]), ); - return AccessCode::from_json($res->access_code); + return AccessCode::from_json( + Body::read($res, "access_code", "/access_codes/get"), + ); } /** @@ -430,7 +440,7 @@ public function list( return array_map( fn($r) => AccessCode::from_json($r), - $res->access_codes, + Body::read_list($res, "access_codes", "/access_codes/list"), ); } @@ -462,7 +472,13 @@ public function pull_backup_access_code(string $access_code_id): AccessCode ), ); - return AccessCode::from_json($res->access_code); + return AccessCode::from_json( + Body::read( + $res, + "access_code", + "/access_codes/pull_backup_access_code", + ), + ); } /** diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index a2897660..a94074c3 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -51,6 +51,12 @@ public function create_unmanaged_access_code( ), ); - return UnmanagedAccessCode::from_json($res->access_code); + return UnmanagedAccessCode::from_json( + Body::read( + $res, + "access_code", + "/access_codes/simulate/create_unmanaged_access_code", + ), + ); } } diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 8e28e0de..4c0739fc 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -123,7 +123,9 @@ public function get( ]), ); - return UnmanagedAccessCode::from_json($res->access_code); + return UnmanagedAccessCode::from_json( + Body::read($res, "access_code", "/access_codes/unmanaged/get"), + ); } /** @@ -173,7 +175,11 @@ public function list( return array_map( fn($r) => UnmanagedAccessCode::from_json($r), - $res->access_codes, + Body::read_list( + $res, + "access_codes", + "/access_codes/unmanaged/list", + ), ); } diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 46ae3690..29e5df1c 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -120,7 +120,9 @@ public function create( ]), ); - return AccessGrant::from_json($res->access_grant); + return AccessGrant::from_json( + Body::read($res, "access_grant", "/access_grants/create"), + ); } /** @@ -171,7 +173,9 @@ public function get( ]), ); - return AccessGrant::from_json($res->access_grant); + return AccessGrant::from_json( + Body::read($res, "access_grant", "/access_grants/get"), + ); } /** @@ -220,7 +224,9 @@ public function get_related( ]), ); - return Batch::from_json($res->batch); + return Batch::from_json( + Body::read($res, "batch", "/access_grants/get_related"), + ); } /** @@ -312,7 +318,7 @@ public function list( return array_map( fn($r) => AccessGrant::from_json($r), - $res->access_grants, + Body::read_list($res, "access_grants", "/access_grants/list"), ); } @@ -342,7 +348,13 @@ public function request_access_methods( ), ); - return AccessGrant::from_json($res->access_grant); + return AccessGrant::from_json( + Body::read( + $res, + "access_grant", + "/access_grants/request_access_methods", + ), + ); } /** diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index d7b716c7..b22800b8 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -43,7 +43,9 @@ public function get(string $access_grant_id): UnmanagedAccessGrant ]), ); - return UnmanagedAccessGrant::from_json($res->access_grant); + return UnmanagedAccessGrant::from_json( + Body::read($res, "access_grant", "/access_grants/unmanaged/get"), + ); } /** @@ -100,7 +102,11 @@ public function list( return array_map( fn($r) => UnmanagedAccessGrant::from_json($r), - $res->access_grants, + Body::read_list( + $res, + "access_grants", + "/access_grants/unmanaged/list", + ), ); } diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index e36c36ca..41c22195 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -54,7 +54,13 @@ public function assign_card( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/access_methods/assign_card", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -125,7 +131,9 @@ public function encode( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/access_methods/encode"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -150,7 +158,9 @@ public function get(string $access_method_id): AccessMethod ]), ); - return AccessMethod::from_json($res->access_method); + return AccessMethod::from_json( + Body::read($res, "access_method", "/access_methods/get"), + ); } /** @@ -182,7 +192,9 @@ public function get_related( ]), ); - return Batch::from_json($res->batch); + return Batch::from_json( + Body::read($res, "batch", "/access_methods/get_related"), + ); } /** @@ -263,7 +275,7 @@ public function list( return array_map( fn($r) => AccessMethod::from_json($r), - $res->access_methods, + Body::read_list($res, "access_methods", "/access_methods/list"), ); } @@ -292,7 +304,13 @@ public function unlock_door( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/access_methods/unlock_door", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 9bc0a43a..34c85538 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -42,7 +42,9 @@ public function get(string $access_method_id): UnmanagedAccessMethod ]), ); - return UnmanagedAccessMethod::from_json($res->access_method); + return UnmanagedAccessMethod::from_json( + Body::read($res, "access_method", "/access_methods/unmanaged/get"), + ); } /** @@ -81,7 +83,11 @@ public function list( return array_map( fn($r) => UnmanagedAccessMethod::from_json($r), - $res->access_methods, + Body::read_list( + $res, + "access_methods", + "/access_methods/unmanaged/list", + ), ); } } diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 74cb4208..d52d009a 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -89,7 +89,9 @@ public function get(string $acs_access_group_id): AcsAccessGroup ]), ); - return AcsAccessGroup::from_json($res->acs_access_group); + return AcsAccessGroup::from_json( + Body::read($res, "acs_access_group", "/acs/access_groups/get"), + ); } /** @@ -130,7 +132,11 @@ public function list( return array_map( fn($r) => AcsAccessGroup::from_json($r), - $res->acs_access_groups, + Body::read_list( + $res, + "acs_access_groups", + "/acs/access_groups/list", + ), ); } @@ -157,7 +163,11 @@ public function list_accessible_entrances( return array_map( fn($r) => AcsEntrance::from_json($r), - $res->acs_entrances, + Body::read_list( + $res, + "acs_entrances", + "/acs/access_groups/list_accessible_entrances", + ), ); } @@ -179,7 +189,10 @@ public function list_users(string $acs_access_group_id): array ]), ); - return array_map(fn($r) => AcsUser::from_json($r), $res->acs_users); + return array_map( + fn($r) => AcsUser::from_json($r), + Body::read_list($res, "acs_users", "/acs/access_groups/list_users"), + ); } /** diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 12291c33..b8a8774e 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -141,7 +141,9 @@ public function create( ]), ); - return AcsCredential::from_json($res->acs_credential); + return AcsCredential::from_json( + Body::read($res, "acs_credential", "/acs/credentials/create"), + ); } /** @@ -179,7 +181,9 @@ public function get(string $acs_credential_id): AcsCredential ]), ); - return AcsCredential::from_json($res->acs_credential); + return AcsCredential::from_json( + Body::read($res, "acs_credential", "/acs/credentials/get"), + ); } /** @@ -248,7 +252,7 @@ public function list( return array_map( fn($r) => AcsCredential::from_json($r), - $res->acs_credentials, + Body::read_list($res, "acs_credentials", "/acs/credentials/list"), ); } @@ -274,7 +278,11 @@ public function list_accessible_entrances(string $acs_credential_id): array return array_map( fn($r) => AcsEntrance::from_json($r), - $res->acs_entrances, + Body::read_list( + $res, + "acs_entrances", + "/acs/credentials/list_accessible_entrances", + ), ); } diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index 0ec0f2b0..91728f03 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -60,7 +60,13 @@ public function encode_credential( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/acs/encoders/encode_credential", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -85,7 +91,9 @@ public function get(string $acs_encoder_id): AcsEncoder ]), ); - return AcsEncoder::from_json($res->acs_encoder); + return AcsEncoder::from_json( + Body::read($res, "acs_encoder", "/acs/encoders/get"), + ); } /** @@ -137,7 +145,7 @@ public function list( return array_map( fn($r) => AcsEncoder::from_json($r), - $res->acs_encoders, + Body::read_list($res, "acs_encoders", "/acs/encoders/list"), ); } @@ -168,7 +176,13 @@ public function scan_credential( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/acs/encoders/scan_credential", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -214,7 +228,13 @@ public function scan_to_assign_credential( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/acs/encoders/scan_to_assign_credential", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index d9fcb87e..be84c517 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -46,7 +46,9 @@ public function get(string $acs_entrance_id): AcsEntrance ]), ); - return AcsEntrance::from_json($res->acs_entrance); + return AcsEntrance::from_json( + Body::read($res, "acs_entrance", "/acs/entrances/get"), + ); } /** @@ -156,7 +158,7 @@ public function list( return array_map( fn($r) => AcsEntrance::from_json($r), - $res->acs_entrances, + Body::read_list($res, "acs_entrances", "/acs/entrances/list"), ); } @@ -188,7 +190,11 @@ public function list_credentials_with_access( return array_map( fn($r) => AcsCredential::from_json($r), - $res->acs_credentials, + Body::read_list( + $res, + "acs_credentials", + "/acs/entrances/list_credentials_with_access", + ), ); } @@ -217,7 +223,9 @@ public function unlock( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/acs/entrances/unlock"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index af1d681d..b4b059db 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -42,7 +42,9 @@ public function get(string $acs_system_id): AcsSystem ]), ); - return AcsSystem::from_json($res->acs_system); + return AcsSystem::from_json( + Body::read($res, "acs_system", "/acs/systems/get"), + ); } /** @@ -78,7 +80,10 @@ public function list( ]), ); - return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); + return array_map( + fn($r) => AcsSystem::from_json($r), + Body::read_list($res, "acs_systems", "/acs/systems/list"), + ); } /** @@ -104,7 +109,14 @@ public function list_compatible_credential_manager_acs_systems( ), ); - return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); + return array_map( + fn($r) => AcsSystem::from_json($r), + Body::read_list( + $res, + "acs_systems", + "/acs/systems/list_compatible_credential_manager_acs_systems", + ), + ); } /** diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 9c0d8399..64ce6cae 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -99,7 +99,9 @@ public function create( ]), ); - return AcsUser::from_json($res->acs_user); + return AcsUser::from_json( + Body::read($res, "acs_user", "/acs/users/create"), + ); } /** @@ -181,7 +183,9 @@ public function get( ]), ); - return AcsUser::from_json($res->acs_user); + return AcsUser::from_json( + Body::read($res, "acs_user", "/acs/users/get"), + ); } /** @@ -250,7 +254,10 @@ public function list( $on_response($res); } - return array_map(fn($r) => AcsUser::from_json($r), $res->acs_users); + return array_map( + fn($r) => AcsUser::from_json($r), + Body::read_list($res, "acs_users", "/acs/users/list"), + ); } /** @@ -297,7 +304,11 @@ public function list_accessible_entrances( return array_map( fn($r) => AcsEntrance::from_json($r), - $res->acs_entrances, + Body::read_list( + $res, + "acs_entrances", + "/acs/users/list_accessible_entrances", + ), ); } diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 9e1e4944..74acf26f 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -48,7 +48,9 @@ public function get( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/action_attempts/get"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -99,7 +101,7 @@ public function list( return array_map( fn($r) => ActionAttempt::from_json($r), - $res->action_attempts, + Body::read_list($res, "action_attempts", "/action_attempts/list"), ); } } diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index dbd36c2b..7146e7c8 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -80,7 +80,9 @@ public function create( ]), ); - return ClientSession::from_json($res->client_session); + return ClientSession::from_json( + Body::read($res, "client_session", "/client_sessions/create"), + ); } /** @@ -126,7 +128,9 @@ public function get( ]), ); - return ClientSession::from_json($res->client_session); + return ClientSession::from_json( + Body::read($res, "client_session", "/client_sessions/get"), + ); } /** @@ -175,7 +179,13 @@ public function get_or_create( ]), ); - return ClientSession::from_json($res->client_session); + return ClientSession::from_json( + Body::read( + $res, + "client_session", + "/client_sessions/get_or_create", + ), + ); } /** @@ -280,7 +290,7 @@ public function list( return array_map( fn($r) => ClientSession::from_json($r), - $res->client_sessions, + Body::read_list($res, "client_sessions", "/client_sessions/list"), ); } diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 807dd4ae..5dc7b7e3 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -103,7 +103,9 @@ public function create( ]), ); - return ConnectWebview::from_json($res->connect_webview); + return ConnectWebview::from_json( + Body::read($res, "connect_webview", "/connect_webviews/create"), + ); } /** @@ -145,7 +147,9 @@ public function get(string $connect_webview_id): ConnectWebview ]), ); - return ConnectWebview::from_json($res->connect_webview); + return ConnectWebview::from_json( + Body::read($res, "connect_webview", "/connect_webviews/get"), + ); } /** @@ -202,7 +206,7 @@ public function list( return array_map( fn($r) => ConnectWebview::from_json($r), - $res->connect_webviews, + Body::read_list($res, "connect_webviews", "/connect_webviews/list"), ); } } diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 673d1221..7dc68fef 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -81,7 +81,9 @@ public function get( ]), ); - return ConnectedAccount::from_json($res->connected_account); + return ConnectedAccount::from_json( + Body::read($res, "connected_account", "/connected_accounts/get"), + ); } /** @@ -143,7 +145,11 @@ public function list( return array_map( fn($r) => ConnectedAccount::from_json($r), - $res->connected_accounts, + Body::read_list( + $res, + "connected_accounts", + "/connected_accounts/list", + ), ); } diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index 0e275dca..7baced07 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -99,7 +99,9 @@ public function create_portal( ]), ); - return CustomerPortal::from_json($res->customer_portal); + return CustomerPortal::from_json( + Body::read($res, "customer_portal", "/customers/create_portal"), + ); } /** diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index e9cf9fae..7cd98776 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -60,7 +60,7 @@ public function get(?string $device_id = null, ?string $name = null): Device ]), ); - return Device::from_json($res->device); + return Device::from_json(Body::read($res, "device", "/devices/get")); } /** @@ -165,7 +165,10 @@ public function list( $on_response($res); } - return array_map(fn($r) => Device::from_json($r), $res->devices); + return array_map( + fn($r) => Device::from_json($r), + Body::read_list($res, "devices", "/devices/list"), + ); } /** @@ -195,7 +198,11 @@ public function list_device_providers( return array_map( fn($r) => DeviceProvider::from_json($r), - $res->device_providers, + Body::read_list( + $res, + "device_providers", + "/devices/list_device_providers", + ), ); } diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index a4974282..9ebb9e2f 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -60,7 +60,9 @@ public function get( ]), ); - return UnmanagedDevice::from_json($res->device); + return UnmanagedDevice::from_json( + Body::read($res, "device", "/devices/unmanaged/get"), + ); } /** @@ -149,7 +151,7 @@ public function list( return array_map( fn($r) => UnmanagedDevice::from_json($r), - $res->devices, + Body::read_list($res, "devices", "/devices/unmanaged/list"), ); } diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index e8fd51d5..f2c8177c 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -60,7 +60,7 @@ public function get( ]), ); - return Event::from_json($res->event); + return Event::from_json(Body::read($res, "event", "/events/get")); } /** @@ -253,6 +253,9 @@ public function list( ]), ); - return array_map(fn($r) => Event::from_json($r), $res->events); + return array_map( + fn($r) => Event::from_json($r), + Body::read_list($res, "events", "/events/list"), + ); } } diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index ce21eff7..d732d637 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -72,7 +72,9 @@ public function get( ]), ); - return InstantKey::from_json($res->instant_key); + return InstantKey::from_json( + Body::read($res, "instant_key", "/instant_keys/get"), + ); } /** @@ -97,7 +99,7 @@ public function list(?string $user_identity_id = null): array return array_map( fn($r) => InstantKey::from_json($r), - $res->instant_keys, + Body::read_list($res, "instant_keys", "/instant_keys/list"), ); } } diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 96b6b9f0..d02b6263 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -59,7 +59,13 @@ public function configure_auto_lock( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/locks/configure_auto_lock", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -96,7 +102,7 @@ public function get(?string $device_id = null, ?string $name = null): Device ]), ); - return Device::from_json($res->device); + return Device::from_json(Body::read($res, "device", "/locks/get")); } /** @@ -145,7 +151,10 @@ public function list( ]), ); - return array_map(fn($r) => Device::from_json($r), $res->devices); + return array_map( + fn($r) => Device::from_json($r), + Body::read_list($res, "devices", "/locks/list"), + ); } /** @@ -170,7 +179,9 @@ public function lock_door( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/locks/lock_door"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -199,7 +210,9 @@ public function unlock_door( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/locks/unlock_door"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index 70a5f08d..02daf9fa 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -52,7 +52,13 @@ public function keypad_code_entry( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/locks/simulate/keypad_code_entry", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -83,7 +89,13 @@ public function manual_lock_via_keypad( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/locks/simulate/manual_lock_via_keypad", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index 2bc01c76..4901eabb 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -76,6 +76,9 @@ public function list( ]), ); - return array_map(fn($r) => Device::from_json($r), $res->devices); + return array_map( + fn($r) => Device::from_json($r), + Body::read_list($res, "devices", "/noise_sensors/list"), + ); } } diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 88558830..8d65eb62 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -68,7 +68,13 @@ public function create( ), ); - return NoiseThreshold::from_json($res->noise_threshold); + return NoiseThreshold::from_json( + Body::read( + $res, + "noise_threshold", + "/noise_sensors/noise_thresholds/create", + ), + ); } /** @@ -112,7 +118,13 @@ public function get(string $noise_threshold_id): NoiseThreshold ), ); - return NoiseThreshold::from_json($res->noise_threshold); + return NoiseThreshold::from_json( + Body::read( + $res, + "noise_threshold", + "/noise_sensors/noise_thresholds/get", + ), + ); } /** @@ -137,7 +149,11 @@ public function list(string $device_id): array return array_map( fn($r) => NoiseThreshold::from_json($r), - $res->noise_thresholds, + Body::read_list( + $res, + "noise_thresholds", + "/noise_sensors/noise_thresholds/list", + ), ); } diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index f56953e8..d8576183 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -60,7 +60,7 @@ public function get(string $device_id): Phone ]), ); - return Phone::from_json($res->phone); + return Phone::from_json(Body::read($res, "phone", "/phones/get")); } /** @@ -91,6 +91,9 @@ public function list( ]), ); - return array_map(fn($r) => Phone::from_json($r), $res->phones); + return array_map( + fn($r) => Phone::from_json($r), + Body::read_list($res, "phones", "/phones/list"), + ); } } diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index ba99c52e..353ceafb 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -62,6 +62,8 @@ public function create_sandbox_phone( ), ); - return Phone::from_json($res->phone); + return Phone::from_json( + Body::read($res, "phone", "/phones/simulate/create_sandbox_phone"), + ); } } diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 9d3fca31..e4e3f398 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -136,7 +136,7 @@ public function create( ]), ); - return Space::from_json($res->space); + return Space::from_json(Body::read($res, "space", "/spaces/create")); } /** @@ -187,7 +187,7 @@ public function get( ]), ); - return Space::from_json($res->space); + return Space::from_json(Body::read($res, "space", "/spaces/get")); } /** @@ -236,7 +236,9 @@ public function get_related( ]), ); - return Batch::from_json($res->batch); + return Batch::from_json( + Body::read($res, "batch", "/spaces/get_related"), + ); } /** @@ -286,7 +288,10 @@ public function list( $on_response($res); } - return array_map(fn($r) => Space::from_json($r), $res->spaces); + return array_map( + fn($r) => Space::from_json($r), + Body::read_list($res, "spaces", "/spaces/list"), + ); } /** @@ -396,6 +401,6 @@ public function update( ]), ); - return Space::from_json($res->space); + return Space::from_json(Body::read($res, "space", "/spaces/update")); } } diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 3af98e17..a20245d3 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -62,7 +62,13 @@ public function activate_climate_preset( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/thermostats/activate_climate_preset", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -105,7 +111,9 @@ public function cool( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/thermostats/cool"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -250,7 +258,9 @@ public function heat( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/thermostats/heat"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -307,7 +317,9 @@ public function heat_cool( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/thermostats/heat_cool"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -360,7 +372,10 @@ public function list( ]), ); - return array_map(fn($r) => Device::from_json($r), $res->devices); + return array_map( + fn($r) => Device::from_json($r), + Body::read_list($res, "devices", "/thermostats/list"), + ); } /** @@ -385,7 +400,9 @@ public function off( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/thermostats/off"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -447,7 +464,9 @@ public function set_fan_mode( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/thermostats/set_fan_mode"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -507,7 +526,13 @@ public function set_hvac_mode( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/thermostats/set_hvac_mode", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], @@ -698,7 +723,13 @@ public function update_weekly_program( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/thermostats/update_weekly_program", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 3d64dcbc..e6a71d7c 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -54,7 +54,11 @@ public function create( ); return ThermostatDailyProgram::from_json( - $res->thermostat_daily_program, + Body::read( + $res, + "thermostat_daily_program", + "/thermostats/daily_programs/create", + ), ); } @@ -109,7 +113,13 @@ public function update( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read( + $res, + "action_attempt", + "/thermostats/daily_programs/update", + ), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index e9d1c21d..5f716d7f 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -70,7 +70,13 @@ public function create( ]), ); - return ThermostatSchedule::from_json($res->thermostat_schedule); + return ThermostatSchedule::from_json( + Body::read( + $res, + "thermostat_schedule", + "/thermostats/schedules/create", + ), + ); } /** @@ -108,7 +114,13 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule ]), ); - return ThermostatSchedule::from_json($res->thermostat_schedule); + return ThermostatSchedule::from_json( + Body::read( + $res, + "thermostat_schedule", + "/thermostats/schedules/get", + ), + ); } /** @@ -137,7 +149,11 @@ public function list( return array_map( fn($r) => ThermostatSchedule::from_json($r), - $res->thermostat_schedules, + Body::read_list( + $res, + "thermostat_schedules", + "/thermostats/schedules/list", + ), ); } diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 863a49b2..390fc72b 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -107,7 +107,9 @@ public function create( ]), ); - return UserIdentity::from_json($res->user_identity); + return UserIdentity::from_json( + Body::read($res, "user_identity", "/user_identities/create"), + ); } /** @@ -160,7 +162,13 @@ public function generate_instant_key( ), ); - return InstantKey::from_json($res->instant_key); + return InstantKey::from_json( + Body::read( + $res, + "instant_key", + "/user_identities/generate_instant_key", + ), + ); } /** @@ -194,7 +202,9 @@ public function get( ]), ); - return UserIdentity::from_json($res->user_identity); + return UserIdentity::from_json( + Body::read($res, "user_identity", "/user_identities/get"), + ); } /** @@ -276,7 +286,7 @@ public function list( return array_map( fn($r) => UserIdentity::from_json($r), - $res->user_identities, + Body::read_list($res, "user_identities", "/user_identities/list"), ); } @@ -300,7 +310,14 @@ public function list_accessible_devices(string $user_identity_id): array ), ); - return array_map(fn($r) => Device::from_json($r), $res->devices); + return array_map( + fn($r) => Device::from_json($r), + Body::read_list( + $res, + "devices", + "/user_identities/list_accessible_devices", + ), + ); } /** @@ -325,7 +342,11 @@ public function list_accessible_entrances(string $user_identity_id): array return array_map( fn($r) => AcsEntrance::from_json($r), - $res->acs_entrances, + Body::read_list( + $res, + "acs_entrances", + "/user_identities/list_accessible_entrances", + ), ); } @@ -347,7 +368,14 @@ public function list_acs_systems(string $user_identity_id): array ]), ); - return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); + return array_map( + fn($r) => AcsSystem::from_json($r), + Body::read_list( + $res, + "acs_systems", + "/user_identities/list_acs_systems", + ), + ); } /** @@ -368,7 +396,14 @@ public function list_acs_users(string $user_identity_id): array ]), ); - return array_map(fn($r) => AcsUser::from_json($r), $res->acs_users); + return array_map( + fn($r) => AcsUser::from_json($r), + Body::read_list( + $res, + "acs_users", + "/user_identities/list_acs_users", + ), + ); } /** diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index c794d2cb..9bf8f516 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -43,7 +43,9 @@ public function get(string $user_identity_id): UnmanagedUserIdentity ]), ); - return UnmanagedUserIdentity::from_json($res->user_identity); + return UnmanagedUserIdentity::from_json( + Body::read($res, "user_identity", "/user_identities/unmanaged/get"), + ); } /** @@ -90,7 +92,11 @@ public function list( return array_map( fn($r) => UnmanagedUserIdentity::from_json($r), - $res->user_identities, + Body::read_list( + $res, + "user_identities", + "/user_identities/unmanaged/list", + ), ); } diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 29cbb648..9d53b37e 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -46,7 +46,9 @@ public function create(string $url, ?array $event_types = null): Webhook ]), ); - return Webhook::from_json($res->webhook); + return Webhook::from_json( + Body::read($res, "webhook", "/webhooks/create"), + ); } /** @@ -84,7 +86,7 @@ public function get(string $webhook_id): Webhook ]), ); - return Webhook::from_json($res->webhook); + return Webhook::from_json(Body::read($res, "webhook", "/webhooks/get")); } /** @@ -96,7 +98,10 @@ public function list(): array { $res = Body::decode($this->client->request("GET", "/webhooks/list")); - return array_map(fn($r) => Webhook::from_json($r), $res->webhooks); + return array_map( + fn($r) => Webhook::from_json($r), + Body::read_list($res, "webhooks", "/webhooks/list"), + ); } /** diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index a1336c9d..ab1ce1ac 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -99,7 +99,9 @@ public function create( ]), ); - return Workspace::from_json($res->workspace); + return Workspace::from_json( + Body::read($res, "workspace", "/workspaces/create"), + ); } /** @@ -111,7 +113,9 @@ public function get(): Workspace { $res = Body::decode($this->client->request("GET", "/workspaces/get")); - return Workspace::from_json($res->workspace); + return Workspace::from_json( + Body::read($res, "workspace", "/workspaces/get"), + ); } /** @@ -123,7 +127,10 @@ public function list(): array { $res = Body::decode($this->client->request("GET", "/workspaces/list")); - return array_map(fn($r) => Workspace::from_json($r), $res->workspaces); + return array_map( + fn($r) => Workspace::from_json($r), + Body::read_list($res, "workspaces", "/workspaces/list"), + ); } /** @@ -140,7 +147,9 @@ public function reset_sandbox( ); return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempt), + ActionAttempt::from_json( + Body::read($res, "action_attempt", "/workspaces/reset_sandbox"), + ), $this->client, $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"], diff --git a/tests/MalformedResponseTest.php b/tests/MalformedResponseTest.php index e132c10a..e8cc0993 100644 --- a/tests/MalformedResponseTest.php +++ b/tests/MalformedResponseTest.php @@ -9,7 +9,9 @@ use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\TestCase; use Seam\HttpApiError; +use Seam\InvalidResponseError; use Seam\Seam; +use Seam\SeamException; use Tests\Support\RecordingClient; /** @@ -84,6 +86,107 @@ public static function nonSeamErrorResponses(): array ]; } + /** + * @dataProvider malformedSuccessResponses + */ + public function testMalformedSuccessResponsesRaiseASeamError( + Response $response, + string $expected_message, + ): void { + $seam = $this->seam(new RecordingClient([$response])); + + try { + $seam->devices->get("d1"); + $this->fail("Expected an InvalidResponseError"); + } catch (InvalidResponseError $error) { + $this->assertInstanceOf(SeamException::class, $error); + $this->assertSame("/devices/get", $error->getPath()); + $this->assertSame("device", $error->getKey()); + $this->assertStringContainsString( + $expected_message, + $error->getMessage(), + ); + } + } + + public static function malformedSuccessResponses(): array + { + return [ + "missing the response key" => [ + RecordingClient::json(200, ["ok" => true]), + "which the response does not contain", + ], + "the wrong response key" => [ + RecordingClient::json(200, [ + "devices" => [["device_id" => "d1"]], + ]), + "which the response does not contain", + ], + "json that is not an object" => [ + RecordingClient::json(200, [1, 2]), + "instead of a response object", + ], + "empty body" => [ + RecordingClient::raw(200, "", "application/json"), + "instead of a response object", + ], + "malformed json" => [ + RecordingClient::raw(200, "{invalid", "application/json"), + "instead of a response object", + ], + "a gateway page with a json content type" => [ + RecordingClient::raw( + 200, + "Maintenance", + "application/json", + ), + "instead of a response object", + ], + ]; + } + + public function testAListResponseThatIsNotAListRaisesASeamError(): void + { + $seam = $this->seam( + new RecordingClient([ + RecordingClient::json(200, ["devices" => "not-a-list"]), + ]), + ); + + try { + $seam->devices->list(); + $this->fail("Expected an InvalidResponseError"); + } catch (InvalidResponseError $error) { + $this->assertSame("devices", $error->getKey()); + $this->assertStringContainsString( + "instead of a list", + $error->getMessage(), + ); + } + } + + public function testAMalformedActionAttemptPollRaisesASeamError(): void + { + $seam = $this->seam( + new RecordingClient([ + RecordingClient::json(200, [ + "action_attempt" => [ + "action_attempt_id" => "aa_1", + "status" => "pending", + ], + ]), + RecordingClient::json(200, ["ok" => true]), + ]), + ); + + $this->expectException(InvalidResponseError::class); + + $seam->action_attempts->get("aa_1", [ + "timeout" => 5.0, + "polling_interval" => 0.01, + ]); + } + /** * A redirect is not a success, so it must not be handed back to the * caller as though it were a resource. From b24d0420d67b48afbf70623a70039fc6888afaf8 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 15:34:30 -0700 Subject: [PATCH 73/80] fix: Separate an unreadable webhook payload from a forged one (#469) SeamWebhook::verify raised Svix's WebhookVerificationException for a payload whose signature had already matched but whose body would not parse. The README maps that exception to a 400, so an unreadable delivery was answered with an error and Svix redelivered it on its full backoff schedule, while whoever read the logs saw a verification failure and went looking for a forgery. Raise InvalidWebhookPayloadError instead, a new SeamException, so the two cases can be answered differently: a signature that does not match may be forged, and a body that does not parse is genuinely from Seam and will not become readable however many times it arrives. Catch the parse failure explicitly rather than inferring it from a null event, and treat a correctly signed payload that is not an event the same way. It used to be returned as an Event with every field null, with nothing to tell the caller. Cast header names before lowercasing them, since an all-digit name arrives as an int key and would raise a TypeError. The parse path had no coverage at all. Add cases for malformed JSON, a non-object body, an empty body, and a signed non-event, along with the expired-timestamp and missing-header cases the suite was missing. Claude-Session: https://claude.ai/code/session_01HH3wdHh4Y6Wjyc5uHwk5iG Co-authored-by: Claude --- README.md | 4 +- src/InvalidWebhookPayloadError.php | 10 +++++ src/SeamWebhook.php | 21 ++++++--- tests/SeamWebhookTest.php | 72 +++++++++++++++++++++++++++++- 4 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 src/InvalidWebhookPayloadError.php diff --git a/README.md b/README.md index da94f346..5989b5e3 100644 --- a/README.md +++ b/README.md @@ -331,7 +331,9 @@ try { $event = $webhook->verify($request_body, $request_headers); print $event->event_type; } catch (Svix\Exception\WebhookVerificationException $error) { - http_response_code(400); + http_response_code(401); +} catch (Seam\InvalidWebhookPayloadError $error) { + http_response_code(204); } ``` diff --git a/src/InvalidWebhookPayloadError.php b/src/InvalidWebhookPayloadError.php new file mode 100644 index 00000000..69df0d89 --- /dev/null +++ b/src/InvalidWebhookPayloadError.php @@ -0,0 +1,10 @@ + $headers The HTTP request headers. * * @throws \Svix\Exception\WebhookVerificationException When the signature does not match. + * @throws InvalidWebhookPayloadError When the signature matches but the body is not a Seam event. */ public function verify(string $payload, array $headers): Event { $normalized_headers = []; foreach ($headers as $name => $value) { - $normalized_headers[strtolower($name)] = $value; + $normalized_headers[strtolower((string) $name)] = $value; } $this->webhook->verify($payload, $normalized_headers); - $event = Event::from_json(json_decode($payload)); + $decoded = json_decode($payload); - if ($event === null) { - throw new WebhookVerificationException( + if (json_last_error() !== JSON_ERROR_NONE) { + throw new InvalidWebhookPayloadError( + "The verified webhook payload is not valid JSON: " . + json_last_error_msg(), + ); + } + + $event = Event::from_json($decoded); + + if ($event === null || $event->event_id === null) { + throw new InvalidWebhookPayloadError( "The verified webhook payload did not contain an event", ); } diff --git a/tests/SeamWebhookTest.php b/tests/SeamWebhookTest.php index be871513..f60e96fc 100644 --- a/tests/SeamWebhookTest.php +++ b/tests/SeamWebhookTest.php @@ -5,6 +5,8 @@ namespace Tests; use PHPUnit\Framework\TestCase; +use Seam\InvalidWebhookPayloadError; +use Seam\SeamException; use Seam\SeamWebhook; use Svix\Exception\WebhookVerificationException; use Svix\Webhook; @@ -28,10 +30,10 @@ private function payload(): string /** * @return array */ - private function signed_headers(string $payload): array + private function signed_headers(string $payload, ?int $at = null): array { $id = "msg_test"; - $timestamp = (string) time(); + $timestamp = (string) ($at ?? time()); $signature = (new Webhook(self::SECRET))->sign( $id, @@ -100,4 +102,70 @@ public function testVerifyRejectsTheWrongSecret(): void "whsec_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", ))->verify($payload, $headers); } + + public function testVerifyRejectsAnExpiredTimestamp(): void + { + $payload = $this->payload(); + + $this->expectException(WebhookVerificationException::class); + + (new SeamWebhook(self::SECRET))->verify( + $payload, + $this->signed_headers($payload, time() - 3600), + ); + } + + /** + * @dataProvider missingHeaders + */ + public function testVerifyRejectsAMissingHeader(string $missing): void + { + $payload = $this->payload(); + $headers = $this->signed_headers($payload); + unset($headers[$missing]); + + $this->expectException(WebhookVerificationException::class); + + (new SeamWebhook(self::SECRET))->verify($payload, $headers); + } + + public static function missingHeaders(): array + { + return [ + "svix-id" => ["svix-id"], + "svix-timestamp" => ["svix-timestamp"], + "svix-signature" => ["svix-signature"], + ]; + } + + /** + * @dataProvider unreadablePayloads + */ + public function testVerifyDistinguishesAnUnreadablePayload( + string $payload, + ): void { + $headers = $this->signed_headers($payload); + + try { + (new SeamWebhook(self::SECRET))->verify($payload, $headers); + $this->fail("Expected InvalidWebhookPayloadError"); + } catch (InvalidWebhookPayloadError $error) { + $this->assertInstanceOf(SeamException::class, $error); + $this->assertNotInstanceOf( + WebhookVerificationException::class, + $error, + ); + } + } + + public static function unreadablePayloads(): array + { + return [ + "malformed json" => ["{not json"], + "json that is not an object" => ["[1, 2]"], + "json null" => ["null"], + "empty body" => [""], + "object that is not an event" => ['{"hello":"world"}'], + ]; + } } From d61b532ea3e6690331de5b465920c91b910b73f2 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 22:34:49 +0000 Subject: [PATCH 74/80] 4.0.0-beta.17 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e2cc7aba..9dec8762 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.16", + "version": "4.0.0-beta.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.16", + "version": "4.0.0-beta.17", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 32a4e529..72467580 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.16", + "version": "4.0.0-beta.17", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index 7bf5525e..4092dd9f 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.16"; + public const VERSION = "4.0.0-beta.17"; public static function get(): string { From fe401802870913a45a9992e35a444ce1e69552d0 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 15:54:34 -0700 Subject: [PATCH 75/80] fix: Leave pagination params out of the at least one parameter guard (#474) The generated guard counted limit and page_cursor as parameters, so access_codes->list(limit: 20) satisfied it while naming no filter, and a paginator satisfied it from page 2 onward purely because page_cursor was set. Co-authored-by: Claude --- codegen/layouts/partials/route-method.hbs | 2 +- codegen/lib/layouts/route.ts | 11 +- src/Routes/AccessCodesClient.php | 2 - src/Routes/AccessMethodsClient.php | 2 - src/Routes/EventsClient.php | 1 - tests/RequiredParametersTest.php | 165 ++++++++++++++++++++++ 6 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 tests/RequiredParametersTest.php diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 0229b4dc..0552974d 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,7 +1,7 @@ {{{methodPhpDoc this}}} public function {{methodName}}({{{signatureParams}}}): {{returnType}} { {{#if requiresAtLeastOneParameter}} - if ({{#each parameters}}{{#unless @first}} && {{/unless}}${{name}} === null{{/each}}) { + if ({{#each atLeastOneParameterNames}}{{#unless @first}} && {{/unless}}${{this}} === null{{/each}}) { throw new \InvalidArgumentException("At least one parameter is required for {{path}}"); } {{/if}} diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 8407fc4a..f58c5ef9 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -40,6 +40,7 @@ export interface MethodLayoutContext { returnType: string hasParams: boolean requiresAtLeastOneParameter: boolean + atLeastOneParameterNames: string[] signatureParams: string usesActionAttempt: boolean usesOnResponse: boolean @@ -60,6 +61,8 @@ export interface RouteLayoutContext extends ClientLayoutContext { useStatements: string[] } +const paginationParameters = new Set(['limit', 'page_cursor']) + const waitForActionAttemptParameter = { name: 'wait_for_action_attempt', type: 'bool|array|null', @@ -124,6 +127,10 @@ const getMethodLayoutContext = ( .concat(usesOnResponse ? ['?callable $on_response = null'] : []) .join(', ') + const atLeastOneParameterNames = sortedParameters + .map(({ name }) => name) + .filter((name) => !paginationParameters.has(name)) + const endpointParameters = sortedParameters.map( ({ name, type, phpDocType, description, isOptional, isNullable }) => ({ name, @@ -164,7 +171,9 @@ const getMethodLayoutContext = ( path, returnType, hasParams: parameters.length > 0, - requiresAtLeastOneParameter: method.requiresAtLeastOneParameter, + requiresAtLeastOneParameter: + method.requiresAtLeastOneParameter && atLeastOneParameterNames.length > 0, + atLeastOneParameterNames, signatureParams, usesActionAttempt, usesOnResponse, diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index c5211607..9fb239cb 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -386,8 +386,6 @@ public function list( $access_method_id === null && $customer_key === null && $device_id === null && - $limit === null && - $page_cursor === null && $search === null && $user_identifier_key === null ) { diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 41c22195..2e3210f6 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -228,8 +228,6 @@ public function list( $access_grant_key === null && $acs_entrance_id === null && $device_id === null && - $limit === null && - $page_cursor === null && $space_id === null ) { throw new \InvalidArgumentException( diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index f2c8177c..671745be 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -149,7 +149,6 @@ public function list( $event_ids === null && $event_type === null && $event_types === null && - $limit === null && $since === null && $space_id === null && $space_ids === null && diff --git a/tests/RequiredParametersTest.php b/tests/RequiredParametersTest.php new file mode 100644 index 00000000..e3c5d584 --- /dev/null +++ b/tests/RequiredParametersTest.php @@ -0,0 +1,165 @@ + [], "events" => []]), + ]); + + return [ + Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + retries: 0, + ), + $recorder, + ]; + } + + private function assertRejected(callable $call, string $path): void + { + [$seam, $recorder] = $this->recorded(); + + try { + $call($seam); + $this->fail("Expected InvalidArgumentException for $path"); + } catch (\InvalidArgumentException $error) { + $this->assertSame( + "At least one parameter is required for $path", + $error->getMessage(), + ); + } + + $this->assertSame(0, $recorder->request_count()); + } + + public function testRejectsACallThatNamesNothing(): void + { + $this->assertRejected( + fn(Seam $seam) => $seam->access_codes->list(), + "/access_codes/list", + ); + } + + /** + * @dataProvider paginationOnlyCalls + */ + public function testPaginationParamsAloneDoNotSatisfyTheGuard( + callable $call, + string $path, + ): void { + $this->assertRejected($call, $path); + } + + public static function paginationOnlyCalls(): array + { + return [ + "limit" => [ + fn(Seam $seam) => $seam->access_codes->list(limit: 20), + "/access_codes/list", + ], + "page cursor" => [ + fn(Seam $seam) => $seam->access_codes->list( + page_cursor: "cursor", + ), + "/access_codes/list", + ], + "limit and page cursor" => [ + fn(Seam $seam) => $seam->access_codes->list( + limit: 20, + page_cursor: "cursor", + ), + "/access_codes/list", + ], + "limit on an unpaginated list" => [ + fn(Seam $seam) => $seam->events->list(limit: 20), + "/events/list", + ], + ]; + } + + /** + * @dataProvider filteredCalls + */ + public function testAcceptsACallThatNamesAFilter( + callable $call, + string $expected_query, + ): void { + [$seam, $recorder] = $this->recorded(); + + $call($seam); + + $this->assertSame(1, $recorder->request_count()); + $this->assertStringContainsString( + $expected_query, + $recorder->request()->getUri()->getQuery(), + ); + } + + public static function filteredCalls(): array + { + return [ + "a filter" => [ + fn(Seam $seam) => $seam->access_codes->list( + device_id: "device-1", + ), + "device_id=device-1", + ], + "a filter alongside pagination" => [ + fn(Seam $seam) => $seam->access_codes->list( + device_id: "device-1", + limit: 20, + ), + "device_id=device-1", + ], + "a filter on an unpaginated list" => [ + fn(Seam $seam) => $seam->events->list( + event_type: "device.connected", + ), + "event_type=device.connected", + ], + ]; + } + + public function testAPaginatorOverAnUnfilteredListIsRejectedThroughout(): void + { + [$seam] = $this->recorded(); + + $pages = $seam->createPaginator( + fn($params) => $seam->access_codes->list(...$params), + ); + + foreach ( + [ + fn() => $pages->firstPage(), + fn() => $pages->flattenToArray(), + fn() => iterator_to_array($pages->flatten()), + ] + as $call + ) { + try { + $call(); + $this->fail("Expected InvalidArgumentException"); + } catch (\InvalidArgumentException $error) { + $this->assertSame( + "At least one parameter is required for /access_codes/list", + $error->getMessage(), + ); + } + } + } +} From 66ebb9529af8e2ec8809648c50326cbaa7ec151a Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 19 Aug 2026 22:55:20 +0000 Subject: [PATCH 76/80] 4.0.0-beta.18 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9dec8762..ab887011 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.17", + "version": "4.0.0-beta.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.17", + "version": "4.0.0-beta.18", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 72467580..ca0eefc6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.17", + "version": "4.0.0-beta.18", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index 4092dd9f..aa10c7f8 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.17"; + public const VERSION = "4.0.0-beta.18"; public static function get(): string { From ac0803ec730c15b37e2d5df3070add66c231d7f8 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 19:46:37 -0700 Subject: [PATCH 77/80] feat: restore discriminated resource variants (#479) * feat: restore discriminated resource variants * fix: preserve unknown response values * fix: keep response enum values as strings * docs: explain optional response enums --- README.md | 41 +- codegen/layouts/resource.hbs | 40 +- codegen/lib/layouts/resource.ts | 122 +- codegen/lib/map-php-type.ts | 27 +- codegen/lib/resource-model.ts | 622 +- src/Resources/AccessCode.php | 2376 ++- src/Resources/AccessGrant.php | 838 +- src/Resources/AccessMethod.php | 685 +- src/Resources/AcsAccessGroup.php | 821 +- src/Resources/AcsCredential.php | 454 +- src/Resources/AcsEncoder.php | 13 +- src/Resources/AcsEntrance.php | 388 +- src/Resources/AcsSystem.php | 773 +- src/Resources/AcsUser.php | 1464 +- src/Resources/ActionAttempt.php | 3174 +++- src/Resources/ClientSession.php | 9 +- src/Resources/ConnectWebview.php | 26 +- src/Resources/ConnectedAccount.php | 772 +- src/Resources/Device.php | 6245 +++++--- src/Resources/DeviceProvider.php | 82 +- src/Resources/Event.php | 12879 +++++++++++++++- src/Resources/InstantKey.php | 14 +- src/Resources/NoiseThreshold.php | 2 +- src/Resources/Phone.php | 36 +- src/Resources/Space.php | 22 +- src/Resources/ThermostatDailyProgram.php | 8 +- src/Resources/ThermostatSchedule.php | 14 +- src/Resources/UnmanagedAccessCode.php | 1848 ++- src/Resources/UnmanagedAccessGrant.php | 832 +- src/Resources/UnmanagedAccessMethod.php | 682 +- src/Resources/UnmanagedDevice.php | 2415 ++- src/Resources/UnmanagedUserIdentity.php | 230 +- src/Resources/UserIdentity.php | 224 +- src/Resources/Webhook.php | 6 +- src/Resources/Workspace.php | 16 +- src/Routes/AccessCodesClient.php | 6 +- src/Routes/AccessGrantsClient.php | 24 +- src/Routes/AccessMethodsClient.php | 6 +- src/Routes/AcsCredentialsClient.php | 2 +- src/Routes/AcsEncodersClient.php | 4 +- src/Routes/AcsEntrancesClient.php | 4 +- src/Routes/AcsSystemsClient.php | 4 +- src/Routes/AcsUsersClient.php | 2 +- src/Routes/ActionAttemptsClient.php | 2 +- src/Routes/ClientSessionsClient.php | 18 +- src/Routes/ConnectWebviewsClient.php | 6 +- src/Routes/ConnectedAccountsClient.php | 2 +- src/Routes/CustomersClient.php | 78 +- src/Routes/DevicesClient.php | 8 +- src/Routes/DevicesUnmanagedClient.php | 6 +- src/Routes/EventsClient.php | 18 +- src/Routes/LocksClient.php | 2 +- src/Routes/NoiseSensorsClient.php | 2 +- src/Routes/SpacesClient.php | 26 +- src/Routes/ThermostatsClient.php | 2 +- src/Routes/ThermostatsDailyProgramsClient.php | 4 +- src/Routes/UserIdentitiesClient.php | 4 +- src/Routes/WebhooksClient.php | 4 +- src/SeamWebhook.php | 10 + tests/LiteralBooleanTypesTest.php | 16 +- tests/RecordTypesTest.php | 7 +- tests/ResourceTest.php | 53 +- tests/SeamWebhookTest.php | 43 + tests/WaitForActionAttemptTest.php | 24 + 64 files changed, 34504 insertions(+), 4083 deletions(-) diff --git a/README.md b/README.md index 5989b5e3..6b9816ff 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,14 @@ $webhook = new Seam\SeamWebhook($webhook_secret); try { $event = $webhook->verify($request_body, $request_headers); - print $event->event_type; + + print match (true) { + $event instanceof Seam\Resources\Event\AccessCodeCreated + => "Created access code {$event->access_code_id}", + $event::class === Seam\Resources\Event::class + => "Unknown event type {$event->event_type}", + default => "Received {$event->event_type}", + }; } catch (Svix\Exception\WebhookVerificationException $error) { http_response_code(401); } catch (Seam\InvalidWebhookPayloadError $error) { @@ -339,6 +346,38 @@ try { ### Advanced Usage +#### Enum values + +Enum-valued response properties are strings, so they work with ordinary string +comparisons and remain forward-compatible when the API adds a value: + +```php +if ($action_attempt->status === "pending") { + // The action is still running. +} +``` + +The SDK also generates backed enums for autocomplete, discovery of known +values, and optional validation. Use the enum's `value` when comparing, or +`tryFrom()` to convert a response value: + +```php +use Seam\Resources\ActionAttempt\Status; +use Seam\Resources\Event\EventType; + +if ($action_attempt->status === Status::PENDING->value) { + // The action is still running. +} + +$status = Status::tryFrom($action_attempt->status); +$event_type = EventType::tryFrom($event->event_type); +``` + +`tryFrom()` returns `null` for a value introduced after the installed SDK was +released; the original response property still contains the raw string. Enum +properties also reference their companion enum in PHPDoc for IDE and static +analysis hints. + #### Setting the endpoint The endpoint may be set with the `SEAM_ENDPOINT` environment variable, or diff --git a/codegen/layouts/resource.hbs b/codegen/layouts/resource.hbs index 271e4355..f7f3a95a 100644 --- a/codegen/layouts/resource.hbs +++ b/codegen/layouts/resource.hbs @@ -6,18 +6,35 @@ namespace {{namespace}} { {{#if (hasPhpDoc this)}} {{{resourcePhpDoc this}}} {{/if}} - class {{className}} + {{#if isFinal}}final {{/if}}class {{className}}{{#if extendsName}} extends {{extendsName}}{{/if}} { public static function from_json(mixed $json): {{className}}|null { if (!$json) { return null; } +{{#if factory}} + $discriminant = is_string($json->{{factory.discriminant}} ?? null) + ? {{factory.enumType}}::tryFrom($json->{{factory.discriminant}}) + : null; + + return match ($discriminant) { +{{#each factory.variants}} + {{enumCase}} => {{className}}::from_json($json), +{{/each}} + default => new self( +{{#each fromJsonProps}} + {{{this}}} +{{/each}} + ), + }; +{{else}} return new self( {{#each fromJsonProps}} {{{this}}} {{/each}} ); +{{/if}} } public function __construct( @@ -28,9 +45,30 @@ namespace {{namespace}} { {{{declaration}}} {{/each}} ) { +{{#if parentArgs}} + parent::__construct( +{{#each parentArgs}} + {{{this}}} +{{/each}} + ); +{{/if}} } } +{{/each}} +{{#each enums}} + enum {{enumName}}: string + { +{{#each cases}} +{{#if description}} + /** + * {{description}} + */ +{{/if}} + case {{name}} = {{{value}}}; +{{/each}} + } + {{/each}} } diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index 03ea8c16..ee9d6284 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -1,16 +1,9 @@ -// Builds the template context for resource files (src/Resources/{Name}.php): -// the resource class and the nested classes for its object properties, grouped -// into one braced namespace block per namespace. Each class contributes its -// from_json body lines and constructor parameter lines. -// -// Whether a property is required is carried on isOptional: an optional one -// gets a null default, a required one does not. Constructor parameters are -// emitted required first, since PHP deprecates an optional parameter -// declared before a required one. +// Builds the template context for generated resource files. import type { ResourceClassProperty, ResourceClassSchema, + ResourceEnumSchema, ResourceSchema, } from '../resource-model.js' @@ -19,8 +12,18 @@ export interface ClassLayoutContext { description: string isDeprecated: boolean deprecationMessage: string + isFinal: boolean + extendsName: string + factory?: FactoryLayoutContext fromJsonProps: string[] constructorParams: ConstructorParamLayoutContext[] + parentArgs: string[] +} + +export interface FactoryLayoutContext { + discriminant: string + enumType: string + variants: Array<{ enumCase: string; className: string }> } export interface ConstructorParamLayoutContext { @@ -31,9 +34,15 @@ export interface ConstructorParamLayoutContext { deprecationMessage: string } +export interface EnumLayoutContext { + enumName: string + cases: Array<{ name: string; value: string; description: string }> +} + export interface NamespaceLayoutContext { namespace: string classes: ClassLayoutContext[] + enums: EnumLayoutContext[] } export interface ResourceLayoutContext { @@ -60,38 +69,40 @@ const generateFromJsonProp = (property: ResourceClassProperty): string => { const generateConstructorParam = ( property: ResourceClassProperty, + promote: boolean, ): ConstructorParamLayoutContext => { - let declaration: string + let type: string let phpDocType = '' - // Resource decoding remains tolerant of sparse response envelopes. Optional - // properties can also be omitted when constructing a resource directly; - // nullable properties retain the same null-safe representation. const defaultValue = property.isOptional ? ' = null' : '' switch (property.kind) { case 'objectReference': - declaration = `public ${property.referenceName}|null $${property.name}${defaultValue},` + type = `${property.referenceName}|null` break case 'listReference': - declaration = `public array${property.isOptional ? '|null' : ''} $${property.name}${defaultValue},` + type = `array${property.isOptional ? '|null' : ''}` + phpDocType = `list<${property.referenceName}>${property.isOptional ? '|null' : ''}` break case 'record': + type = `${property.phpType}|null` phpDocType = `${property.phpDocType}|null` - declaration = `public ${property.phpType}|null $${property.name}${defaultValue},` break case 'value': { - const { phpType } = property - const nullSuffix = phpType === 'mixed' ? '' : '|null' - declaration = `public ${phpType}${nullSuffix} $${property.name}${defaultValue},` + const nullSuffix = property.phpType === 'mixed' ? '' : '|null' + type = `${property.phpType}${nullSuffix}` + phpDocType = + property.phpDocType === '' || property.phpDocType === property.phpType + ? '' + : `${property.phpDocType}|null` break } } return { - declaration, + declaration: `${promote ? 'public ' : ''}${type} $${property.name}${defaultValue},`, phpDocType, description: property.description, isDeprecated: property.isDeprecated, @@ -102,52 +113,69 @@ const generateConstructorParam = ( const getClassLayoutContext = ( schema: ResourceClassSchema, ): ClassLayoutContext => { - const sorted = [...schema.properties].sort((a, b) => - a.name.localeCompare(b.name), + const inheritedNames = new Set( + schema.inheritedProperties.map(({ name }) => name), ) - - const parameterOrder = sortRequiredFirst(sorted) + const properties = sortRequiredFirst([ + ...schema.inheritedProperties, + ...schema.properties, + ]) return { className: schema.name, description: schema.description, isDeprecated: schema.isDeprecated, deprecationMessage: schema.deprecationMessage, - fromJsonProps: sorted.map(generateFromJsonProp), - constructorParams: parameterOrder.map(generateConstructorParam), + isFinal: schema.isFinal, + extendsName: schema.extendsName, + ...(schema.factory == null ? {} : { factory: schema.factory }), + fromJsonProps: properties.map(generateFromJsonProp), + constructorParams: properties.map((property) => + generateConstructorParam(property, !inheritedNames.has(property.name)), + ), + parentArgs: schema.inheritedProperties.map( + ({ name }) => `${name}: $${name},`, + ), } } +const getEnumLayoutContext = ( + schema: ResourceEnumSchema, +): EnumLayoutContext => ({ + enumName: schema.name, + cases: schema.cases.map((enumCase) => ({ + ...enumCase, + value: JSON.stringify(enumCase.value), + })), +}) + const sortRequiredFirst = ( properties: ResourceClassProperty[], -): ResourceClassProperty[] => [ - ...properties.filter(({ isOptional }) => !isOptional), - ...properties.filter(({ isOptional }) => isOptional), -] +): ResourceClassProperty[] => + [...properties].sort( + (a, b) => + Number(a.isOptional) - Number(b.isOptional) || + a.name.localeCompare(b.name), + ) export const setResourceLayoutContext = ( resource: ResourceSchema, ): ResourceLayoutContext => { - // First appearance order, so the resource class leads the file and every - // owning namespace precedes the namespaces nested inside it. - const namespaces = new Map() - - for (const schema of resource.classes) { - const classes = namespaces.get(schema.namespace) - const context = getClassLayoutContext(schema) + const namespaces = new Map() - if (classes == null) { - namespaces.set(schema.namespace, [context]) - continue + for (const declaration of resource.declarations) { + let context = namespaces.get(declaration.namespace) + if (context == null) { + context = { namespace: declaration.namespace, classes: [], enums: [] } + namespaces.set(declaration.namespace, context) } - classes.push(context) + if (declaration.kind === 'class') { + context.classes.push(getClassLayoutContext(declaration)) + } else { + context.enums.push(getEnumLayoutContext(declaration)) + } } - return { - namespaces: [...namespaces.entries()].map(([namespace, classes]) => ({ - namespace, - classes, - })), - } + return { namespaces: [...namespaces.values()] } } diff --git a/codegen/lib/map-php-type.ts b/codegen/lib/map-php-type.ts index 0ea504a7..f7f206e0 100644 --- a/codegen/lib/map-php-type.ts +++ b/codegen/lib/map-php-type.ts @@ -7,7 +7,11 @@ type RecordValueType = NonNullable< Extract['valueTypes'] >[number] -export const getPhpType = (schema: Parameter | Property): string => { +export const getPhpType = ( + schema: Parameter | Property, + enumType = 'string', +): string => { + if (schema.format === 'enum') return enumType if (schema.format === 'record' && !('resourceType' in schema)) { return 'array|\\stdClass' } @@ -34,6 +38,10 @@ export const getPhpType = (schema: Parameter | Property): string => { } export const getPhpDocType = (schema: Parameter | Property): string => { + if (schema.format === 'list') { + return `list<${getListItemPhpType(schema)}>` + } + if (schema.format !== 'record' || 'resourceType' in schema) { return getPhpType(schema) } @@ -45,6 +53,23 @@ export const getPhpDocType = (schema: Parameter | Property): string => { return `array|\\stdClass` } +const getListItemPhpType = ( + schema: Extract, +): string => { + switch (schema.itemFormat) { + case 'number': + return 'isItemInt' in schema && schema.isItemInt ? 'int' : 'float' + case 'boolean': + return 'bool' + case 'object': + case 'record': + case 'discriminated_object': + return 'array|\\stdClass' + default: + return 'string' + } +} + const getRecordValuePhpType = (type: RecordValueType): string => { switch (type) { case 'string': diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index 5fb92983..e2e49899 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -1,25 +1,25 @@ // Builds the resource class model for src/Resources from the blueprint. // -// Each blueprint resource becomes a PHP class in its own file. A nested object -// property, or a list of objects, becomes its own class named after the -// property alone and declared in the namespace of the class that owns it, so -// the device battery is Seam\Resources\Device\Properties\Battery. Nesting the -// classes keeps Seam\Resources free of the hundreds of names that exist only -// to type a property, and lets two properties of the same name at different -// depths keep their own shapes. -// -// Discriminated unions (events, action attempts, and discriminated object -// lists) are flattened into a single class holding the union of the variant -// properties. - -import type { Blueprint, Property, Resource } from '@seamapi/blueprint' -import { pascalCase } from 'change-case' +// Nested objects are emitted recursively in the namespace of their owner. +// Discriminated resources and object lists are emitted as an abstract base, +// one final class per variant, and an unknown-discriminant fallback. + +import type { + Blueprint, + EnumProperty, + Property, + Resource, +} from '@seamapi/blueprint' +import { constantCase, pascalCase } from 'change-case' import { getPhpDocType, getPhpType } from './map-php-type.js' -import { mergeProperties } from './merge-properties.js' export type ResourceClassProperty = - | ({ kind: 'value'; phpType: string } & ResourceClassPropertyMetadata) + | ({ + kind: 'value' + phpType: string + phpDocType: string + } & ResourceClassPropertyMetadata) | ({ kind: 'record' phpType: string @@ -43,18 +43,43 @@ interface ResourceClassPropertyMetadata { deprecationMessage: string } +export interface ResourceFactoryVariant { + enumCase: string + className: string +} + +export interface ResourceFactory { + discriminant: string + enumType: string + variants: ResourceFactoryVariant[] +} + export interface ResourceClassSchema { + kind: 'class' name: string namespace: string description: string isDeprecated: boolean deprecationMessage: string + isFinal: boolean + extendsName: string properties: ResourceClassProperty[] + inheritedProperties: ResourceClassProperty[] + factory?: ResourceFactory +} + +export interface ResourceEnumSchema { + kind: 'enum' + name: string + namespace: string + cases: Array<{ name: string; value: string; description: string }> } +export type ResourceDeclaration = ResourceClassSchema | ResourceEnumSchema + export interface ResourceSchema { name: string - classes: ResourceClassSchema[] + declarations: ResourceDeclaration[] } export interface ResourceModel { @@ -63,15 +88,9 @@ export interface ResourceModel { } const rootNamespace = 'Seam\\Resources' - -// A cyclic schema would recurse forever. Real shapes are nowhere near this -// deep, so exceeding it means the blueprint is cyclic rather than nested. const maxDepth = 16 -// PHP reserves these as type names or keywords, so a class cannot be called -// any of them. Class names are case insensitive, so the check has to be too. const reservedClassNames = new Set([ - // Type names. 'array', 'bool', 'callable', @@ -90,7 +109,6 @@ const reservedClassNames = new Set([ 'string', 'true', 'void', - // Keywords. 'abstract', 'and', 'as', @@ -164,83 +182,94 @@ interface ClassDocs { deprecationMessage: string } -interface BuiltClass { - schema: ResourceClassSchema - nestedClasses: BuiltClass[] +interface BuiltDeclaration { + declaration: ResourceDeclaration + nestedDeclarations: BuiltDeclaration[] } -export const createResourceModel = (blueprint: Blueprint): ResourceModel => { - const baseResources = new Map() - - for (const resource of blueprint.resources) { - baseResources.set(resource.resourceType, resource.properties) - } - - // The blueprint models events and action attempts as one resource per - // variant. The PHP SDK has a single class for each, so the variants are - // merged into one schema. - const { events } = blueprint - if (events.length > 0) { - baseResources.set( - 'event', - mergeProperties( - events.map((event) => event.properties), - 'event', - ), - ) - } - - const { actionAttempts } = blueprint - if (actionAttempts.length > 0) { - baseResources.set( - 'action_attempt', - mergeProperties( - actionAttempts.map((actionAttempt) => actionAttempt.properties), - 'action_attempt', - ), - ) - } - - const baseResourceTypes = [...baseResources.keys()].sort() +interface VariantInput { + properties: Property[] + description: string +} - const resources = baseResourceTypes.map((resourceType) => { +export const createResourceModel = (blueprint: Blueprint): ResourceModel => { + const discriminatedTypes = new Set( + [...blueprint.events, ...blueprint.actionAttempts].map( + ({ resourceType }) => resourceType, + ), + ) + const resources = new Map( + blueprint.resources + .filter(({ resourceType }) => !discriminatedTypes.has(resourceType)) + .map((resource) => [resource.resourceType, resource] as const), + ) + + const resourceTypes: string[] = [ + ...resources.keys(), + ...(blueprint.events.length > 0 ? ['event'] : []), + ...(blueprint.actionAttempts.length > 0 ? ['action_attempt'] : []), + ].sort() + + const schemas = resourceTypes.map((resourceType): ResourceSchema => { const name = pascalCase(resourceType) - const sourceResource = getSourceResource(blueprint, resourceType) - - const built = buildClass( - name, - rootNamespace, - baseResources.get(resourceType) ?? [], - resourceType, - 0, - { - description: sourceResource?.description ?? '', - isDeprecated: sourceResource?.isDeprecated ?? false, - deprecationMessage: sourceResource?.deprecationMessage ?? '', - }, - ) + let built: BuiltDeclaration + + if (resourceType === 'event') { + built = buildDiscriminatedClass( + name, + rootNamespace, + blueprint.events, + 'event_type', + resourceType, + 0, + { + description: 'Base class for events returned by the Seam API.', + isDeprecated: false, + deprecationMessage: '', + }, + ) + } else if (resourceType === 'action_attempt') { + built = buildDiscriminatedClass( + name, + rootNamespace, + blueprint.actionAttempts, + 'action_type', + resourceType, + 0, + { + description: + 'Base class for actions whose completion is tracked asynchronously.', + isDeprecated: false, + deprecationMessage: '', + }, + true, + ) + } else { + const resource = resources.get(resourceType) + built = buildClass( + name, + rootNamespace, + resource?.properties ?? [], + resourceType, + 0, + docsFor(resource), + ) + } - return { name, classes: flattenClasses(built) } + return { name, declarations: flattenDeclarations(built) } }) return { - resourceNames: resources.map((resource) => resource.name), - resources, + resourceNames: schemas.map(({ name }) => name), + resources: schemas, } } -const getSourceResource = ( - blueprint: Blueprint, - resourceType: string, -): Resource | undefined => - blueprint.resources.find( - (resource) => resource.resourceType === resourceType, - ) ?? - (resourceType === 'event' - ? blueprint.events[0] - : resourceType === 'action_attempt' - ? blueprint.actionAttempts[0] - : undefined) +const docsFor = (resource: Resource | undefined): ClassDocs => ({ + description: resource?.description ?? '', + isDeprecated: resource?.isDeprecated ?? false, + deprecationMessage: resource?.deprecationMessage ?? '', +}) const buildClass = ( className: string, @@ -249,128 +278,367 @@ const buildClass = ( path: string, depth: number, docs: ClassDocs, -): BuiltClass => { - if (depth > maxDepth) { - throw new Error( - `Cannot generate ${path}: nesting exceeded a depth of ${maxDepth}, which means the schema is cyclic`, - ) - } + options: { + isFinal?: boolean + extendsName?: string + inheritedProperties?: ResourceClassProperty[] + factory?: ResourceFactory + } = {}, +): BuiltDeclaration => { + assertDepth(path, depth) - // A class at namespace N with short name S owns its children at N\S. const nestedNamespace = `${namespace}\\${className}` - const nestedClasses: BuiltClass[] = [] - const takenClassNames = new Set() + const nestedDeclarations: BuiltDeclaration[] = [] + const takenNames = new Set() const properties = classProperties.map((property): ResourceClassProperty => { - const metadata = { - name: property.name, - description: property.description, - isOptional: property.isOptional, - isNullable: property.isNullable, - isDeprecated: property.isDeprecated, - deprecationMessage: property.deprecationMessage, - } - - const nestedProperties = getNestedProperties( - property, - `${path}.${property.name}`, - ) - - if (nestedProperties == null) { - return property.format === 'record' && !('resourceType' in property) - ? { - ...metadata, - kind: 'record', - phpType: getPhpType(property), - phpDocType: getPhpDocType(property), - } - : { ...metadata, kind: 'value', phpType: getPhpType(property) } - } - - const nestedClassName = pascalCase(property.name) + const metadata = propertyMetadata(property) const nestedPath = `${path}.${property.name}` + const nestedClassName = pascalCase(property.name) - if (reservedClassNames.has(nestedClassName.toLowerCase())) { - throw new Error( - `Cannot generate ${nestedPath}: ${nestedClassName} is reserved in PHP`, + if (property.format === 'enum') { + assertAvailableName( + nestedClassName, + nestedPath, + nestedNamespace, + takenNames, ) + const enumType = `\\${nestedNamespace}\\${nestedClassName}` + nestedDeclarations.push( + buildEnum(nestedClassName, nestedNamespace, property), + ) + return { + ...metadata, + kind: 'value', + phpType: 'string', + phpDocType: `value-of<${enumType}>|string`, + } } - if (takenClassNames.has(nestedClassName)) { - throw new Error( - `Cannot generate ${nestedPath}: ${nestedClassName} is already used by a sibling property in ${nestedNamespace}`, + if ( + property.format === 'list' && + property.itemFormat === 'discriminated_object' + ) { + assertAvailableName( + nestedClassName, + nestedPath, + nestedNamespace, + takenNames, + ) + nestedDeclarations.push( + buildDiscriminatedClass( + nestedClassName, + nestedNamespace, + property.variants, + property.discriminator, + nestedPath, + depth + 1, + propertyDocs(property), + ), ) + return { + ...metadata, + kind: 'listReference', + referenceName: `\\${nestedNamespace}\\${nestedClassName}`, + } } - takenClassNames.add(nestedClassName) - - nestedClasses.push( - buildClass( + const nestedProperties = getNestedProperties(property) + if (nestedProperties != null) { + assertAvailableName( nestedClassName, - nestedNamespace, - nestedProperties, nestedPath, - depth + 1, - { - description: property.description, - isDeprecated: property.isDeprecated, - deprecationMessage: property.deprecationMessage, - }, - ), - ) - - // Referenced relative to the namespace the owning class is declared in, - // so PHP resolves Device\Properties from within Seam\Resources. - return { - ...metadata, - kind: property.format === 'list' ? 'listReference' : 'objectReference', - referenceName: `${className}\\${nestedClassName}`, + nestedNamespace, + takenNames, + ) + nestedDeclarations.push( + buildClass( + nestedClassName, + nestedNamespace, + nestedProperties, + nestedPath, + depth + 1, + propertyDocs(property), + ), + ) + const referenceName = `\\${nestedNamespace}\\${nestedClassName}` + return { + ...metadata, + kind: property.format === 'list' ? 'listReference' : 'objectReference', + referenceName, + } } + + return property.format === 'record' && !('resourceType' in property) + ? { + ...metadata, + kind: 'record', + phpType: getPhpType(property), + phpDocType: getPhpDocType(property), + } + : { + ...metadata, + kind: 'value', + phpType: getPhpType(property), + phpDocType: getPhpDocType(property), + } }) return { - schema: { + declaration: { + kind: 'class', name: className, namespace, ...docs, + isFinal: options.isFinal ?? false, + extendsName: options.extendsName ?? '', properties, + inheritedProperties: options.inheritedProperties ?? [], + ...(options.factory == null ? {} : { factory: options.factory }), }, - nestedClasses, + nestedDeclarations, } } -// Decides whether a property earns a class of its own. An object with no -// documented properties stays a plain value rather than becoming an empty -// class. -const getNestedProperties = ( - property: Property, +const buildDiscriminatedClass = ( + className: string, + namespace: string, + variants: VariantInput[], + discriminator: string, path: string, -): Property[] | undefined => { - if (property.format === 'object') { - return property.properties.length > 0 ? property.properties : undefined + depth: number, + docs: ClassDocs, + actionAttempt = false, +): BuiltDeclaration => { + assertDepth(path, depth) + if (variants.length === 0) { + return buildClass(className, namespace, [], path, depth, docs) } - if (property.format === 'list') { - if (property.itemFormat === 'object') { - return property.itemProperties.length > 0 - ? property.itemProperties - : undefined + const variantInfo = variants.map((variant) => { + const property = variant.properties.find( + ({ name }) => name === discriminator, + ) + if (property?.format !== 'enum' || property.values.length !== 1) { + throw new Error( + `Cannot generate ${path}: ${discriminator} is not a single-value enum`, + ) } + return { variant, value: property.values[0]?.name ?? '' } + }) - if (property.itemFormat === 'discriminated_object') { - const itemProperties = mergeProperties( - property.variants.map((variant) => variant.properties), - path, + const commonNames = new Set( + variants[0]?.properties + .filter((property) => + variants.every((variant) => { + const candidate = variant.properties.find( + ({ name }) => name === property.name, + ) + return ( + candidate != null && + (property.name === discriminator || + (actionAttempt && property.name === 'error') || + propertyShape(candidate) === propertyShape(property)) + ) + }), ) - return itemProperties.length > 0 ? itemProperties : undefined - } + .map(({ name }) => name) ?? [], + ) + + const commonProperties = (variants[0]?.properties ?? []) + .filter(({ name }) => commonNames.has(name)) + .map((property) => { + if (property.format !== 'enum') return property + const values = uniqueEnumValues( + variants.flatMap( + (variant) => + ( + variant.properties.find(({ name }) => name === property.name) as + EnumProperty | undefined + )?.values ?? [], + ), + ) + return { ...property, values } + }) + .map((property) => { + if (!actionAttempt || !['error', 'result'].includes(property.name)) { + return property + } + return { + ...property, + description: `${property.description}${property.description === '' ? '' : ' '}Null while the action attempt is pending or when this value does not apply.`, + } + }) + + const discriminantProperty = commonProperties.find( + ({ name }) => name === discriminator, + ) + if (discriminantProperty?.format !== 'enum') { + throw new Error(`Cannot generate ${path}: missing ${discriminator}`) + } + + const enumName = pascalCase(discriminator) + const enumType = `\\${namespace}\\${className}\\${enumName}` + const factory: ResourceFactory = { + discriminant: discriminator, + enumType, + variants: variantInfo.map(({ value }) => ({ + enumCase: `${enumType}::${enumCaseName(value)}`, + className: `\\${namespace}\\${className}\\${pascalCase(value)}`, + })), } + const built = buildClass( + className, + namespace, + commonProperties, + path, + depth, + { + ...docs, + description: `${docs.description}${docs.description === '' ? '' : ' '}Known ${discriminator} values use subclasses; unknown values use this base class and retain their raw discriminator.`, + }, + { factory }, + ) + const base = built.declaration + if (base.kind !== 'class') throw new Error(`Cannot generate ${path}`) + const baseName = `\\${namespace}\\${className}` + for (const { variant, value } of variantInfo) { + const ownProperties = variant.properties + .filter(({ name }) => !commonNames.has(name)) + .map((property) => { + if (!actionAttempt || property.name !== 'result') return property + return { + ...property, + description: `${property.description}${property.description === '' ? '' : ' '}Null while the action attempt is pending or when this value does not apply.`, + } + }) + built.nestedDeclarations.push( + buildClass( + pascalCase(value), + `${namespace}\\${className}`, + ownProperties, + `${path}.${value}`, + depth + 1, + { + description: variant.description, + isDeprecated: false, + deprecationMessage: '', + }, + { + isFinal: true, + extendsName: baseName, + inheritedProperties: base.properties, + }, + ), + ) + } + + return built +} + +const buildEnum = ( + name: string, + namespace: string, + property: EnumProperty, +): BuiltDeclaration => ({ + declaration: { + kind: 'enum', + name, + namespace, + cases: uniqueEnumValues(property.values).map((value) => ({ + name: enumCaseName(value.name), + value: value.name, + description: value.description, + })), + }, + nestedDeclarations: [], +}) + +const enumCaseName = (value: string): string => { + const name = constantCase(value) + return /^[A-Z_]/.test(name) && !reservedClassNames.has(name.toLowerCase()) + ? name + : `VALUE_${name}` +} + +const uniqueEnumValues = (values: T[]): T[] => [ + ...new Map(values.map((value) => [value.name, value])).values(), +] + +const propertyShape = (property: Property): string => + JSON.stringify(property, (key, value: unknown) => + [ + 'description', + 'isDeprecated', + 'deprecationMessage', + 'isUndocumented', + 'undocumentedMessage', + 'isDraft', + 'draftMessage', + 'propertyGroupKey', + ].includes(key) + ? undefined + : value, + ) + +const propertyMetadata = ( + property: Property, +): ResourceClassPropertyMetadata => ({ + name: property.name, + description: property.description, + isOptional: property.isOptional, + isNullable: property.isNullable, + isDeprecated: property.isDeprecated, + deprecationMessage: property.deprecationMessage, +}) + +const propertyDocs = (property: Property): ClassDocs => ({ + description: property.description, + isDeprecated: property.isDeprecated, + deprecationMessage: property.deprecationMessage, +}) + +const getNestedProperties = (property: Property): Property[] | undefined => { + if (property.format === 'object') { + return property.properties.length > 0 ? property.properties : undefined + } + if (property.format === 'list' && property.itemFormat === 'object') { + return property.itemProperties.length > 0 + ? property.itemProperties + : undefined + } return undefined } -// Depth first, so an owning class always precedes the children it references. -const flattenClasses = (built: BuiltClass): ResourceClassSchema[] => [ - built.schema, - ...built.nestedClasses.flatMap(flattenClasses), +const assertDepth = (path: string, depth: number): void => { + if (depth > maxDepth) { + throw new Error( + `Cannot generate ${path}: nesting exceeded a depth of ${maxDepth}, which means the schema is cyclic`, + ) + } +} + +const assertAvailableName = ( + name: string, + path: string, + namespace: string, + takenNames: Set, +): void => { + if (reservedClassNames.has(name.toLowerCase())) { + throw new Error(`Cannot generate ${path}: ${name} is reserved in PHP`) + } + if (takenNames.has(name.toLowerCase())) { + throw new Error( + `Cannot generate ${path}: ${name} is already used in ${namespace}`, + ) + } + takenNames.add(name.toLowerCase()) +} + +const flattenDeclarations = ( + built: BuiltDeclaration, +): ResourceDeclaration[] => [ + built.declaration, + ...built.nestedDeclarations.flatMap(flattenDeclarations), ] diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index bbdb20bc..1188cccd 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -25,19 +25,10 @@ public static function from_json(mixed $json): AccessCode|null common_code_key: $json->common_code_key ?? null, created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, - dormakaba_oracode_metadata: isset( - $json->dormakaba_oracode_metadata, - ) - ? AccessCode\DormakabaOracodeMetadata::from_json( - $json->dormakaba_oracode_metadata, - ) - : null, - ends_at: $json->ends_at ?? null, errors: array_map( - fn($e) => AccessCode\Errors::from_json($e), + fn($e) => \Seam\Resources\AccessCode\Errors::from_json($e), $json->errors ?? [], ), - is_backup: $json->is_backup ?? null, is_backup_access_code_available: $json->is_backup_access_code_available ?? null, is_external_modification_allowed: $json->is_external_modification_allowed ?? @@ -45,24 +36,39 @@ public static function from_json(mixed $json): AccessCode|null is_managed: $json->is_managed ?? null, is_offline_access_code: $json->is_offline_access_code ?? null, is_one_time_use: $json->is_one_time_use ?? null, - is_scheduled_on_device: $json->is_scheduled_on_device ?? null, - is_waiting_for_code_assignment: $json->is_waiting_for_code_assignment ?? - null, name: $json->name ?? null, pending_mutations: array_map( - fn($p) => AccessCode\PendingMutations::from_json($p), + fn( + $p, + ) => \Seam\Resources\AccessCode\PendingMutations::from_json( + $p, + ), $json->pending_mutations ?? [], ), - pulled_backup_access_code_id: $json->pulled_backup_access_code_id ?? - null, - starts_at: $json->starts_at ?? null, status: $json->status ?? null, type: $json->type ?? null, warnings: array_map( - fn($w) => AccessCode\Warnings::from_json($w), + fn($w) => \Seam\Resources\AccessCode\Warnings::from_json( + $w, + ), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, + dormakaba_oracode_metadata: isset( + $json->dormakaba_oracode_metadata, + ) + ? \Seam\Resources\AccessCode\DormakabaOracodeMetadata::from_json( + $json->dormakaba_oracode_metadata, + ) + : null, + ends_at: $json->ends_at ?? null, + is_backup: $json->is_backup ?? null, + is_scheduled_on_device: $json->is_scheduled_on_device ?? null, + is_waiting_for_code_assignment: $json->is_waiting_for_code_assignment ?? + null, + pulled_backup_access_code_id: $json->pulled_backup_access_code_id ?? + null, + starts_at: $json->starts_at ?? null, ); } @@ -89,6 +95,8 @@ public function __construct( public string|null $device_id, /** * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * + * @var list<\Seam\Resources\AccessCode\Errors> */ public array $errors, /** @@ -117,18 +125,26 @@ public function __construct( public string|null $name, /** * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. + * + * @var list<\Seam\Resources\AccessCode\PendingMutations> */ public array $pending_mutations, /** * Current status of the access code within the operational lifecycle. Values are `setting`, a transitional phase that indicates that the code is being configured or activated; `set`, which indicates that the code is active and operational; `unset`, which indicates a deactivated or unused state, either before activation or after deliberate deactivation; `removing`, which indicates a transitional period in which the code is being deleted or made inactive; and `unknown`, which indicates an indeterminate state, due to reasons such as system errors or incomplete data, that highlights a potential need for system review or troubleshooting. See also [Lifecycle of Access Codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes). + * + * @var value-of<\Seam\Resources\AccessCode\Status>|string|null */ public string|null $status, /** * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. + * + * @var value-of<\Seam\Resources\AccessCode\Type>|string|null */ public string|null $type, /** * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * + * @var list<\Seam\Resources\AccessCode\Warnings> */ public array $warnings, /** @@ -138,7 +154,7 @@ public function __construct( /** * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. */ - public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, + public \Seam\Resources\AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Date and time after which the time-bound access code becomes inactive. */ @@ -228,7 +244,7 @@ public function __construct( } /** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -237,70 +253,120 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_access_code_error: $json->is_access_code_error ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - managed_access_code_id: $json->managed_access_code_id ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => Errors\ModifiedFields::from_json($m), - $json->modified_fields ?? [], + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\AccessCode\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessCode\Errors\ErrorCode::PROVIDER_ISSUE + => \Seam\Resources\AccessCode\Errors\ProviderIssue::from_json( + $json, ), - unmanaged_access_code_id: $json->unmanaged_access_code_id ?? - null, - ); + \Seam\Resources\AccessCode\Errors\ErrorCode::FAILED_TO_SET_ON_DEVICE + => \Seam\Resources\AccessCode\Errors\FailedToSetOnDevice::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::FAILED_TO_REMOVE_FROM_DEVICE + => \Seam\Resources\AccessCode\Errors\FailedToRemoveFromDevice::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::DUPLICATE_CODE_ON_DEVICE + => \Seam\Resources\AccessCode\Errors\DuplicateCodeOnDevice::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::NO_SPACE_FOR_ACCESS_CODE_ON_DEVICE + => \Seam\Resources\AccessCode\Errors\NoSpaceForAccessCodeOnDevice::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::CONFLICTING_EXTERNAL_MODIFICATION + => \Seam\Resources\AccessCode\Errors\ConflictingExternalModification::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::ACCESS_CODE_INACTIVE + => \Seam\Resources\AccessCode\Errors\AccessCodeInactive::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::ACCOUNT_DISCONNECTED + => \Seam\Resources\AccessCode\Errors\AccountDisconnected::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED + => \Seam\Resources\AccessCode\Errors\SaltoKsSubscriptionLimitExceeded::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::INSUFFICIENT_PERMISSIONS + => \Seam\Resources\AccessCode\Errors\InsufficientPermissions::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::DORMAKABA_SITES_DISCONNECTED + => \Seam\Resources\AccessCode\Errors\DormakabaSitesDisconnected::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::DEVICE_OFFLINE + => \Seam\Resources\AccessCode\Errors\DeviceOffline::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::DEVICE_REMOVED + => \Seam\Resources\AccessCode\Errors\DeviceRemoved::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::HUB_DISCONNECTED + => \Seam\Resources\AccessCode\Errors\HubDisconnected::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::DEVICE_DISCONNECTED + => \Seam\Resources\AccessCode\Errors\DeviceDisconnected::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::EMPTY_BACKUP_ACCESS_CODE_POOL + => \Seam\Resources\AccessCode\Errors\EmptyBackupAccessCodePool::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::AUGUST_LOCK_NOT_AUTHORIZED + => \Seam\Resources\AccessCode\Errors\AugustLockNotAuthorized::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::MISSING_DEVICE_CREDENTIALS + => \Seam\Resources\AccessCode\Errors\MissingDeviceCredentials::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::AUXILIARY_HEAT_RUNNING + => \Seam\Resources\AccessCode\Errors\AuxiliaryHeatRunning::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::SUBSCRIPTION_REQUIRED + => \Seam\Resources\AccessCode\Errors\SubscriptionRequired::from_json( + $json, + ), + \Seam\Resources\AccessCode\Errors\ErrorCode::BRIDGE_DISCONNECTED + => \Seam\Resources\AccessCode\Errors\BridgeDisconnected::from_json( + $json, + ), + default => new self( + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null */ public string|null $error_code, - /** - * Indicates that this is an access code error. - */ - public true|null $is_access_code_error, - public bool|null $is_connected_account_error, - public bool|null $is_device_error, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - /** - * 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. - */ - public string|null $change_type = null, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at = null, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error = null, - /** - * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $managed_access_code_id = null, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array|null $modified_fields = null, - /** - * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $unmanaged_access_code_id = null, ) {} } /** - * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. + * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. Known mutation_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class PendingMutations { @@ -309,18 +375,43 @@ public static function from_json(mixed $json): PendingMutations|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? PendingMutations\From::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - scheduled_at: $json->scheduled_at ?? null, - to: isset($json->to) - ? PendingMutations\To::from_json($json->to) - : null, - ); + $discriminant = is_string($json->mutation_code ?? null) + ? \Seam\Resources\AccessCode\PendingMutations\MutationCode::tryFrom( + $json->mutation_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessCode\PendingMutations\MutationCode::CREATING + => \Seam\Resources\AccessCode\PendingMutations\Creating::from_json( + $json, + ), + \Seam\Resources\AccessCode\PendingMutations\MutationCode::DEFERRING_CREATION + => \Seam\Resources\AccessCode\PendingMutations\DeferringCreation::from_json( + $json, + ), + \Seam\Resources\AccessCode\PendingMutations\MutationCode::DELETING + => \Seam\Resources\AccessCode\PendingMutations\Deleting::from_json( + $json, + ), + \Seam\Resources\AccessCode\PendingMutations\MutationCode::UPDATING_CODE + => \Seam\Resources\AccessCode\PendingMutations\UpdatingCode::from_json( + $json, + ), + \Seam\Resources\AccessCode\PendingMutations\MutationCode::UPDATING_NAME + => \Seam\Resources\AccessCode\PendingMutations\UpdatingName::from_json( + $json, + ), + \Seam\Resources\AccessCode\PendingMutations\MutationCode::UPDATING_TIME_FRAME + => \Seam\Resources\AccessCode\PendingMutations\UpdatingTimeFrame::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ), + }; } public function __construct( @@ -328,22 +419,21 @@ public function __construct( * Date and time at which the mutation was created. */ public string|null $created_at, - public PendingMutations\From|null $from, /** * Detailed description of the mutation. */ public string|null $message, - public string|null $mutation_code, /** - * Date and time at which Seam will attempt to program this access code on the device. + * Mutation code to indicate that Seam is in the process of setting an access code on the device. + * + * @var value-of<\Seam\Resources\AccessCode\PendingMutations\MutationCode>|string|null */ - public string|null $scheduled_at, - public PendingMutations\To|null $to, + public string|null $mutation_code, ) {} } /** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { @@ -352,16 +442,63 @@ public static function from_json(mixed $json): Warnings|null if (!$json) { return null; } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => Warnings\ModifiedFields::from_json($m), - $json->modified_fields ?? [], + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\AccessCode\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessCode\Warnings\WarningCode::CODE_ROTATES_PERIODICALLY + => \Seam\Resources\AccessCode\Warnings\CodeRotatesPeriodically::from_json( + $json, ), - warning_code: $json->warning_code ?? null, - ); + \Seam\Resources\AccessCode\Warnings\WarningCode::TIME_FRAME_ADJUSTED_FOR_UNKNOWN_TIME_ZONE + => \Seam\Resources\AccessCode\Warnings\TimeFrameAdjustedForUnknownTimeZone::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::EXTERNAL_MODIFICATION_IN_EFFECT + => \Seam\Resources\AccessCode\Warnings\ExternalModificationInEffect::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::DELAY_IN_SETTING_ON_DEVICE + => \Seam\Resources\AccessCode\Warnings\DelayInSettingOnDevice::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::DELAY_IN_REMOVING_FROM_DEVICE + => \Seam\Resources\AccessCode\Warnings\DelayInRemovingFromDevice::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::THIRD_PARTY_INTEGRATION_DETECTED + => \Seam\Resources\AccessCode\Warnings\ThirdPartyIntegrationDetected::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::IGLOO_ALGOPIN_MUST_BE_USED_WITHIN_24_HOURS + => \Seam\Resources\AccessCode\Warnings\IglooAlgopinMustBeUsedWithin_24Hours::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::MANAGEMENT_TRANSFERRED + => \Seam\Resources\AccessCode\Warnings\ManagementTransferred::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::USING_BACKUP_ACCESS_CODE + => \Seam\Resources\AccessCode\Warnings\UsingBackupAccessCode::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\AccessCode\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\AccessCode\Warnings\WarningCode::UNKNOWN_ISSUE_WITH_ACCESS_CODE + => \Seam\Resources\AccessCode\Warnings\UnknownIssueWithAccessCode::from_json( + $json, + ), + default => new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ), + }; } public function __construct( @@ -371,151 +508,2058 @@ public function __construct( public string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null */ public string|null $warning_code, - /** - * 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. - */ - public string|null $change_type = null, /** * Date and time at which Seam created the warning. */ public string|null $created_at = null, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array|null $modified_fields = null, ) {} } + + enum Status: string + { + case SETTING = "setting"; + case SET = "set"; + case VALUE_UNSET = "unset"; + case REMOVING = "removing"; + case UNKNOWN = "unknown"; + } + + enum Type: string + { + case TIME_BOUND = "time_bound"; + case ONGOING = "ongoing"; + } } namespace Seam\Resources\AccessCode\Errors { /** - * List of fields that were changed externally, with their previous and new values. + * Indicates a provider-specific issue that prevents the access code from being set or managed. Check the error message for details. */ - class ModifiedFields + final class ProviderIssue extends \Seam\Resources\AccessCode\Errors { - public static function from_json(mixed $json): ModifiedFields|null + public static function from_json(mixed $json): ProviderIssue|null { if (!$json) { return null; } return new self( - field: $json->field ?? null, - from: $json->from ?? null, - to: $json->to ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, ); } public function __construct( /** - * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null */ - public string|null $field, + string|null $error_code, /** - * The previous value of the field. + * Indicates that this is an access code error. */ - public string|null $from, + public true|null $is_access_code_error, /** - * The new value of the field. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $to, - ) {} + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } } -} -namespace Seam\Resources\AccessCode\PendingMutations { - class From + /** + * Failed to set code on device. + */ + final class FailedToSetOnDevice extends \Seam\Resources\AccessCode\Errors { - public static function from_json(mixed $json): From|null + public static function from_json(mixed $json): FailedToSetOnDevice|null { if (!$json) { return null; } return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, ); } public function __construct( /** - * Previous PIN code. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null */ - public string|null $code, + string|null $error_code, /** - * Previous end time for the access code. + * Indicates that this is an access code error. */ - public string|null $ends_at, + public true|null $is_access_code_error, /** - * Previous access code name. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $name, + string|null $message, /** - * Previous start time for the access code. + * Date and time at which Seam created the error. */ - public string|null $starts_at, - ) {} + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } } - class To + /** + * Failed to remove code from device. + */ + final class FailedToRemoveFromDevice extends + \Seam\Resources\AccessCode\Errors { - public static function from_json(mixed $json): To|null - { + public static function from_json( + mixed $json, + ): FailedToRemoveFromDevice|null { if (!$json) { return null; } return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, ); } public function __construct( /** - * New PIN code. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null */ - public string|null $code, + string|null $error_code, /** - * New end time for the access code. + * Indicates that this is an access code error. */ - public string|null $ends_at, + public true|null $is_access_code_error, /** - * New access code name. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $name, + string|null $message, /** - * New start time for the access code. + * Date and time at which Seam created the error. */ - public string|null $starts_at, - ) {} + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } } -} -namespace Seam\Resources\AccessCode\Warnings { /** - * List of fields that were changed externally, with their previous and new values. + * Duplicate access code detected on device. */ - class ModifiedFields + final class DuplicateCodeOnDevice extends \Seam\Resources\AccessCode\Errors { - public static function from_json(mixed $json): ModifiedFields|null - { + public static function from_json( + mixed $json, + ): DuplicateCodeOnDevice|null { if (!$json) { return null; } return new self( - field: $json->field ?? null, - from: $json->from ?? null, - to: $json->to ?? null, - ); - } - - public function __construct( - /** + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + managed_access_code_id: $json->managed_access_code_id ?? null, + unmanaged_access_code_id: $json->unmanaged_access_code_id ?? + null, + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + /** + * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $managed_access_code_id = null, + /** + * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $unmanaged_access_code_id = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * No space for access code on device. + */ + final class NoSpaceForAccessCodeOnDevice extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): NoSpaceForAccessCodeOnDevice|null { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * 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. + */ + final class ConflictingExternalModification extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): ConflictingExternalModification|null { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + modified_fields: array_map( + fn( + $m, + ) => \Seam\Resources\AccessCode\Errors\ConflictingExternalModification\ModifiedFields::from_json( + $m, + ), + $json->modified_fields ?? [], + ), + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * 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. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ConflictingExternalModification\ChangeType>|string|null + */ + public string|null $change_type = null, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + /** + * List of fields that were changed externally, with their previous and new values. + * + * @var list<\Seam\Resources\AccessCode\Errors\ConflictingExternalModification\ModifiedFields>|null + */ + public array|null $modified_fields = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the access code is disabled or inactive on the device. The code exists but will not grant access until re-enabled. + */ + final class AccessCodeInactive extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): AccessCodeInactive|null + { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the account is disconnected. + */ + final class AccountDisconnected extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): AccountDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the Salto site user limit has been reached. + */ + final class SaltoKsSubscriptionLimitExceeded extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitExceeded|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * 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. + */ + final class InsufficientPermissions extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): InsufficientPermissions|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. + */ + final class DormakabaSitesDisconnected extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): DormakabaSitesDisconnected|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the device is offline. + */ + final class DeviceOffline extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): DeviceOffline|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the device has been removed. + */ + final class DeviceRemoved extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): DeviceRemoved|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the hub is disconnected. + */ + final class HubDisconnected extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): HubDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the device is disconnected. + */ + final class DeviceDisconnected extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): DeviceDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. + */ + final class EmptyBackupAccessCodePool extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): EmptyBackupAccessCodePool|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the user is not authorized to use the August lock. + */ + final class AugustLockNotAuthorized extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): AugustLockNotAuthorized|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that device credentials are missing. + */ + final class MissingDeviceCredentials extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): MissingDeviceCredentials|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the auxiliary heat is running. + */ + final class AuxiliaryHeatRunning extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): AuxiliaryHeatRunning|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that a subscription is required to connect. + */ + final class SubscriptionRequired extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): SubscriptionRequired|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/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](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system-errors-seam_bridge_disconnected). + */ + final class BridgeDisconnected extends \Seam\Resources\AccessCode\Errors + { + public static function from_json(mixed $json): BridgeDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error = null, + /** + * Indicates whether the error is related specifically to the connected account. + */ + public bool|null $is_connected_account_error = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + enum ErrorCode: string + { + case PROVIDER_ISSUE = "provider_issue"; + case FAILED_TO_SET_ON_DEVICE = "failed_to_set_on_device"; + case FAILED_TO_REMOVE_FROM_DEVICE = "failed_to_remove_from_device"; + case DUPLICATE_CODE_ON_DEVICE = "duplicate_code_on_device"; + case NO_SPACE_FOR_ACCESS_CODE_ON_DEVICE = "no_space_for_access_code_on_device"; + case CONFLICTING_EXTERNAL_MODIFICATION = "conflicting_external_modification"; + case ACCESS_CODE_INACTIVE = "access_code_inactive"; + case ACCOUNT_DISCONNECTED = "account_disconnected"; + case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; + case INSUFFICIENT_PERMISSIONS = "insufficient_permissions"; + case DORMAKABA_SITES_DISCONNECTED = "dormakaba_sites_disconnected"; + case DEVICE_OFFLINE = "device_offline"; + case DEVICE_REMOVED = "device_removed"; + case HUB_DISCONNECTED = "hub_disconnected"; + case DEVICE_DISCONNECTED = "device_disconnected"; + case EMPTY_BACKUP_ACCESS_CODE_POOL = "empty_backup_access_code_pool"; + case AUGUST_LOCK_NOT_AUTHORIZED = "august_lock_not_authorized"; + case MISSING_DEVICE_CREDENTIALS = "missing_device_credentials"; + case AUXILIARY_HEAT_RUNNING = "auxiliary_heat_running"; + case SUBSCRIPTION_REQUIRED = "subscription_required"; + case BRIDGE_DISCONNECTED = "bridge_disconnected"; + } +} + +namespace Seam\Resources\AccessCode\Errors\ConflictingExternalModification { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); + } + + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } + + enum ChangeType: string + { + case MODIFIED = "modified"; + case REMOVED = "removed"; + } +} + +namespace Seam\Resources\AccessCode\PendingMutations { + /** + * Seam is in the process of setting an access code on the device. + */ + final class Creating extends \Seam\Resources\AccessCode\PendingMutations + { + public static function from_json(mixed $json): Creating|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of setting an access code on the device. + * + * @var value-of<\Seam\Resources\AccessCode\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is waiting until closer to the access code's start time before programming it on the device. + */ + final class DeferringCreation extends + \Seam\Resources\AccessCode\PendingMutations + { + public static function from_json(mixed $json): DeferringCreation|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + scheduled_at: $json->scheduled_at ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of setting an access code on the device. + * + * @var value-of<\Seam\Resources\AccessCode\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * Date and time at which Seam will attempt to program this access code on the device. + */ + public string|null $scheduled_at, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of removing an access code from the device. + */ + final class Deleting extends \Seam\Resources\AccessCode\PendingMutations + { + public static function from_json(mixed $json): Deleting|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of setting an access code on the device. + * + * @var value-of<\Seam\Resources\AccessCode\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an updated PIN code to the device. + */ + final class UpdatingCode extends \Seam\Resources\AccessCode\PendingMutations + { + public static function from_json(mixed $json): UpdatingCode|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AccessCode\PendingMutations\UpdatingCode\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AccessCode\PendingMutations\UpdatingCode\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Previous code configuration. + */ + public \Seam\Resources\AccessCode\PendingMutations\UpdatingCode\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of setting an access code on the device. + * + * @var value-of<\Seam\Resources\AccessCode\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New code configuration. + */ + public \Seam\Resources\AccessCode\PendingMutations\UpdatingCode\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an updated access code name to the device. + */ + final class UpdatingName extends \Seam\Resources\AccessCode\PendingMutations + { + public static function from_json(mixed $json): UpdatingName|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AccessCode\PendingMutations\UpdatingName\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AccessCode\PendingMutations\UpdatingName\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Previous name configuration. + */ + public \Seam\Resources\AccessCode\PendingMutations\UpdatingName\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of setting an access code on the device. + * + * @var value-of<\Seam\Resources\AccessCode\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New name configuration. + */ + public \Seam\Resources\AccessCode\PendingMutations\UpdatingName\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an updated time frame to the device. + */ + final class UpdatingTimeFrame extends + \Seam\Resources\AccessCode\PendingMutations + { + public static function from_json(mixed $json): UpdatingTimeFrame|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AccessCode\PendingMutations\UpdatingTimeFrame\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AccessCode\PendingMutations\UpdatingTimeFrame\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Previous time frame configuration. + */ + public \Seam\Resources\AccessCode\PendingMutations\UpdatingTimeFrame\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of setting an access code on the device. + * + * @var value-of<\Seam\Resources\AccessCode\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New time frame configuration. + */ + public \Seam\Resources\AccessCode\PendingMutations\UpdatingTimeFrame\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + enum MutationCode: string + { + case CREATING = "creating"; + case DEFERRING_CREATION = "deferring_creation"; + case DELETING = "deleting"; + case UPDATING_CODE = "updating_code"; + case UPDATING_NAME = "updating_name"; + case UPDATING_TIME_FRAME = "updating_time_frame"; + } +} + +namespace Seam\Resources\AccessCode\PendingMutations\UpdatingCode { + /** + * Previous code configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(code: $json->code ?? null); + } + + public function __construct( + /** + * Previous PIN code. + */ + public string|null $code, + ) {} + } + + /** + * New code configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(code: $json->code ?? null); + } + + public function __construct( + /** + * New PIN code. + */ + public string|null $code, + ) {} + } +} + +namespace Seam\Resources\AccessCode\PendingMutations\UpdatingName { + /** + * Previous name configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(name: $json->name ?? null); + } + + public function __construct( + /** + * Previous access code name. + */ + public string|null $name, + ) {} + } + + /** + * New name configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(name: $json->name ?? null); + } + + public function __construct( + /** + * New access code name. + */ + public string|null $name, + ) {} + } +} + +namespace Seam\Resources\AccessCode\PendingMutations\UpdatingTimeFrame { + /** + * Previous time frame configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * Previous end time for the access code. + */ + public string|null $ends_at, + /** + * Previous start time for the access code. + */ + public string|null $starts_at, + ) {} + } + + /** + * New time frame configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * New end time for the access code. + */ + public string|null $ends_at, + /** + * New start time for the access code. + */ + public string|null $starts_at, + ) {} + } +} + +namespace Seam\Resources\AccessCode\Warnings { + /** + * The access code's PIN rotates periodically when the code is renewed. Retrieve the latest code before each use. + */ + final class CodeRotatesPeriodically extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): CodeRotatesPeriodically|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class TimeFrameAdjustedForUnknownTimeZone extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): TimeFrameAdjustedForUnknownTimeZone|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class ExternalModificationInEffect extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): ExternalModificationInEffect|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + modified_fields: array_map( + fn( + $m, + ) => \Seam\Resources\AccessCode\Warnings\ExternalModificationInEffect\ModifiedFields::from_json( + $m, + ), + $json->modified_fields ?? [], + ), + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * 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. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\ExternalModificationInEffect\ChangeType>|string|null + */ + public string|null $change_type = null, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + /** + * List of fields that were changed externally, with their previous and new values. + * + * @var list<\Seam\Resources\AccessCode\Warnings\ExternalModificationInEffect\ModifiedFields>|null + */ + public array|null $modified_fields = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Delay in setting code on device. + */ + final class DelayInSettingOnDevice extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): DelayInSettingOnDevice|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Delay in removing code from device. + */ + final class DelayInRemovingFromDevice extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): DelayInRemovingFromDevice|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Third-party integration detected that may cause access codes to fail. + */ + final class ThirdPartyIntegrationDetected extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): ThirdPartyIntegrationDetected|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Algopins must be used within 24 hours. + */ + final class IglooAlgopinMustBeUsedWithin_24Hours extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): IglooAlgopinMustBeUsedWithin_24Hours|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Management was transferred to another workspace. + */ + final class ManagementTransferred extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): ManagementTransferred|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * A backup access code has been pulled and is being used in place of this access code. + */ + final class UsingBackupAccessCode extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): UsingBackupAccessCode|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Access code is being deleted. + */ + final class BeingDeleted extends \Seam\Resources\AccessCode\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * An unknown issue occurred with the access code. + */ + final class UnknownIssueWithAccessCode extends + \Seam\Resources\AccessCode\Warnings + { + public static function from_json( + mixed $json, + ): UnknownIssueWithAccessCode|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case CODE_ROTATES_PERIODICALLY = "code_rotates_periodically"; + case TIME_FRAME_ADJUSTED_FOR_UNKNOWN_TIME_ZONE = "time_frame_adjusted_for_unknown_time_zone"; + case EXTERNAL_MODIFICATION_IN_EFFECT = "external_modification_in_effect"; + case DELAY_IN_SETTING_ON_DEVICE = "delay_in_setting_on_device"; + case DELAY_IN_REMOVING_FROM_DEVICE = "delay_in_removing_from_device"; + case THIRD_PARTY_INTEGRATION_DETECTED = "third_party_integration_detected"; + case IGLOO_ALGOPIN_MUST_BE_USED_WITHIN_24_HOURS = "igloo_algopin_must_be_used_within_24_hours"; + case MANAGEMENT_TRANSFERRED = "management_transferred"; + case USING_BACKUP_ACCESS_CODE = "using_backup_access_code"; + case BEING_DELETED = "being_deleted"; + case UNKNOWN_ISSUE_WITH_ACCESS_CODE = "unknown_issue_with_access_code"; + } +} + +namespace Seam\Resources\AccessCode\Warnings\ExternalModificationInEffect { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); + } + + public function __construct( + /** * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). */ public string|null $field, @@ -529,4 +2573,10 @@ public function __construct( public string|null $to, ) {} } + + enum ChangeType: string + { + case MODIFIED = "modified"; + case REMOVED = "removed"; + } } diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index c2b7ae5c..678b906e 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -13,38 +13,48 @@ public static function from_json(mixed $json): AccessGrant|null } return new self( access_grant_id: $json->access_grant_id ?? null, - access_grant_key: $json->access_grant_key ?? null, access_method_ids: $json->access_method_ids ?? null, - client_session_token: $json->client_session_token ?? null, created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? - null, display_name: $json->display_name ?? null, ends_at: $json->ends_at ?? null, errors: array_map( - fn($e) => AccessGrant\Errors::from_json($e), + fn($e) => \Seam\Resources\AccessGrant\Errors::from_json($e), $json->errors ?? [], ), - instant_key_url: $json->instant_key_url ?? null, location_ids: $json->location_ids ?? null, name: $json->name ?? null, pending_mutations: array_map( - fn($p) => AccessGrant\PendingMutations::from_json($p), + fn( + $p, + ) => \Seam\Resources\AccessGrant\PendingMutations::from_json( + $p, + ), $json->pending_mutations ?? [], ), requested_access_methods: array_map( - fn($r) => AccessGrant\RequestedAccessMethods::from_json($r), + fn( + $r, + ) => \Seam\Resources\AccessGrant\RequestedAccessMethods::from_json( + $r, + ), $json->requested_access_methods ?? [], ), - reservation_key: $json->reservation_key ?? null, space_ids: $json->space_ids ?? null, starts_at: $json->starts_at ?? null, user_identity_id: $json->user_identity_id ?? null, warnings: array_map( - fn($w) => AccessGrant\Warnings::from_json($w), + fn($w) => \Seam\Resources\AccessGrant\Warnings::from_json( + $w, + ), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, + access_grant_key: $json->access_grant_key ?? null, + client_session_token: $json->client_session_token ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + instant_key_url: $json->instant_key_url ?? null, + reservation_key: $json->reservation_key ?? null, ); } @@ -55,6 +65,8 @@ public function __construct( public string|null $access_grant_id, /** * IDs of the access methods created for the Access Grant. + * + * @var list|null */ public array|null $access_method_ids, /** @@ -71,9 +83,12 @@ public function __construct( public string|null $ends_at, /** * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + * + * @var list<\Seam\Resources\AccessGrant\Errors> */ public array $errors, /** + * @var list|null * @deprecated Use `space_ids`. */ public array|null $location_ids, @@ -83,14 +98,20 @@ public function __construct( public string|null $name, /** * List of pending mutations for the access grant. This shows updates that are in progress. + * + * @var list<\Seam\Resources\AccessGrant\PendingMutations> */ public array $pending_mutations, /** * Access methods that the user requested for the Access Grant. + * + * @var list<\Seam\Resources\AccessGrant\RequestedAccessMethods> */ public array $requested_access_methods, /** * IDs of the spaces to which the Access Grant gives access. + * + * @var list|null */ public array|null $space_ids, /** @@ -103,6 +124,8 @@ public function __construct( public string|null $user_identity_id, /** * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + * + * @var list<\Seam\Resources\AccessGrant\Warnings> */ public array $warnings, /** @@ -135,7 +158,7 @@ public function __construct( namespace Seam\Resources\AccessGrant { /** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -144,12 +167,24 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\AccessGrant\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessGrant\Errors\ErrorCode::CANNOT_CREATE_REQUESTED_ACCESS_METHODS + => \Seam\Resources\AccessGrant\Errors\CannotCreateRequestedAccessMethods::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ), + }; } public function __construct( @@ -159,6 +194,8 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Errors\ErrorCode>|string|null */ public string|null $error_code, /** @@ -167,13 +204,15 @@ public function __construct( public string|null $message, /** * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. + * + * @var list|null */ public array|null $missing_device_ids = null, ) {} } /** - * List of pending mutations for the access grant. This shows updates that are in progress. + * List of pending mutations for the access grant. This shows updates that are in progress. Known mutation_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class PendingMutations { @@ -182,36 +221,44 @@ public static function from_json(mixed $json): PendingMutations|null if (!$json) { return null; } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? PendingMutations\From::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? PendingMutations\To::from_json($json->to) - : null, - ); + $discriminant = is_string($json->mutation_code ?? null) + ? \Seam\Resources\AccessGrant\PendingMutations\MutationCode::tryFrom( + $json->mutation_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessGrant\PendingMutations\MutationCode::UPDATING_SPACES + => \Seam\Resources\AccessGrant\PendingMutations\UpdatingSpaces::from_json( + $json, + ), + \Seam\Resources\AccessGrant\PendingMutations\MutationCode::UPDATING_ACCESS_TIMES + => \Seam\Resources\AccessGrant\PendingMutations\UpdatingAccessTimes::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ), + }; } public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, /** * Date and time at which the mutation was created. */ public string|null $created_at, - public PendingMutations\From|null $from, /** * Detailed description of the mutation. */ public string|null $message, + /** + * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. + * + * @var value-of<\Seam\Resources\AccessGrant\PendingMutations\MutationCode>|string|null + */ public string|null $mutation_code, - public PendingMutations\To|null $to, ) {} } @@ -227,20 +274,22 @@ public static function from_json( return null; } return new self( - code: $json->code ?? null, created_access_method_ids: $json->created_access_method_ids ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, + mode: $json->mode ?? null, + code: $json->code ?? null, instant_key_max_use_count: $json->instant_key_max_use_count ?? null, - mode: $json->mode ?? null, ); } public function __construct( /** * IDs of the access methods created for the requested access method. + * + * @var list|null */ public array|null $created_access_method_ids, /** @@ -253,6 +302,8 @@ public function __construct( public string|null $display_name, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\AccessGrant\RequestedAccessMethods\Mode>|string|null */ public string|null $mode, /** @@ -267,11 +318,203 @@ public function __construct( } /** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\AccessGrant\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessGrant\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\AccessGrant\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\AccessGrant\Warnings\WarningCode::UNDERPROVISIONED_ACCESS + => \Seam\Resources\AccessGrant\Warnings\UnderprovisionedAccess::from_json( + $json, + ), + \Seam\Resources\AccessGrant\Warnings\WarningCode::OVERPROVISIONED_ACCESS + => \Seam\Resources\AccessGrant\Warnings\OverprovisionedAccess::from_json( + $json, + ), + \Seam\Resources\AccessGrant\Warnings\WarningCode::UPDATING_ACCESS_TIMES + => \Seam\Resources\AccessGrant\Warnings\UpdatingAccessTimes::from_json( + $json, + ), + \Seam\Resources\AccessGrant\Warnings\WarningCode::REQUESTED_CODE_UNAVAILABLE + => \Seam\Resources\AccessGrant\Warnings\RequestedCodeUnavailable::from_json( + $json, + ), + \Seam\Resources\AccessGrant\Warnings\WarningCode::DEVICE_DOES_NOT_SUPPORT_ACCESS_CODES + => \Seam\Resources\AccessGrant\Warnings\DeviceDoesNotSupportAccessCodes::from_json( + $json, + ), + \Seam\Resources\AccessGrant\Warnings\WarningCode::DEVICE_TIME_CONSTRAINTS_VIOLATED + => \Seam\Resources\AccessGrant\Warnings\DeviceTimeConstraintsViolated::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\AccessGrant\Errors { + /** + * Indicates that Seam could not create one or more of the requested access methods for the access grant. + */ + final class CannotCreateRequestedAccessMethods extends + \Seam\Resources\AccessGrant\Errors + { + public static function from_json( + mixed $json, + ): CannotCreateRequestedAccessMethods|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. + * + * @var list|null + */ + array|null $missing_device_ids = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + missing_device_ids: $missing_device_ids, + ); + } + } + + enum ErrorCode: string + { + case CANNOT_CREATE_REQUESTED_ACCESS_METHODS = "cannot_create_requested_access_methods"; + } +} + +namespace Seam\Resources\AccessGrant\PendingMutations { + /** + * Seam is in the process of updating the devices/spaces associated with this access grant. + */ + final class UpdatingSpaces extends + \Seam\Resources\AccessGrant\PendingMutations + { + public static function from_json(mixed $json): UpdatingSpaces|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AccessGrant\PendingMutations\UpdatingSpaces\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AccessGrant\PendingMutations\UpdatingSpaces\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Previous location configuration. + */ + public \Seam\Resources\AccessGrant\PendingMutations\UpdatingSpaces\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. + * + * @var value-of<\Seam\Resources\AccessGrant\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New location configuration. + */ + public \Seam\Resources\AccessGrant\PendingMutations\UpdatingSpaces\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of updating the access times for this access grant. + */ + final class UpdatingAccessTimes extends + \Seam\Resources\AccessGrant\PendingMutations + { + public static function from_json(mixed $json): UpdatingAccessTimes|null { if (!$json) { return null; @@ -279,58 +522,125 @@ public static function from_json(mixed $json): Warnings|null return new self( access_method_ids: $json->access_method_ids ?? null, created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - failed_devices: array_map( - fn($f) => Warnings\FailedDevices::from_json($f), - $json->failed_devices ?? [], - ), + from: isset($json->from) + ? \Seam\Resources\AccessGrant\PendingMutations\UpdatingAccessTimes\From::from_json( + $json->from, + ) + : null, message: $json->message ?? null, - new_code: $json->new_code ?? null, - original_code: $json->original_code ?? null, - reason: $json->reason ?? null, - warning_code: $json->warning_code ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AccessGrant\PendingMutations\UpdatingAccessTimes\To::from_json( + $json->to, + ) + : null, ); } public function __construct( /** * IDs of the access methods being updated. + * + * @var list|null */ public array|null $access_method_ids, /** - * Date and time at which Seam created the warning. + * Date and time at which the mutation was created. */ - public string|null $created_at, - public string|null $device_id, + string|null $created_at, /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Previous access time configuration. */ - public string|null $message, + public \Seam\Resources\AccessGrant\PendingMutations\UpdatingAccessTimes\From|null $from, /** - * The new PIN code that was assigned instead. + * Detailed description of the mutation. */ - public string|null $new_code, + string|null $message, /** - * The originally requested PIN code that was unavailable. + * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. + * + * @var value-of<\Seam\Resources\AccessGrant\PendingMutations\MutationCode>|string|null */ - public string|null $original_code, + string|null $mutation_code, /** - * Specific reason why the grant's times are not programmable on the device. + * New access time configuration. */ - public string|null $reason, + public \Seam\Resources\AccessGrant\PendingMutations\UpdatingAccessTimes\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + enum MutationCode: string + { + case UPDATING_SPACES = "updating_spaces"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + } +} + +namespace Seam\Resources\AccessGrant\PendingMutations\UpdatingSpaces { + /** + * Previous location configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * Previous device IDs where access codes existed. + * + * @var list|null */ - public string|null $warning_code, + public array|null $device_ids, + ) {} + } + + /** + * New location configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + common_code_key: $json->common_code_key ?? null, + ); + } + + public function __construct( /** - * 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). + * New device IDs where access codes should be created. + * + * @var list|null */ - public array|null $failed_devices = null, + public array|null $device_ids, + /** + * Common code key to ensure PIN code reuse across devices. + */ + public string|null $common_code_key = null, ) {} } } -namespace Seam\Resources\AccessGrant\PendingMutations { +namespace Seam\Resources\AccessGrant\PendingMutations\UpdatingAccessTimes { + /** + * Previous access time configuration. + */ class From { public static function from_json(mixed $json): From|null @@ -339,17 +649,12 @@ public static function from_json(mixed $json): From|null return null; } return new self( - device_ids: $json->device_ids ?? null, ends_at: $json->ends_at ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( - /** - * Previous device IDs where access codes existed. - */ - public array|null $device_ids, /** * Previous end time for access. */ @@ -361,6 +666,9 @@ public function __construct( ) {} } + /** + * New access time configuration. + */ class To { public static function from_json(mixed $json): To|null @@ -369,18 +677,12 @@ public static function from_json(mixed $json): To|null return null; } return new self( - common_code_key: $json->common_code_key ?? null, - device_ids: $json->device_ids ?? null, ends_at: $json->ends_at ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( - /** - * New device IDs where access codes should be created. - */ - public array|null $device_ids, /** * New end time for access. */ @@ -389,15 +691,384 @@ public function __construct( * New start time for access. */ public string|null $starts_at, - /** - * Common code key to ensure PIN code reuse across devices. - */ - public string|null $common_code_key = null, ) {} } } +namespace Seam\Resources\AccessGrant\RequestedAccessMethods { + enum Mode: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } +} + namespace Seam\Resources\AccessGrant\Warnings { + /** + * Indicates that the [access grant](https://docs.seam.co/use-cases/granting-access) is being deleted. + */ + final class BeingDeleted extends \Seam\Resources\AccessGrant\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the access grant should have access to more locations than it currently does. Access methods are being created for the missing locations. + */ + final class UnderprovisionedAccess extends + \Seam\Resources\AccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): UnderprovisionedAccess|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the access grant has access to locations it should not have. Access methods are being removed from the extra locations. + */ + final class OverprovisionedAccess extends + \Seam\Resources\AccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): OverprovisionedAccess|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + failed_devices: array_map( + fn( + $f, + ) => \Seam\Resources\AccessGrant\Warnings\OverprovisionedAccess\FailedDevices::from_json( + $f, + ), + $json->failed_devices ?? [], + ), + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * 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). + * + * @var list<\Seam\Resources\AccessGrant\Warnings\OverprovisionedAccess\FailedDevices>|null + */ + public array|null $failed_devices = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the access times for this [access grant](https://docs.seam.co/use-cases/granting-access) are being updated. + */ + final class UpdatingAccessTimes extends \Seam\Resources\AccessGrant\Warnings + { + public static function from_json(mixed $json): UpdatingAccessTimes|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * IDs of the access methods being updated. + * + * @var list|null + */ + public array|null $access_method_ids, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the requested PIN code was already in use on a device, so a different code was assigned. + */ + final class RequestedCodeUnavailable extends + \Seam\Resources\AccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): RequestedCodeUnavailable|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * ID of the device where the requested code was unavailable. + */ + public string|null $device_id, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * The new PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that was unavailable. + */ + public string|null $original_code, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that a device in the access grant does not support access codes and was excluded from code materialization. + */ + final class DeviceDoesNotSupportAccessCodes extends + \Seam\Resources\AccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): DeviceDoesNotSupportAccessCodes|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * ID of the device that does not support access codes. + */ + public string|null $device_id, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class DeviceTimeConstraintsViolated extends + \Seam\Resources\AccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): DeviceTimeConstraintsViolated|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + message: $json->message ?? null, + reason: $json->reason ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * ID of the device whose time constraints the access grant violates. + */ + public string|null $device_id, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Specific reason why the grant's times are not programmable on the device. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\DeviceTimeConstraintsViolated\Reason>|string|null + */ + public string|null $reason, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case BEING_DELETED = "being_deleted"; + case UNDERPROVISIONED_ACCESS = "underprovisioned_access"; + case OVERPROVISIONED_ACCESS = "overprovisioned_access"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + case REQUESTED_CODE_UNAVAILABLE = "requested_code_unavailable"; + case DEVICE_DOES_NOT_SUPPORT_ACCESS_CODES = "device_does_not_support_access_codes"; + case DEVICE_TIME_CONSTRAINTS_VIOLATED = "device_time_constraints_violated"; + } +} + +namespace Seam\Resources\AccessGrant\Warnings\OverprovisionedAccess { /** * 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). */ @@ -431,3 +1102,12 @@ public function __construct( ) {} } } + +namespace Seam\Resources\AccessGrant\Warnings\DeviceTimeConstraintsViolated { + enum Reason: string + { + case DURATION_EXCEEDS_MAX = "duration_exceeds_max"; + case TIMES_DO_NOT_MATCH_SLOTS = "times_do_not_match_slots"; + case ONGOING_NOT_SUPPORTED = "ongoing_not_supported"; + } +} diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index f6ae0845..d331fb43 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -13,33 +13,41 @@ public static function from_json(mixed $json): AccessMethod|null } return new self( access_method_id: $json->access_method_id ?? null, - client_session_token: $json->client_session_token ?? null, - code: $json->code ?? null, created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? - null, display_name: $json->display_name ?? null, errors: array_map( - fn($e) => AccessMethod\Errors::from_json($e), + fn($e) => \Seam\Resources\AccessMethod\Errors::from_json( + $e, + ), $json->errors ?? [], ), - instant_key_url: $json->instant_key_url ?? null, - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, is_issued: $json->is_issued ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, issued_at: $json->issued_at ?? null, mode: $json->mode ?? null, pending_mutations: array_map( - fn($p) => AccessMethod\PendingMutations::from_json($p), + fn( + $p, + ) => \Seam\Resources\AccessMethod\PendingMutations::from_json( + $p, + ), $json->pending_mutations ?? [], ), warnings: array_map( - fn($w) => AccessMethod\Warnings::from_json($w), + fn($w) => \Seam\Resources\AccessMethod\Warnings::from_json( + $w, + ), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, + client_session_token: $json->client_session_token ?? null, + code: $json->code ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + instant_key_url: $json->instant_key_url ?? null, + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, ); } @@ -58,6 +66,8 @@ public function __construct( public string|null $display_name, /** * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * + * @var list<\Seam\Resources\AccessMethod\Errors> */ public array $errors, /** @@ -70,14 +80,20 @@ public function __construct( public string|null $issued_at, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\AccessMethod\Mode>|string|null */ public string|null $mode, /** * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + * + * @var list<\Seam\Resources\AccessMethod\PendingMutations> */ public array $pending_mutations, /** * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * + * @var list<\Seam\Resources\AccessMethod\Warnings> */ public array $warnings, /** @@ -122,7 +138,7 @@ public function __construct( namespace Seam\Resources\AccessMethod { /** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -131,11 +147,23 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\AccessMethod\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessMethod\Errors\ErrorCode::FAILED_TO_ISSUE + => \Seam\Resources\AccessMethod\Errors\FailedToIssue::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -145,6 +173,8 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessMethod\Errors\ErrorCode>|string|null */ public string|null $error_code, /** @@ -155,11 +185,184 @@ public function __construct( } /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. Known mutation_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class PendingMutations { public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->mutation_code ?? null) + ? \Seam\Resources\AccessMethod\PendingMutations\MutationCode::tryFrom( + $json->mutation_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessMethod\PendingMutations\MutationCode::PROVISIONING_ACCESS + => \Seam\Resources\AccessMethod\PendingMutations\ProvisioningAccess::from_json( + $json, + ), + \Seam\Resources\AccessMethod\PendingMutations\MutationCode::REVOKING_ACCESS + => \Seam\Resources\AccessMethod\PendingMutations\RevokingAccess::from_json( + $json, + ), + \Seam\Resources\AccessMethod\PendingMutations\MutationCode::UPDATING_ACCESS_TIMES + => \Seam\Resources\AccessMethod\PendingMutations\UpdatingAccessTimes::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + /** + * Detailed description of the mutation. + */ + public string|null $message, + /** + * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. + * + * @var value-of<\Seam\Resources\AccessMethod\PendingMutations\MutationCode>|string|null + */ + public string|null $mutation_code, + ) {} + } + + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\AccessMethod\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AccessMethod\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\AccessMethod\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\AccessMethod\Warnings\WarningCode::UPDATING_ACCESS_TIMES + => \Seam\Resources\AccessMethod\Warnings\UpdatingAccessTimes::from_json( + $json, + ), + \Seam\Resources\AccessMethod\Warnings\WarningCode::PULLED_BACKUP_ACCESS_CODE + => \Seam\Resources\AccessMethod\Warnings\PulledBackupAccessCode::from_json( + $json, + ), + \Seam\Resources\AccessMethod\Warnings\WarningCode::DELAY_IN_ISSUING + => \Seam\Resources\AccessMethod\Warnings\DelayInIssuing::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessMethod\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } + + enum Mode: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } +} + +namespace Seam\Resources\AccessMethod\Errors { + /** + * Indicates that Seam was unable to issue this [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant) 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. + */ + final class FailedToIssue extends \Seam\Resources\AccessMethod\Errors + { + public static function from_json(mixed $json): FailedToIssue|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessMethod\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case FAILED_TO_ISSUE = "failed_to_issue"; + } +} + +namespace Seam\Resources\AccessMethod\PendingMutations { + /** + * Seam is in the process of provisioning access for this access method on new devices. + */ + final class ProvisioningAccess extends + \Seam\Resources\AccessMethod\PendingMutations + { + public static function from_json(mixed $json): ProvisioningAccess|null { if (!$json) { return null; @@ -167,12 +370,16 @@ public static function from_json(mixed $json): PendingMutations|null return new self( created_at: $json->created_at ?? null, from: isset($json->from) - ? PendingMutations\From::from_json($json->from) + ? \Seam\Resources\AccessMethod\PendingMutations\ProvisioningAccess\From::from_json( + $json->from, + ) : null, message: $json->message ?? null, mutation_code: $json->mutation_code ?? null, to: isset($json->to) - ? PendingMutations\To::from_json($json->to) + ? \Seam\Resources\AccessMethod\PendingMutations\ProvisioningAccess\To::from_json( + $json->to, + ) : null, ); } @@ -181,58 +388,262 @@ public function __construct( /** * Date and time at which the mutation was created. */ - public string|null $created_at, - public PendingMutations\From|null $from, + string|null $created_at, + /** + * Previous device configuration. + */ + public \Seam\Resources\AccessMethod\PendingMutations\ProvisioningAccess\From|null $from, /** * Detailed description of the mutation. */ - public string|null $message, - public string|null $mutation_code, - public PendingMutations\To|null $to, - ) {} + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. + * + * @var value-of<\Seam\Resources\AccessMethod\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New device configuration. + */ + public \Seam\Resources\AccessMethod\PendingMutations\ProvisioningAccess\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } } /** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * Seam is in the process of revoking access for this access method from devices. */ - class Warnings + final class RevokingAccess extends + \Seam\Resources\AccessMethod\PendingMutations { - public static function from_json(mixed $json): Warnings|null + public static function from_json(mixed $json): RevokingAccess|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AccessMethod\PendingMutations\RevokingAccess\From::from_json( + $json->from, + ) + : null, message: $json->message ?? null, - original_access_method_id: $json->original_access_method_id ?? - null, - warning_code: $json->warning_code ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AccessMethod\PendingMutations\RevokingAccess\To::from_json( + $json->to, + ) + : null, ); } public function __construct( /** - * Date and time at which Seam created the warning. + * Date and time at which the mutation was created. */ - public string|null $created_at, + string|null $created_at, /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Previous device configuration. */ - public string|null $message, + public \Seam\Resources\AccessMethod\PendingMutations\RevokingAccess\From|null $from, /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * Detailed description of the mutation. */ - public string|null $warning_code, + string|null $message, /** - * ID of the original access method from which this backup access method was split, if applicable. + * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. + * + * @var value-of<\Seam\Resources\AccessMethod\PendingMutations\MutationCode>|string|null */ - public string|null $original_access_method_id = null, + string|null $mutation_code, + /** + * New device configuration. + */ + public \Seam\Resources\AccessMethod\PendingMutations\RevokingAccess\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of updating the access times for this access method. + */ + final class UpdatingAccessTimes extends + \Seam\Resources\AccessMethod\PendingMutations + { + public static function from_json(mixed $json): UpdatingAccessTimes|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AccessMethod\PendingMutations\UpdatingAccessTimes\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AccessMethod\PendingMutations\UpdatingAccessTimes\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Previous access time configuration. + */ + public \Seam\Resources\AccessMethod\PendingMutations\UpdatingAccessTimes\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. + * + * @var value-of<\Seam\Resources\AccessMethod\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New access time configuration. + */ + public \Seam\Resources\AccessMethod\PendingMutations\UpdatingAccessTimes\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + enum MutationCode: string + { + case PROVISIONING_ACCESS = "provisioning_access"; + case REVOKING_ACCESS = "revoking_access"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + } +} + +namespace Seam\Resources\AccessMethod\PendingMutations\ProvisioningAccess { + /** + * Previous device configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( + /** + * Previous device IDs where access was provisioned. + * + * @var list|null + */ + public array|null $device_ids, + ) {} + } + + /** + * New device configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( + /** + * New device IDs where access is being provisioned. + * + * @var list|null + */ + public array|null $device_ids, ) {} } } -namespace Seam\Resources\AccessMethod\PendingMutations { +namespace Seam\Resources\AccessMethod\PendingMutations\RevokingAccess { + /** + * Previous device configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( + /** + * Previous device IDs where access existed. + * + * @var list|null + */ + public array|null $device_ids, + ) {} + } + + /** + * New device configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( + /** + * New device IDs where access should remain. + * + * @var list|null + */ + public array|null $device_ids, + ) {} + } +} + +namespace Seam\Resources\AccessMethod\PendingMutations\UpdatingAccessTimes { + /** + * Previous access time configuration. + */ class From { public static function from_json(mixed $json): From|null @@ -241,14 +652,12 @@ public static function from_json(mixed $json): From|null return null; } return new self( - device_ids: $json->device_ids ?? null, ends_at: $json->ends_at ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( - public array|null $device_ids, /** * Previous end time for access. */ @@ -260,6 +669,9 @@ public function __construct( ) {} } + /** + * New access time configuration. + */ class To { public static function from_json(mixed $json): To|null @@ -268,14 +680,12 @@ public static function from_json(mixed $json): To|null return null; } return new self( - device_ids: $json->device_ids ?? null, ends_at: $json->ends_at ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( - public array|null $device_ids, /** * New end time for access. */ @@ -287,3 +697,186 @@ public function __construct( ) {} } } + +namespace Seam\Resources\AccessMethod\Warnings { + /** + * Indicates that the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant) is being deleted. + */ + final class BeingDeleted extends \Seam\Resources\AccessMethod\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessMethod\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the access times for this [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant) are being updated. + */ + final class UpdatingAccessTimes extends + \Seam\Resources\AccessMethod\Warnings + { + public static function from_json(mixed $json): UpdatingAccessTimes|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessMethod\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class PulledBackupAccessCode extends + \Seam\Resources\AccessMethod\Warnings + { + public static function from_json( + mixed $json, + ): PulledBackupAccessCode|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessMethod\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that Seam has not yet issued this [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant), 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. + */ + final class DelayInIssuing extends \Seam\Resources\AccessMethod\Warnings + { + public static function from_json(mixed $json): DelayInIssuing|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessMethod\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case BEING_DELETED = "being_deleted"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + case PULLED_BACKUP_ACCESS_CODE = "pulled_backup_access_code"; + case DELAY_IN_ISSUING = "delay_in_issuing"; + } +} diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index fb38046e..463cd3d6 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -19,18 +19,15 @@ public static function from_json(mixed $json): AcsAccessGroup|null access_group_type: $json->access_group_type ?? null, access_group_type_display_name: $json->access_group_type_display_name ?? null, - access_schedule: isset($json->access_schedule) - ? AcsAccessGroup\AccessSchedule::from_json( - $json->access_schedule, - ) - : null, acs_access_group_id: $json->acs_access_group_id ?? null, acs_system_id: $json->acs_system_id ?? null, connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, errors: array_map( - fn($e) => AcsAccessGroup\Errors::from_json($e), + fn($e) => \Seam\Resources\AcsAccessGroup\Errors::from_json( + $e, + ), $json->errors ?? [], ), external_type: $json->external_type ?? null, @@ -39,19 +36,31 @@ public static function from_json(mixed $json): AcsAccessGroup|null is_managed: $json->is_managed ?? null, name: $json->name ?? null, pending_mutations: array_map( - fn($p) => AcsAccessGroup\PendingMutations::from_json($p), + fn( + $p, + ) => \Seam\Resources\AcsAccessGroup\PendingMutations::from_json( + $p, + ), $json->pending_mutations ?? [], ), warnings: array_map( - fn($w) => AcsAccessGroup\Warnings::from_json($w), + fn( + $w, + ) => \Seam\Resources\AcsAccessGroup\Warnings::from_json($w), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, + access_schedule: isset($json->access_schedule) + ? \Seam\Resources\AcsAccessGroup\AccessSchedule::from_json( + $json->access_schedule, + ) + : null, ); } public function __construct( /** + * @var value-of<\Seam\Resources\AcsAccessGroup\AccessGroupType>|string|null * @deprecated Use `external_type`. */ public string|null $access_group_type, @@ -81,10 +90,14 @@ public function __construct( public string|null $display_name, /** * Errors associated with the `acs_access_group`. + * + * @var list<\Seam\Resources\AcsAccessGroup\Errors> */ public array $errors, /** * Brand-specific terminology for the access group type. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\ExternalType>|string|null */ public string|null $external_type, /** @@ -101,10 +114,14 @@ public function __construct( public string|null $name, /** * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. + * + * @var list<\Seam\Resources\AcsAccessGroup\PendingMutations> */ public array $pending_mutations, /** * Warnings associated with the `acs_access_group`. + * + * @var list<\Seam\Resources\AcsAccessGroup\Warnings> */ public array $warnings, /** @@ -114,7 +131,7 @@ public function __construct( /** * `starts_at` and `ends_at` timestamps for the access group's access. */ - public AcsAccessGroup\AccessSchedule|null $access_schedule = null, + public \Seam\Resources\AcsAccessGroup\AccessSchedule|null $access_schedule = null, ) {} } } @@ -149,7 +166,7 @@ public function __construct( } /** - * Errors associated with the `acs_access_group`. + * Errors associated with the `acs_access_group`. Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -158,11 +175,23 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\AcsAccessGroup\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsAccessGroup\Errors\ErrorCode::FAILED_TO_CREATE_ON_ACS_SYSTEM + => \Seam\Resources\AcsAccessGroup\Errors\FailedToCreateOnAcsSystem::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -172,6 +201,8 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\Errors\ErrorCode>|string|null */ public string|null $error_code, /** @@ -182,7 +213,7 @@ public function __construct( } /** - * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. + * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. Known mutation_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class PendingMutations { @@ -191,41 +222,68 @@ public static function from_json(mixed $json): PendingMutations|null if (!$json) { return null; } - return new self( - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? PendingMutations\From::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? PendingMutations\To::from_json($json->to) - : null, - variant: $json->variant ?? null, - ); + $discriminant = is_string($json->mutation_code ?? null) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::tryFrom( + $json->mutation_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::CREATING + => \Seam\Resources\AcsAccessGroup\PendingMutations\Creating::from_json( + $json, + ), + \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::DELETING + => \Seam\Resources\AcsAccessGroup\PendingMutations\Deleting::from_json( + $json, + ), + \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::DEFERRING_DELETION + => \Seam\Resources\AcsAccessGroup\PendingMutations\DeferringDeletion::from_json( + $json, + ), + \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::UPDATING_GROUP_INFORMATION + => \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingGroupInformation::from_json( + $json, + ), + \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::UPDATING_ACCESS_SCHEDULE + => \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingAccessSchedule::from_json( + $json, + ), + \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::UPDATING_USER_MEMBERSHIP + => \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingUserMembership::from_json( + $json, + ), + \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::UPDATING_ENTRANCE_MEMBERSHIP + => \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingEntranceMembership::from_json( + $json, + ), + \Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode::DEFERRING_USER_MEMBERSHIP_UPDATE + => \Seam\Resources\AcsAccessGroup\PendingMutations\DeferringUserMembershipUpdate::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ), + }; } public function __construct( - /** - * ID of the user involved in the scheduled change. - */ - public string|null $acs_user_id, /** * Date and time at which the mutation was created. */ public string|null $created_at, - public PendingMutations\From|null $from, /** * Detailed description of the mutation. */ public string|null $message, - public string|null $mutation_code, - public PendingMutations\To|null $to, /** - * Whether the user is scheduled to be added to or removed from this access group. + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null */ - public string|null $variant, + public string|null $mutation_code, ) {} } @@ -257,46 +315,544 @@ public function __construct( public string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\Warnings\WarningCode>|string|null */ public string|null $warning_code, ) {} } + + enum AccessGroupType: string + { + case PTI_UNIT = "pti_unit"; + case PTI_ACCESS_LEVEL = "pti_access_level"; + case SALTO_KS_ACCESS_GROUP = "salto_ks_access_group"; + case BRIVO_GROUP = "brivo_group"; + case SALTO_SPACE_GROUP = "salto_space_group"; + case DORMAKABA_COMMUNITY_ACCESS_GROUP = "dormakaba_community_access_group"; + case DORMAKABA_AMBIANCE_ACCESS_GROUP = "dormakaba_ambiance_access_group"; + case AVIGILON_ALTA_GROUP = "avigilon_alta_group"; + case KISI_ACCESS_GROUP = "kisi_access_group"; + case AKILES_MEMBER_GROUP = "akiles_member_group"; + } + + enum ExternalType: string + { + case PTI_UNIT = "pti_unit"; + case PTI_ACCESS_LEVEL = "pti_access_level"; + case SALTO_KS_ACCESS_GROUP = "salto_ks_access_group"; + case BRIVO_GROUP = "brivo_group"; + case SALTO_SPACE_GROUP = "salto_space_group"; + case DORMAKABA_COMMUNITY_ACCESS_GROUP = "dormakaba_community_access_group"; + case DORMAKABA_AMBIANCE_ACCESS_GROUP = "dormakaba_ambiance_access_group"; + case AVIGILON_ALTA_GROUP = "avigilon_alta_group"; + case KISI_ACCESS_GROUP = "kisi_access_group"; + case AKILES_MEMBER_GROUP = "akiles_member_group"; + } +} + +namespace Seam\Resources\AcsAccessGroup\Errors { + /** + * Indicates that the [access group](https://docs.seam.co/low-level-apis/access-systems/user-management/assigning-users-to-access-groups) was not created on the [access system](https://docs.seam.co/low-level-apis/access-systems). This is likely due to an internal unexpected error. Contact Seam [support](mailto:support@seam.co). + */ + final class FailedToCreateOnAcsSystem extends + \Seam\Resources\AcsAccessGroup\Errors + { + public static function from_json( + mixed $json, + ): FailedToCreateOnAcsSystem|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case FAILED_TO_CREATE_ON_ACS_SYSTEM = "failed_to_create_on_acs_system"; + } } namespace Seam\Resources\AcsAccessGroup\PendingMutations { - class From + /** + * Seam is in the process of pushing an access group creation to the integrated access system. + */ + final class Creating extends \Seam\Resources\AcsAccessGroup\PendingMutations { - public static function from_json(mixed $json): From|null + public static function from_json(mixed $json): Creating|null { if (!$json) { return null; } return new self( - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, ); } public function __construct( /** - * Old entrance ID. + * Date and time at which the mutation was created. */ - public string|null $acs_entrance_id, + string|null $created_at, /** - * Old user ID. + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an access group deletion to the integrated access system. + */ + final class Deleting extends \Seam\Resources\AcsAccessGroup\PendingMutations + { + public static function from_json(mixed $json): Deleting|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * This access group is scheduled for automatic deletion when its access window expires. + */ + final class DeferringDeletion extends + \Seam\Resources\AcsAccessGroup\PendingMutations + { + public static function from_json(mixed $json): DeferringDeletion|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an access group information update to the integrated access system. + */ + final class UpdatingGroupInformation extends + \Seam\Resources\AcsAccessGroup\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingGroupInformation|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingGroupInformation\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingGroupInformation\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Old access group information. + */ + public \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingGroupInformation\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New access group information. + */ + public \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingGroupInformation\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an access schedule update to the integrated access system. + */ + final class UpdatingAccessSchedule extends + \Seam\Resources\AcsAccessGroup\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingAccessSchedule|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingAccessSchedule\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingAccessSchedule\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Old access schedule information. + */ + public \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingAccessSchedule\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New access schedule information. + */ + public \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingAccessSchedule\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing a user membership update to the integrated access system. + */ + final class UpdatingUserMembership extends + \Seam\Resources\AcsAccessGroup\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingUserMembership|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingUserMembership\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingUserMembership\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Old user membership. + */ + public \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingUserMembership\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New user membership. + */ + public \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingUserMembership\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an entrance membership update to the integrated access system. + */ + final class UpdatingEntranceMembership extends + \Seam\Resources\AcsAccessGroup\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingEntranceMembership|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingEntranceMembership\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingEntranceMembership\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Old entrance membership. + */ + public \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingEntranceMembership\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New entrance membership. + */ + public \Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingEntranceMembership\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * A scheduled user membership change is pending for this access group. + */ + final class DeferringUserMembershipUpdate extends + \Seam\Resources\AcsAccessGroup\PendingMutations + { + public static function from_json( + mixed $json, + ): DeferringUserMembershipUpdate|null { + if (!$json) { + return null; + } + return new self( + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + variant: $json->variant ?? null, + ); + } + + public function __construct( + /** + * ID of the user involved in the scheduled change. */ public string|null $acs_user_id, /** - * Ending time for the access schedule. + * Date and time at which the mutation was created. */ - public string|null $ends_at, + string|null $created_at, /** - * Starting time for the access schedule. + * Detailed description of the mutation. */ - public string|null $starts_at, + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * Whether the user is scheduled to be added to or removed from this access group. + * + * @var value-of<\Seam\Resources\AcsAccessGroup\PendingMutations\DeferringUserMembershipUpdate\Variant>|string|null + */ + public string|null $variant, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + enum MutationCode: string + { + case CREATING = "creating"; + case DELETING = "deleting"; + case DEFERRING_DELETION = "deferring_deletion"; + case UPDATING_GROUP_INFORMATION = "updating_group_information"; + case UPDATING_ACCESS_SCHEDULE = "updating_access_schedule"; + case UPDATING_USER_MEMBERSHIP = "updating_user_membership"; + case UPDATING_ENTRANCE_MEMBERSHIP = "updating_entrance_membership"; + case DEFERRING_USER_MEMBERSHIP_UPDATE = "deferring_user_membership_update"; + } +} + +namespace Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingGroupInformation { + /** + * Old access group information. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(name: $json->name ?? null); + } + + public function __construct( /** * Name of the access group. */ @@ -304,31 +860,74 @@ public function __construct( ) {} } + /** + * New access group information. + */ class To { public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(name: $json->name ?? null); + } + + public function __construct( + /** + * Name of the access group. + */ + public string|null $name = null, + ) {} + } +} + +namespace Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingAccessSchedule { + /** + * Old access schedule information. + */ + class From + { + public static function from_json(mixed $json): From|null { if (!$json) { return null; } return new self( - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_user_id: $json->acs_user_id ?? null, ends_at: $json->ends_at ?? null, - name: $json->name ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( /** - * New entrance ID. + * Ending time for the access schedule. */ - public string|null $acs_entrance_id, + public string|null $ends_at, /** - * New user ID. + * Starting time for the access schedule. */ - public string|null $acs_user_id, + public string|null $starts_at, + ) {} + } + + /** + * New access schedule information. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( /** * Ending time for the access schedule. */ @@ -337,10 +936,110 @@ public function __construct( * Starting time for the access schedule. */ public string|null $starts_at, + ) {} + } +} + +namespace Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingUserMembership { + /** + * Old user membership. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(acs_user_id: $json->acs_user_id ?? null); + } + + public function __construct( /** - * Name of the access group. + * Old user ID. */ - public string|null $name = null, + public string|null $acs_user_id, + ) {} + } + + /** + * New user membership. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(acs_user_id: $json->acs_user_id ?? null); + } + + public function __construct( + /** + * New user ID. + */ + public string|null $acs_user_id, ) {} } } + +namespace Seam\Resources\AcsAccessGroup\PendingMutations\UpdatingEntranceMembership { + /** + * Old entrance membership. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(acs_entrance_id: $json->acs_entrance_id ?? null); + } + + public function __construct( + /** + * Old entrance ID. + */ + public string|null $acs_entrance_id, + ) {} + } + + /** + * New entrance membership. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(acs_entrance_id: $json->acs_entrance_id ?? null); + } + + public function __construct( + /** + * New entrance ID. + */ + public string|null $acs_entrance_id, + ) {} + } +} + +namespace Seam\Resources\AcsAccessGroup\PendingMutations\DeferringUserMembershipUpdate { + enum Variant: string + { + case ADDING = "adding"; + case REMOVING = "removing"; + } +} + +namespace Seam\Resources\AcsAccessGroup\Warnings { + enum WarningCode: string + { + case UNKNOWN_ISSUE_WITH_ACS_ACCESS_GROUP = "unknown_issue_with_acs_access_group"; + case BEING_DELETED = "being_deleted"; + } +} diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index e18bc0bc..87f5c279 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -20,38 +20,47 @@ public static function from_json(mixed $json): AcsCredential|null return new self( access_method: $json->access_method ?? null, acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => \Seam\Resources\AcsCredential\Errors::from_json( + $e, + ), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + warnings: array_map( + fn($w) => \Seam\Resources\AcsCredential\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, acs_user_id: $json->acs_user_id ?? null, akiles_metadata: isset($json->akiles_metadata) - ? AcsCredential\AkilesMetadata::from_json( + ? \Seam\Resources\AcsCredential\AkilesMetadata::from_json( $json->akiles_metadata, ) : null, assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - ? AcsCredential\AssaAbloyVostioMetadata::from_json( + ? \Seam\Resources\AcsCredential\AssaAbloyVostioMetadata::from_json( $json->assa_abloy_vostio_metadata, ) : null, card_number: $json->card_number ?? null, code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => AcsCredential\Errors::from_json($e), - $json->errors ?? [], - ), external_type: $json->external_type ?? null, external_type_display_name: $json->external_type_display_name ?? null, is_issued: $json->is_issued ?? null, is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? null, - is_managed: $json->is_managed ?? null, is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? null, is_one_time_use: $json->is_one_time_use ?? null, @@ -63,21 +72,18 @@ public static function from_json(mixed $json): AcsCredential|null starts_at: $json->starts_at ?? null, user_identity_id: $json->user_identity_id ?? null, visionline_metadata: isset($json->visionline_metadata) - ? AcsCredential\VisionlineMetadata::from_json( + ? \Seam\Resources\AcsCredential\VisionlineMetadata::from_json( $json->visionline_metadata, ) : null, - warnings: array_map( - fn($w) => AcsCredential\Warnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, ); } public function __construct( /** * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\AcsCredential\AccessMethod>|string|null */ public string|null $access_method, /** @@ -102,6 +108,8 @@ public function __construct( public string|null $display_name, /** * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list<\Seam\Resources\AcsCredential\Errors> */ public array $errors, /** @@ -110,6 +118,8 @@ public function __construct( public true|null $is_managed, /** * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list<\Seam\Resources\AcsCredential\Warnings> */ public array $warnings, /** @@ -127,11 +137,11 @@ public function __construct( /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\AkilesMetadata|null $akiles_metadata = null, + public \Seam\Resources\AcsCredential\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, + public \Seam\Resources\AcsCredential\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -146,6 +156,8 @@ public function __construct( public string|null $ends_at = null, /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + * + * @var value-of<\Seam\Resources\AcsCredential\ExternalType>|string|null */ public string|null $external_type = null, /** @@ -191,7 +203,7 @@ public function __construct( /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\VisionlineMetadata|null $visionline_metadata = null, + public \Seam\Resources\AcsCredential\VisionlineMetadata|null $visionline_metadata = null, ) {} } } @@ -247,6 +259,8 @@ public function __construct( public bool|null $auto_join = null, /** * Names of the doors to which to grant access in the Vostio access system. + * + * @var list|null */ public array|null $door_names = null, /** @@ -263,6 +277,8 @@ public function __construct( public string|null $key_issuing_request_id = null, /** * IDs of the guest entrances to override in the Vostio access system. + * + * @var list|null */ public array|null $override_guest_acs_entrance_ids = null, ) {} @@ -325,6 +341,8 @@ public function __construct( public bool|null $auto_join = null, /** * Card function type in the Visionline access system. + * + * @var value-of<\Seam\Resources\AcsCredential\VisionlineMetadata\CardFunctionType>|string|null */ public string|null $card_function_type = null, /** @@ -333,6 +351,8 @@ public function __construct( public string|null $card_id = null, /** * Common entrance IDs in the Visionline access system. + * + * @var list|null */ public array|null $common_acs_entrance_ids = null, /** @@ -341,6 +361,8 @@ public function __construct( public string|null $credential_id = null, /** * Guest entrance IDs in the Visionline access system. + * + * @var list|null */ public array|null $guest_acs_entrance_ids = null, /** @@ -349,18 +371,379 @@ public function __construct( public bool|null $is_valid = null, /** * IDs of the credentials to which you want to join. + * + * @var list|null */ public array|null $joiner_acs_credential_ids = null, ) {} } /** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\AcsCredential\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsCredential\Warnings\WarningCode::WAITING_TO_BE_ISSUED + => \Seam\Resources\AcsCredential\Warnings\WaitingToBeIssued::from_json( + $json, + ), + \Seam\Resources\AcsCredential\Warnings\WarningCode::SCHEDULE_EXTERNALLY_MODIFIED + => \Seam\Resources\AcsCredential\Warnings\ScheduleExternallyModified::from_json( + $json, + ), + \Seam\Resources\AcsCredential\Warnings\WarningCode::SCHEDULE_MODIFIED + => \Seam\Resources\AcsCredential\Warnings\ScheduleModified::from_json( + $json, + ), + \Seam\Resources\AcsCredential\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\AcsCredential\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\AcsCredential\Warnings\WarningCode::UNKNOWN_ISSUE_WITH_ACS_CREDENTIAL + => \Seam\Resources\AcsCredential\Warnings\UnknownIssueWithAcsCredential::from_json( + $json, + ), + \Seam\Resources\AcsCredential\Warnings\WarningCode::NEEDS_TO_BE_REISSUED + => \Seam\Resources\AcsCredential\Warnings\NeedsToBeReissued::from_json( + $json, + ), + \Seam\Resources\AcsCredential\Warnings\WarningCode::REQUESTED_CODE_UNAVAILABLE + => \Seam\Resources\AcsCredential\Warnings\RequestedCodeUnavailable::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsCredential\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } + + enum AccessMethod: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } + + enum ExternalType: string + { + case PTI_CARD = "pti_card"; + case BRIVO_CREDENTIAL = "brivo_credential"; + case HID_CREDENTIAL = "hid_credential"; + case VISIONLINE_CARD = "visionline_card"; + case SALTO_KS_CREDENTIAL = "salto_ks_credential"; + case ASSA_ABLOY_VOSTIO_KEY = "assa_abloy_vostio_key"; + case SALTO_SPACE_KEY = "salto_space_key"; + case LATCH_ACCESS = "latch_access"; + case DORMAKABA_AMBIANCE_CREDENTIAL = "dormakaba_ambiance_credential"; + case HOTEK_CARD = "hotek_card"; + case SALTO_KS_TAG = "salto_ks_tag"; + case AVIGILON_ALTA_CREDENTIAL = "avigilon_alta_credential"; + case KISI_CREDENTIAL = "kisi_credential"; + case AKILES_CREDENTIAL = "akiles_credential"; + } +} + +namespace Seam\Resources\AcsCredential\VisionlineMetadata { + enum CardFunctionType: string + { + case GUEST = "guest"; + case STAFF = "staff"; + } +} + +namespace Seam\Resources\AcsCredential\Warnings { + /** + * Indicates that the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is waiting to be issued. + */ + final class WaitingToBeIssued extends \Seam\Resources\AcsCredential\Warnings + { + public static function from_json(mixed $json): WaitingToBeIssued|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsCredential\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the schedule of one of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials)'s children was modified externally. + */ + final class ScheduleExternallyModified extends + \Seam\Resources\AcsCredential\Warnings + { + public static function from_json( + mixed $json, + ): ScheduleExternallyModified|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsCredential\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the schedule of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was modified to avoid creating a credential with a start date in the past. + */ + final class ScheduleModified extends \Seam\Resources\AcsCredential\Warnings + { + public static function from_json(mixed $json): ScheduleModified|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsCredential\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is being deleted. + */ + final class BeingDeleted extends \Seam\Resources\AcsCredential\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsCredential\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * An unknown issue occurred while syncing the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) with the provider. This issue may affect the proper functioning of the credential. + */ + final class UnknownIssueWithAcsCredential extends + \Seam\Resources\AcsCredential\Warnings + { + public static function from_json( + mixed $json, + ): UnknownIssueWithAcsCredential|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsCredential\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Access permissions for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) have changed. [Reissue](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners/creating-and-encoding-card-based-credentials) (re-encode) the credential. This issue may affect the proper functioning of the credential. + */ + final class NeedsToBeReissued extends \Seam\Resources\AcsCredential\Warnings + { + public static function from_json(mixed $json): NeedsToBeReissued|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsCredential\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the requested PIN code could not be used, so the access system assigned a different code. Give the guest the assigned code. + */ + final class RequestedCodeUnavailable extends + \Seam\Resources\AcsCredential\Warnings + { + public static function from_json( + mixed $json, + ): RequestedCodeUnavailable|null { if (!$json) { return null; } @@ -377,11 +760,11 @@ public function __construct( /** * Date and time at which Seam created the warning. */ - public string|null $created_at, + string|null $created_at, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $message, + string|null $message, /** * The PIN code that was assigned instead. */ @@ -392,8 +775,27 @@ public function __construct( public string|null $original_code, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + * + * @var value-of<\Seam\Resources\AcsCredential\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case WAITING_TO_BE_ISSUED = "waiting_to_be_issued"; + case SCHEDULE_EXTERNALLY_MODIFIED = "schedule_externally_modified"; + case SCHEDULE_MODIFIED = "schedule_modified"; + case BEING_DELETED = "being_deleted"; + case UNKNOWN_ISSUE_WITH_ACS_CREDENTIAL = "unknown_issue_with_acs_credential"; + case NEEDS_TO_BE_REISSUED = "needs_to_be_reissued"; + case REQUESTED_CODE_UNAVAILABLE = "requested_code_unavailable"; } } diff --git a/src/Resources/AcsEncoder.php b/src/Resources/AcsEncoder.php index fdb6ee77..16fe4738 100644 --- a/src/Resources/AcsEncoder.php +++ b/src/Resources/AcsEncoder.php @@ -31,7 +31,7 @@ public static function from_json(mixed $json): AcsEncoder|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, errors: array_map( - fn($e) => AcsEncoder\Errors::from_json($e), + fn($e) => \Seam\Resources\AcsEncoder\Errors::from_json($e), $json->errors ?? [], ), workspace_id: $json->workspace_id ?? null, @@ -61,6 +61,8 @@ public function __construct( public string|null $display_name, /** * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + * + * @var list<\Seam\Resources\AcsEncoder\Errors> */ public array $errors, /** @@ -96,6 +98,8 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsEncoder\Errors\ErrorCode>|string|null */ public string|null $error_code, /** @@ -105,3 +109,10 @@ public function __construct( ) {} } } + +namespace Seam\Resources\AcsEncoder\Errors { + enum ErrorCode: string + { + case ACS_ENCODER_REMOVED = "acs_encoder_removed"; + } +} diff --git a/src/Resources/AcsEntrance.php b/src/Resources/AcsEntrance.php index 116c81c7..d12132df 100644 --- a/src/Resources/AcsEntrance.php +++ b/src/Resources/AcsEntrance.php @@ -16,25 +16,39 @@ public static function from_json(mixed $json): AcsEntrance|null return new self( acs_entrance_id: $json->acs_entrance_id ?? null, acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => \Seam\Resources\AcsEntrance\Errors::from_json($e), + $json->errors ?? [], + ), + space_ids: $json->space_ids ?? null, + warnings: array_map( + fn($w) => \Seam\Resources\AcsEntrance\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), akiles_metadata: isset($json->akiles_metadata) - ? AcsEntrance\AkilesMetadata::from_json( + ? \Seam\Resources\AcsEntrance\AkilesMetadata::from_json( $json->akiles_metadata, ) : null, assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - ? AcsEntrance\AssaAbloyVostioMetadata::from_json( + ? \Seam\Resources\AcsEntrance\AssaAbloyVostioMetadata::from_json( $json->assa_abloy_vostio_metadata, ) : null, avigilon_alta_metadata: isset($json->avigilon_alta_metadata) - ? AcsEntrance\AvigilonAltaMetadata::from_json( + ? \Seam\Resources\AcsEntrance\AvigilonAltaMetadata::from_json( $json->avigilon_alta_metadata, ) : null, brivo_metadata: isset($json->brivo_metadata) - ? AcsEntrance\BrivoMetadata::from_json( + ? \Seam\Resources\AcsEntrance\BrivoMetadata::from_json( $json->brivo_metadata, ) : null, @@ -46,58 +60,46 @@ public static function from_json(mixed $json): AcsEntrance|null can_unlock_with_code: $json->can_unlock_with_code ?? null, can_unlock_with_mobile_key: $json->can_unlock_with_mobile_key ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, dormakaba_ambiance_metadata: isset( $json->dormakaba_ambiance_metadata, ) - ? AcsEntrance\DormakabaAmbianceMetadata::from_json( + ? \Seam\Resources\AcsEntrance\DormakabaAmbianceMetadata::from_json( $json->dormakaba_ambiance_metadata, ) : null, dormakaba_community_metadata: isset( $json->dormakaba_community_metadata, ) - ? AcsEntrance\DormakabaCommunityMetadata::from_json( + ? \Seam\Resources\AcsEntrance\DormakabaCommunityMetadata::from_json( $json->dormakaba_community_metadata, ) : null, - errors: array_map( - fn($e) => AcsEntrance\Errors::from_json($e), - $json->errors ?? [], - ), hotek_metadata: isset($json->hotek_metadata) - ? AcsEntrance\HotekMetadata::from_json( + ? \Seam\Resources\AcsEntrance\HotekMetadata::from_json( $json->hotek_metadata, ) : null, is_locked: $json->is_locked ?? null, latch_metadata: isset($json->latch_metadata) - ? AcsEntrance\LatchMetadata::from_json( + ? \Seam\Resources\AcsEntrance\LatchMetadata::from_json( $json->latch_metadata, ) : null, salto_ks_metadata: isset($json->salto_ks_metadata) - ? AcsEntrance\SaltoKsMetadata::from_json( + ? \Seam\Resources\AcsEntrance\SaltoKsMetadata::from_json( $json->salto_ks_metadata, ) : null, salto_space_metadata: isset($json->salto_space_metadata) - ? AcsEntrance\SaltoSpaceMetadata::from_json( + ? \Seam\Resources\AcsEntrance\SaltoSpaceMetadata::from_json( $json->salto_space_metadata, ) : null, - space_ids: $json->space_ids ?? null, visionline_metadata: isset($json->visionline_metadata) - ? AcsEntrance\VisionlineMetadata::from_json( + ? \Seam\Resources\AcsEntrance\VisionlineMetadata::from_json( $json->visionline_metadata, ) : null, - warnings: array_map( - fn($w) => AcsEntrance\Warnings::from_json($w), - $json->warnings ?? [], - ), ); } @@ -124,32 +126,38 @@ public function __construct( public string|null $display_name, /** * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + * + * @var list<\Seam\Resources\AcsEntrance\Errors> */ public array $errors, /** * IDs of the spaces that the entrance is in. + * + * @var list|null */ public array|null $space_ids, /** * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + * + * @var list<\Seam\Resources\AcsEntrance\Warnings> */ public array $warnings, /** * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AkilesMetadata|null $akiles_metadata = null, + public \Seam\Resources\AcsEntrance\AkilesMetadata|null $akiles_metadata = null, /** * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, + public \Seam\Resources\AcsEntrance\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AvigilonAltaMetadata|null $avigilon_alta_metadata = null, + public \Seam\Resources\AcsEntrance\AvigilonAltaMetadata|null $avigilon_alta_metadata = null, /** * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\BrivoMetadata|null $brivo_metadata = null, + public \Seam\Resources\AcsEntrance\BrivoMetadata|null $brivo_metadata = null, /** * Indicates whether the ACS entrance can belong to a reservation via an access_grant.reservation_key. */ @@ -173,15 +181,15 @@ public function __construct( /** * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\DormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata = null, + public \Seam\Resources\AcsEntrance\DormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata = null, /** * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata = null, + public \Seam\Resources\AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata = null, /** * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\HotekMetadata|null $hotek_metadata = null, + public \Seam\Resources\AcsEntrance\HotekMetadata|null $hotek_metadata = null, /** * Indicates whether the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) is currently locked. */ @@ -189,19 +197,19 @@ public function __construct( /** * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\LatchMetadata|null $latch_metadata = null, + public \Seam\Resources\AcsEntrance\LatchMetadata|null $latch_metadata = null, /** * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\SaltoKsMetadata|null $salto_ks_metadata = null, + public \Seam\Resources\AcsEntrance\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata = null, + public \Seam\Resources\AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata = null, /** * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\VisionlineMetadata|null $visionline_metadata = null, + public \Seam\Resources\AcsEntrance\VisionlineMetadata|null $visionline_metadata = null, ) {} } } @@ -219,7 +227,11 @@ public static function from_json(mixed $json): AkilesMetadata|null } return new self( actions: array_map( - fn($a) => AkilesMetadata\Actions::from_json($a), + fn( + $a, + ) => \Seam\Resources\AcsEntrance\AkilesMetadata\Actions::from_json( + $a, + ), $json->actions ?? [], ), gadget_id: $json->gadget_id ?? null, @@ -231,6 +243,8 @@ public static function from_json(mixed $json): AkilesMetadata|null public function __construct( /** * Actions the gadget exposes (for example, open). + * + * @var list<\Seam\Resources\AcsEntrance\AkilesMetadata\Actions>|null */ public array|null $actions = null, /** @@ -279,6 +293,8 @@ public function __construct( public float|null $door_number = null, /** * Type of the door in the Vostio access system. + * + * @var value-of<\Seam\Resources\AcsEntrance\AssaAbloyVostioMetadata\DoorType>|string|null */ public string|null $door_type = null, /** @@ -651,7 +667,11 @@ public static function from_json(mixed $json): VisionlineMetadata|null door_category: $json->door_category ?? null, door_name: $json->door_name ?? null, profiles: array_map( - fn($p) => VisionlineMetadata\Profiles::from_json($p), + fn( + $p, + ) => \Seam\Resources\AcsEntrance\VisionlineMetadata\Profiles::from_json( + $p, + ), $json->profiles ?? [], ), ); @@ -660,6 +680,8 @@ public static function from_json(mixed $json): VisionlineMetadata|null public function __construct( /** * Category of the door in the Visionline access system. + * + * @var value-of<\Seam\Resources\AcsEntrance\VisionlineMetadata\DoorCategory>|string|null */ public string|null $door_category = null, /** @@ -668,13 +690,15 @@ public function __construct( public string|null $door_name = null, /** * Profile for the door in the Visionline access system. + * + * @var list<\Seam\Resources\AcsEntrance\VisionlineMetadata\Profiles>|null */ public array|null $profiles = null, ) {} } /** - * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { @@ -683,11 +707,39 @@ public static function from_json(mixed $json): Warnings|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\AcsEntrance\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsEntrance\Warnings\WarningCode::SALTO_KS_ENTRANCE_ACCESS_CODE_SUPPORT_REMOVED + => \Seam\Resources\AcsEntrance\Warnings\SaltoKsEntranceAccessCodeSupportRemoved::from_json( + $json, + ), + \Seam\Resources\AcsEntrance\Warnings\WarningCode::ENTRANCE_SHARES_ZONE + => \Seam\Resources\AcsEntrance\Warnings\EntranceSharesZone::from_json( + $json, + ), + \Seam\Resources\AcsEntrance\Warnings\WarningCode::ENTRANCE_SETUP_REQUIRED + => \Seam\Resources\AcsEntrance\Warnings\EntranceSetupRequired::from_json( + $json, + ), + \Seam\Resources\AcsEntrance\Warnings\WarningCode::SALTO_KS_PRIVACY_MODE + => \Seam\Resources\AcsEntrance\Warnings\SaltoKsPrivacyMode::from_json( + $json, + ), + \Seam\Resources\AcsEntrance\Warnings\WarningCode::PRIVACY_MODE + => \Seam\Resources\AcsEntrance\Warnings\PrivacyMode::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; } public function __construct( @@ -701,6 +753,8 @@ public function __construct( public string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsEntrance\Warnings\WarningCode>|string|null */ public string|null $warning_code, ) {} @@ -734,6 +788,16 @@ public function __construct( } } +namespace Seam\Resources\AcsEntrance\AssaAbloyVostioMetadata { + enum DoorType: string + { + case COMMON_DOOR = "CommonDoor"; + case ENTRANCE_DOOR = "EntranceDoor"; + case GUEST_DOOR = "GuestDoor"; + case ELEVATOR = "Elevator"; + } +} + namespace Seam\Resources\AcsEntrance\VisionlineMetadata { /** * Profile for the door in the Visionline access system. @@ -760,8 +824,248 @@ public function __construct( public string|null $visionline_door_profile_id = null, /** * Door profile type in the Visionline access system. + * + * @var value-of<\Seam\Resources\AcsEntrance\VisionlineMetadata\Profiles\VisionlineDoorProfileType>|string|null */ public string|null $visionline_door_profile_type = null, ) {} } + + enum DoorCategory: string + { + case ENTRANCE = "entrance"; + case GUEST = "guest"; + case ELEVATOR_READER = "elevator reader"; + case COMMON = "common"; + case COMMON_PMS = "common (PMS)"; + } +} + +namespace Seam\Resources\AcsEntrance\VisionlineMetadata\Profiles { + enum VisionlineDoorProfileType: string + { + case BLE = "BLE"; + case COMMON_DOOR = "commonDoor"; + case TOUCH = "touch"; + } +} + +namespace Seam\Resources\AcsEntrance\Warnings { + /** + * 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. + */ + final class SaltoKsEntranceAccessCodeSupportRemoved extends + \Seam\Resources\AcsEntrance\Warnings + { + public static function from_json( + mixed $json, + ): SaltoKsEntranceAccessCodeSupportRemoved|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsEntrance\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that this entrance shares a zone with other entrances in Avigilon Alta and cannot be added to an access group individually. + */ + final class EntranceSharesZone extends \Seam\Resources\AcsEntrance\Warnings + { + public static function from_json(mixed $json): EntranceSharesZone|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsEntrance\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that this entrance requires additional configuration in the access control system before Seam can fully manage it. + */ + final class EntranceSetupRequired extends + \Seam\Resources\AcsEntrance\Warnings + { + public static function from_json( + mixed $json, + ): EntranceSetupRequired|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsEntrance\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class SaltoKsPrivacyMode extends \Seam\Resources\AcsEntrance\Warnings + { + public static function from_json(mixed $json): SaltoKsPrivacyMode|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsEntrance\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class PrivacyMode extends \Seam\Resources\AcsEntrance\Warnings + { + public static function from_json(mixed $json): PrivacyMode|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsEntrance\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case SALTO_KS_ENTRANCE_ACCESS_CODE_SUPPORT_REMOVED = "salto_ks_entrance_access_code_support_removed"; + case ENTRANCE_SHARES_ZONE = "entrance_shares_zone"; + case ENTRANCE_SETUP_REQUIRED = "entrance_setup_required"; + case SALTO_KS_PRIVACY_MODE = "salto_ks_privacy_mode"; + case PRIVACY_MODE = "privacy_mode"; + } } diff --git a/src/Resources/AcsSystem.php b/src/Resources/AcsSystem.php index 2bbc80c4..88a89b3c 100644 --- a/src/Resources/AcsSystem.php +++ b/src/Resources/AcsSystem.php @@ -16,41 +16,43 @@ public static function from_json(mixed $json): AcsSystem|null return null; } return new self( - acs_access_group_count: $json->acs_access_group_count ?? null, acs_system_id: $json->acs_system_id ?? null, - acs_user_count: $json->acs_user_count ?? null, connected_account_id: $json->connected_account_id ?? null, connected_account_ids: $json->connected_account_ids ?? null, created_at: $json->created_at ?? null, - default_credential_manager_acs_system_id: $json->default_credential_manager_acs_system_id ?? - null, errors: array_map( - fn($e) => AcsSystem\Errors::from_json($e), + fn($e) => \Seam\Resources\AcsSystem\Errors::from_json($e), $json->errors ?? [], ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, image_alt_text: $json->image_alt_text ?? null, image_url: $json->image_url ?? null, is_credential_manager: $json->is_credential_manager ?? null, location: isset($json->location) - ? AcsSystem\Location::from_json($json->location) + ? \Seam\Resources\AcsSystem\Location::from_json( + $json->location, + ) : null, name: $json->name ?? null, + warnings: array_map( + fn($w) => \Seam\Resources\AcsSystem\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + acs_access_group_count: $json->acs_access_group_count ?? null, + acs_user_count: $json->acs_user_count ?? null, + default_credential_manager_acs_system_id: $json->default_credential_manager_acs_system_id ?? + null, + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, system_type: $json->system_type ?? null, system_type_display_name: $json->system_type_display_name ?? null, visionline_metadata: isset($json->visionline_metadata) - ? AcsSystem\VisionlineMetadata::from_json( + ? \Seam\Resources\AcsSystem\VisionlineMetadata::from_json( $json->visionline_metadata, ) : null, - warnings: array_map( - fn($w) => AcsSystem\Warnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, ); } @@ -66,6 +68,7 @@ public function __construct( /** * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). * + * @var list|null * @deprecated Use `connected_account_id`. */ public array|null $connected_account_ids, @@ -75,6 +78,8 @@ public function __construct( public string|null $created_at, /** * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * @var list<\Seam\Resources\AcsSystem\Errors> */ public array $errors, /** @@ -92,13 +97,15 @@ public function __construct( /** * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public AcsSystem\Location|null $location, + public \Seam\Resources\AcsSystem\Location|null $location, /** * Name of the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ public string|null $name, /** * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * @var list<\Seam\Resources\AcsSystem\Warnings> */ public array $warnings, /** @@ -119,6 +126,8 @@ public function __construct( public string|null $default_credential_manager_acs_system_id = null, /** * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. + * + * @var value-of<\Seam\Resources\AcsSystem\ExternalType>|string|null */ public string|null $external_type = null, /** @@ -126,6 +135,7 @@ public function __construct( */ public string|null $external_type_display_name = null, /** + * @var value-of<\Seam\Resources\AcsSystem\SystemType>|string|null * @deprecated Use `external_type`. */ public string|null $system_type = null, @@ -136,14 +146,14 @@ public function __construct( /** * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public AcsSystem\VisionlineMetadata|null $visionline_metadata = null, + public \Seam\Resources\AcsSystem\VisionlineMetadata|null $visionline_metadata = null, ) {} } } namespace Seam\Resources\AcsSystem { /** - * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -152,12 +162,55 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\AcsSystem\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsSystem\Errors\ErrorCode::SEAM_BRIDGE_DISCONNECTED + => \Seam\Resources\AcsSystem\Errors\SeamBridgeDisconnected::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Errors\ErrorCode::BRIDGE_DISCONNECTED + => \Seam\Resources\AcsSystem\Errors\BridgeDisconnected::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Errors\ErrorCode::VISIONLINE_INSTANCE_UNREACHABLE + => \Seam\Resources\AcsSystem\Errors\VisionlineInstanceUnreachable::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Errors\ErrorCode::SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED + => \Seam\Resources\AcsSystem\Errors\SaltoKsSubscriptionLimitExceeded::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Errors\ErrorCode::INSUFFICIENT_PERMISSIONS + => \Seam\Resources\AcsSystem\Errors\InsufficientPermissions::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Errors\ErrorCode::ACS_SYSTEM_DISCONNECTED + => \Seam\Resources\AcsSystem\Errors\AcsSystemDisconnected::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Errors\ErrorCode::ACCOUNT_DISCONNECTED + => \Seam\Resources\AcsSystem\Errors\AccountDisconnected::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Errors\ErrorCode::SALTO_KS_CERTIFICATION_EXPIRED + => \Seam\Resources\AcsSystem\Errors\SaltoKsCertificationExpired::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Errors\ErrorCode::PROVIDER_SERVICE_UNAVAILABLE + => \Seam\Resources\AcsSystem\Errors\ProviderServiceUnavailable::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -167,16 +220,14 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null */ public string|null $error_code, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - /** - * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error = null, ) {} } @@ -235,21 +286,574 @@ public function __construct( } /** - * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { public static function from_json(mixed $json): Warnings|null { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\AcsSystem\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsSystem\Warnings\WarningCode::SALTO_KS_SUBSCRIPTION_LIMIT_ALMOST_REACHED + => \Seam\Resources\AcsSystem\Warnings\SaltoKsSubscriptionLimitAlmostReached::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Warnings\WarningCode::TIME_ZONE_DOES_NOT_MATCH_LOCATION + => \Seam\Resources\AcsSystem\Warnings\TimeZoneDoesNotMatchLocation::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Warnings\WarningCode::SETUP_REQUIRED + => \Seam\Resources\AcsSystem\Warnings\SetupRequired::from_json( + $json, + ), + \Seam\Resources\AcsSystem\Warnings\WarningCode::UNKNOWN_ISSUE_WITH_ACS_SYSTEM + => \Seam\Resources\AcsSystem\Warnings\UnknownIssueWithAcsSystem::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } + + enum ExternalType: string + { + case PTI_SITE = "pti_site"; + case AVIGILON_ALTA_ORG = "avigilon_alta_org"; + case SALTO_KS_SITE = "salto_ks_site"; + case SALTO_SPACE_SYSTEM = "salto_space_system"; + case BRIVO_ACCOUNT = "brivo_account"; + case HID_CREDENTIAL_MANAGER_ORGANIZATION = "hid_credential_manager_organization"; + case VISIONLINE_SYSTEM = "visionline_system"; + case ASSA_ABLOY_CREDENTIAL_SERVICE = "assa_abloy_credential_service"; + case LATCH_BUILDING = "latch_building"; + case DORMAKABA_COMMUNITY_SITE = "dormakaba_community_site"; + case DORMAKABA_AMBIANCE_SITE = "dormakaba_ambiance_site"; + case LEGIC_CONNECT_CREDENTIAL_SERVICE = "legic_connect_credential_service"; + case ASSA_ABLOY_VOSTIO = "assa_abloy_vostio"; + case ASSA_ABLOY_VOSTIO_CREDENTIAL_SERVICE = "assa_abloy_vostio_credential_service"; + case HOTEK_SITE = "hotek_site"; + case KISI_ORGANIZATION = "kisi_organization"; + case AKILES_ORGANIZATION = "akiles_organization"; + } + + enum SystemType: string + { + case PTI_SITE = "pti_site"; + case AVIGILON_ALTA_ORG = "avigilon_alta_org"; + case SALTO_KS_SITE = "salto_ks_site"; + case SALTO_SPACE_SYSTEM = "salto_space_system"; + case BRIVO_ACCOUNT = "brivo_account"; + case HID_CREDENTIAL_MANAGER_ORGANIZATION = "hid_credential_manager_organization"; + case VISIONLINE_SYSTEM = "visionline_system"; + case ASSA_ABLOY_CREDENTIAL_SERVICE = "assa_abloy_credential_service"; + case LATCH_BUILDING = "latch_building"; + case DORMAKABA_COMMUNITY_SITE = "dormakaba_community_site"; + case DORMAKABA_AMBIANCE_SITE = "dormakaba_ambiance_site"; + case LEGIC_CONNECT_CREDENTIAL_SERVICE = "legic_connect_credential_service"; + case ASSA_ABLOY_VOSTIO = "assa_abloy_vostio"; + case ASSA_ABLOY_VOSTIO_CREDENTIAL_SERVICE = "assa_abloy_vostio_credential_service"; + case HOTEK_SITE = "hotek_site"; + case KISI_ORGANIZATION = "kisi_organization"; + case AKILES_ORGANIZATION = "akiles_organization"; + } +} + +namespace Seam\Resources\AcsSystem\Errors { + /** + * Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/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](https://docs.seam.co/core-concepts/workspaces). + * See also [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system-errors-seam_bridge_disconnected). + */ + final class SeamBridgeDisconnected extends \Seam\Resources\AcsSystem\Errors + { + public static function from_json( + mixed $json, + ): SeamBridgeDisconnected|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/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](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system-errors-seam_bridge_disconnected). + */ + final class BridgeDisconnected extends \Seam\Resources\AcsSystem\Errors + { + public static function from_json(mixed $json): BridgeDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that [Seam Bridge](https://docs.seam.co/capability-guides/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](https://docs.seam.co/device-and-system-integration-guides/assa-abloy-visionline-access-control-system). + * For example, the IP address of the on-premises access control system may be set incorrectly within the Seam [workspace](https://docs.seam.co/core-concepts/workspaces). + * See also [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system-errors-visionline_instance_unreachable). + */ + final class VisionlineInstanceUnreachable extends + \Seam\Resources\AcsSystem\Errors + { + public static function from_json( + mixed $json, + ): VisionlineInstanceUnreachable|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * 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. + */ + final class SaltoKsSubscriptionLimitExceeded extends + \Seam\Resources\AcsSystem\Errors + { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitExceeded|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that Seam's integration user does not have sufficient permissions on the provider's system backing this [access control system](https://docs.seam.co/low-level-apis/access-systems). 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. + */ + final class InsufficientPermissions extends \Seam\Resources\AcsSystem\Errors + { + public static function from_json( + mixed $json, + ): InsufficientPermissions|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the [access control system](https://docs.seam.co/low-level-apis/access-systems) has been disconnected. See [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system) to resolve the issue. + */ + final class AcsSystemDisconnected extends \Seam\Resources\AcsSystem\Errors + { + public static function from_json( + mixed $json, + ): AcsSystemDisconnected|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the login credentials are invalid. Reconnect the account using a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews) to restore access. + */ + final class AccountDisconnected extends \Seam\Resources\AcsSystem\Errors + { + public static function from_json(mixed $json): AccountDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the [access control system](https://docs.seam.co/low-level-apis/access-systems) has lost its Salto KS certification. Contact [support](mailto:support@seam.co) to regain access. + */ + final class SaltoKsCertificationExpired extends + \Seam\Resources\AcsSystem\Errors + { + public static function from_json( + mixed $json, + ): SaltoKsCertificationExpired|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the access control system provider's service is temporarily unavailable. Seam will automatically retry and reconnect when the service becomes available again. + */ + final class ProviderServiceUnavailable extends + \Seam\Resources\AcsSystem\Errors + { + public static function from_json( + mixed $json, + ): ProviderServiceUnavailable|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case SEAM_BRIDGE_DISCONNECTED = "seam_bridge_disconnected"; + case BRIDGE_DISCONNECTED = "bridge_disconnected"; + case VISIONLINE_INSTANCE_UNREACHABLE = "visionline_instance_unreachable"; + case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; + case INSUFFICIENT_PERMISSIONS = "insufficient_permissions"; + case ACS_SYSTEM_DISCONNECTED = "acs_system_disconnected"; + case ACCOUNT_DISCONNECTED = "account_disconnected"; + case SALTO_KS_CERTIFICATION_EXPIRED = "salto_ks_certification_expired"; + case PROVIDER_SERVICE_UNAVAILABLE = "provider_service_unavailable"; + } +} + +namespace Seam\Resources\AcsSystem\Warnings { + /** + * 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. + */ + final class SaltoKsSubscriptionLimitAlmostReached extends + \Seam\Resources\AcsSystem\Warnings + { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitAlmostReached|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates the [access control system](https://docs.seam.co/low-level-apis/access-systems) time zone could not be determined because the reported physical location does not match the time zone configured on the physical [ACS entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + final class TimeZoneDoesNotMatchLocation extends + \Seam\Resources\AcsSystem\Warnings + { + public static function from_json( + mixed $json, + ): TimeZoneDoesNotMatchLocation|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, misconfigured_acs_entrance_ids: $json->misconfigured_acs_entrance_ids ?? null, - warning_code: $json->warning_code ?? null, ); } @@ -257,19 +861,120 @@ public function __construct( /** * Date and time at which Seam created the warning. */ - public string|null $created_at, + string|null $created_at, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $message, + string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Warnings\WarningCode>|string|null */ - public string|null $warning_code, + string|null $warning_code, /** + * @var list|null * @deprecated this field is deprecated. */ public array|null $misconfigured_acs_entrance_ids = null, - ) {} + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class SetupRequired extends \Seam\Resources\AcsSystem\Warnings + { + public static function from_json(mixed $json): SetupRequired|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that Seam encountered an unexpected error while syncing this [access control system](https://docs.seam.co/low-level-apis/access-systems), 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](mailto:support@seam.co). + */ + final class UnknownIssueWithAcsSystem extends + \Seam\Resources\AcsSystem\Warnings + { + public static function from_json( + mixed $json, + ): UnknownIssueWithAcsSystem|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AcsSystem\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case SALTO_KS_SUBSCRIPTION_LIMIT_ALMOST_REACHED = "salto_ks_subscription_limit_almost_reached"; + case TIME_ZONE_DOES_NOT_MATCH_LOCATION = "time_zone_does_not_match_location"; + case SETUP_REQUIRED = "setup_required"; + case UNKNOWN_ISSUE_WITH_ACS_SYSTEM = "unknown_issue_with_acs_system"; } } diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index 4ed77aa4..c063569a 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -16,39 +16,50 @@ public static function from_json(mixed $json): AcsUser|null return null; } return new self( - access_schedule: isset($json->access_schedule) - ? AcsUser\AccessSchedule::from_json($json->access_schedule) - : null, acs_system_id: $json->acs_system_id ?? null, acs_user_id: $json->acs_user_id ?? null, connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - email: $json->email ?? null, - email_address: $json->email_address ?? null, errors: array_map( - fn($e) => AcsUser\Errors::from_json($e), + fn($e) => \Seam\Resources\AcsUser\Errors::from_json($e), $json->errors ?? [], ), + is_managed: $json->is_managed ?? null, + warnings: array_map( + fn($w) => \Seam\Resources\AcsUser\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + access_schedule: isset($json->access_schedule) + ? \Seam\Resources\AcsUser\AccessSchedule::from_json( + $json->access_schedule, + ) + : null, + email: $json->email ?? null, + email_address: $json->email_address ?? null, external_type: $json->external_type ?? null, external_type_display_name: $json->external_type_display_name ?? null, full_name: $json->full_name ?? null, hid_acs_system_id: $json->hid_acs_system_id ?? null, - is_managed: $json->is_managed ?? null, is_suspended: $json->is_suspended ?? null, pending_mutations: array_map( - fn($p) => AcsUser\PendingMutations::from_json($p), + fn( + $p, + ) => \Seam\Resources\AcsUser\PendingMutations::from_json( + $p, + ), $json->pending_mutations ?? [], ), phone_number: $json->phone_number ?? null, salto_ks_metadata: isset($json->salto_ks_metadata) - ? AcsUser\SaltoKsMetadata::from_json( + ? \Seam\Resources\AcsUser\SaltoKsMetadata::from_json( $json->salto_ks_metadata, ) : null, salto_space_metadata: isset($json->salto_space_metadata) - ? AcsUser\SaltoSpaceMetadata::from_json( + ? \Seam\Resources\AcsUser\SaltoSpaceMetadata::from_json( $json->salto_space_metadata, ) : null, @@ -58,11 +69,6 @@ public static function from_json(mixed $json): AcsUser|null user_identity_id: $json->user_identity_id ?? null, user_identity_phone_number: $json->user_identity_phone_number ?? null, - warnings: array_map( - fn($w) => AcsUser\Warnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, ); } @@ -89,6 +95,8 @@ public function __construct( public string|null $display_name, /** * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + * + * @var list<\Seam\Resources\AcsUser\Errors> */ public array $errors, /** @@ -97,6 +105,8 @@ public function __construct( public true|null $is_managed, /** * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + * + * @var list<\Seam\Resources\AcsUser\Warnings> */ public array $warnings, /** @@ -106,7 +116,7 @@ public function __construct( /** * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. */ - public AcsUser\AccessSchedule|null $access_schedule = null, + public \Seam\Resources\AcsUser\AccessSchedule|null $access_schedule = null, /** * @deprecated use email_address. */ @@ -117,6 +127,8 @@ public function __construct( public string|null $email_address = null, /** * Brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. + * + * @var value-of<\Seam\Resources\AcsUser\ExternalType>|string|null */ public string|null $external_type = null, /** @@ -137,6 +149,8 @@ public function __construct( public bool|null $is_suspended = null, /** * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. + * + * @var list<\Seam\Resources\AcsUser\PendingMutations>|null */ public array|null $pending_mutations = null, /** @@ -146,11 +160,11 @@ public function __construct( /** * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public AcsUser\SaltoKsMetadata|null $salto_ks_metadata = null, + public \Seam\Resources\AcsUser\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public AcsUser\SaltoSpaceMetadata|null $salto_space_metadata = null, + public \Seam\Resources\AcsUser\SaltoSpaceMetadata|null $salto_space_metadata = null, /** * Email address of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ @@ -201,7 +215,7 @@ public function __construct( } /** - * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -210,11 +224,43 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\AcsUser\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsUser\Errors\ErrorCode::DELETED_EXTERNALLY + => \Seam\Resources\AcsUser\Errors\DeletedExternally::from_json( + $json, + ), + \Seam\Resources\AcsUser\Errors\ErrorCode::SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED + => \Seam\Resources\AcsUser\Errors\SaltoKsSubscriptionLimitExceeded::from_json( + $json, + ), + \Seam\Resources\AcsUser\Errors\ErrorCode::FAILED_TO_CREATE_ON_ACS_SYSTEM + => \Seam\Resources\AcsUser\Errors\FailedToCreateOnAcsSystem::from_json( + $json, + ), + \Seam\Resources\AcsUser\Errors\ErrorCode::FAILED_TO_UPDATE_ON_ACS_SYSTEM + => \Seam\Resources\AcsUser\Errors\FailedToUpdateOnAcsSystem::from_json( + $json, + ), + \Seam\Resources\AcsUser\Errors\ErrorCode::FAILED_TO_DELETE_ON_ACS_SYSTEM + => \Seam\Resources\AcsUser\Errors\FailedToDeleteOnAcsSystem::from_json( + $json, + ), + \Seam\Resources\AcsUser\Errors\ErrorCode::LATCH_CONFLICT_WITH_RESIDENT_USER + => \Seam\Resources\AcsUser\Errors\LatchConflictWithResidentUser::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -222,6 +268,9 @@ public function __construct( * Date and time at which Seam created the error. */ public string|null $created_at, + /** + * @var value-of<\Seam\Resources\AcsUser\Errors\ErrorCode>|string|null + */ public string|null $error_code, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. @@ -231,7 +280,7 @@ public function __construct( } /** - * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. + * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. Known mutation_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class PendingMutations { @@ -240,46 +289,72 @@ public static function from_json(mixed $json): PendingMutations|null if (!$json) { return null; } - return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? PendingMutations\From::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - scheduled_at: $json->scheduled_at ?? null, - to: isset($json->to) - ? PendingMutations\To::from_json($json->to) - : null, - variant: $json->variant ?? null, - ); + $discriminant = is_string($json->mutation_code ?? null) + ? \Seam\Resources\AcsUser\PendingMutations\MutationCode::tryFrom( + $json->mutation_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsUser\PendingMutations\MutationCode::CREATING + => \Seam\Resources\AcsUser\PendingMutations\Creating::from_json( + $json, + ), + \Seam\Resources\AcsUser\PendingMutations\MutationCode::DELETING + => \Seam\Resources\AcsUser\PendingMutations\Deleting::from_json( + $json, + ), + \Seam\Resources\AcsUser\PendingMutations\MutationCode::DEFERRING_CREATION + => \Seam\Resources\AcsUser\PendingMutations\DeferringCreation::from_json( + $json, + ), + \Seam\Resources\AcsUser\PendingMutations\MutationCode::UPDATING_USER_INFORMATION + => \Seam\Resources\AcsUser\PendingMutations\UpdatingUserInformation::from_json( + $json, + ), + \Seam\Resources\AcsUser\PendingMutations\MutationCode::UPDATING_ACCESS_SCHEDULE + => \Seam\Resources\AcsUser\PendingMutations\UpdatingAccessSchedule::from_json( + $json, + ), + \Seam\Resources\AcsUser\PendingMutations\MutationCode::UPDATING_SUSPENSION_STATE + => \Seam\Resources\AcsUser\PendingMutations\UpdatingSuspensionState::from_json( + $json, + ), + \Seam\Resources\AcsUser\PendingMutations\MutationCode::UPDATING_GROUP_MEMBERSHIP + => \Seam\Resources\AcsUser\PendingMutations\UpdatingGroupMembership::from_json( + $json, + ), + \Seam\Resources\AcsUser\PendingMutations\MutationCode::DEFERRING_GROUP_MEMBERSHIP_UPDATE + => \Seam\Resources\AcsUser\PendingMutations\DeferringGroupMembershipUpdate::from_json( + $json, + ), + \Seam\Resources\AcsUser\PendingMutations\MutationCode::UPDATING_CREDENTIAL_ASSIGNMENT + => \Seam\Resources\AcsUser\PendingMutations\UpdatingCredentialAssignment::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ), + }; } public function __construct( - /** - * ID of the access group involved in the scheduled change. - */ - public string|null $acs_access_group_id, /** * Date and time at which the mutation was created. */ public string|null $created_at, - public PendingMutations\From|null $from, /** * Detailed description of the mutation. */ public string|null $message, - public string|null $mutation_code, - public PendingMutations\To|null $to, - /** - * Whether the user is scheduled to be added to or removed from the access group. - */ - public string|null $variant, /** - * Optional: When the user creation is scheduled to occur. + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null */ - public string|null $scheduled_at = null, + public string|null $mutation_code, ) {} } @@ -333,7 +408,7 @@ public function __construct( } /** - * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { @@ -342,11 +417,39 @@ public static function from_json(mixed $json): Warnings|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\AcsUser\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\AcsUser\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\AcsUser\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\AcsUser\Warnings\WarningCode::SALTO_KS_USER_NOT_SUBSCRIBED + => \Seam\Resources\AcsUser\Warnings\SaltoKsUserNotSubscribed::from_json( + $json, + ), + \Seam\Resources\AcsUser\Warnings\WarningCode::ACS_USER_INACTIVE + => \Seam\Resources\AcsUser\Warnings\AcsUserInactive::from_json( + $json, + ), + \Seam\Resources\AcsUser\Warnings\WarningCode::UNKNOWN_ISSUE_WITH_ACS_USER + => \Seam\Resources\AcsUser\Warnings\UnknownIssueWithAcsUser::from_json( + $json, + ), + \Seam\Resources\AcsUser\Warnings\WarningCode::LATCH_RESIDENT_USER + => \Seam\Resources\AcsUser\Warnings\LatchResidentUser::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; } public function __construct( @@ -358,113 +461,1250 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * @var value-of<\Seam\Resources\AcsUser\Warnings\WarningCode>|string|null + */ public string|null $warning_code, ) {} } + + enum ExternalType: string + { + case PTI_USER = "pti_user"; + case BRIVO_USER = "brivo_user"; + case HID_CREDENTIAL_MANAGER_USER = "hid_credential_manager_user"; + case SALTO_SITE_USER = "salto_site_user"; + case LATCH_USER = "latch_user"; + case DORMAKABA_COMMUNITY_USER = "dormakaba_community_user"; + case SALTO_SPACE_USER = "salto_space_user"; + case AVIGILON_ALTA_USER = "avigilon_alta_user"; + case KISI_USER = "kisi_user"; + } } -namespace Seam\Resources\AcsUser\PendingMutations { - class From +namespace Seam\Resources\AcsUser\Errors { + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was deleted from the [access system](https://docs.seam.co/low-level-apis/access-systems) outside of Seam. + */ + final class DeletedExternally extends \Seam\Resources\AcsUser\Errors { - public static function from_json(mixed $json): From|null + public static function from_json(mixed $json): DeletedExternally|null { if (!$json) { return null; } return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - email_address: $json->email_address ?? null, - ends_at: $json->ends_at ?? null, - full_name: $json->full_name ?? null, - is_suspended: $json->is_suspended ?? null, - phone_number: $json->phone_number ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Old access group ID. + * Date and time at which Seam created the error. */ - public string|null $acs_access_group_id, + string|null $created_at, /** - * Previous credential ID. + * @var value-of<\Seam\Resources\AcsUser\Errors\ErrorCode>|string|null */ - public string|null $acs_credential_id, + string|null $error_code, /** - * Starting time for the access schedule. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $ends_at, - public bool|null $is_suspended, + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) could not be subscribed on Salto KS because the subscription limit has been exceeded. + */ + final class SaltoKsSubscriptionLimitExceeded extends + \Seam\Resources\AcsUser\Errors + { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitExceeded|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Starting time for the access schedule. + * Date and time at which Seam created the error. */ - public string|null $starts_at, + string|null $created_at, /** - * Email address of the access system user. + * @var value-of<\Seam\Resources\AcsUser\Errors\ErrorCode>|string|null */ - public string|null $email_address = null, + string|null $error_code, /** - * Full name of the access system user. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $full_name = null, + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was not created on the [access system](https://docs.seam.co/low-level-apis/access-systems). This is likely due to an internal unexpected error. Contact Seam [support](mailto:support@seam.co). + */ + final class FailedToCreateOnAcsSystem extends \Seam\Resources\AcsUser\Errors + { + public static function from_json( + mixed $json, + ): FailedToCreateOnAcsSystem|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Phone number of the access system user. + * Date and time at which Seam created the error. */ - public string|null $phone_number = null, - ) {} + string|null $created_at, + /** + * @var value-of<\Seam\Resources\AcsUser\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } - class To + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was not updated on the [access system](https://docs.seam.co/low-level-apis/access-systems). This is likely due to an internal unexpected error. Contact Seam [support](mailto:support@seam.co). + */ + final class FailedToUpdateOnAcsSystem extends \Seam\Resources\AcsUser\Errors { - public static function from_json(mixed $json): To|null - { + public static function from_json( + mixed $json, + ): FailedToUpdateOnAcsSystem|null { if (!$json) { return null; } return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - email_address: $json->email_address ?? null, - ends_at: $json->ends_at ?? null, - full_name: $json->full_name ?? null, - is_suspended: $json->is_suspended ?? null, - phone_number: $json->phone_number ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * New access group ID. + * Date and time at which Seam created the error. */ - public string|null $acs_access_group_id, + string|null $created_at, /** - * New credential ID. + * @var value-of<\Seam\Resources\AcsUser\Errors\ErrorCode>|string|null */ - public string|null $acs_credential_id, + string|null $error_code, /** - * Starting time for the access schedule. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $ends_at, - public bool|null $is_suspended, + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was not deleted on the [access system](https://docs.seam.co/low-level-apis/access-systems). This is likely due to an internal unexpected error. Contact Seam [support](mailto:support@seam.co). + */ + final class FailedToDeleteOnAcsSystem extends \Seam\Resources\AcsUser\Errors + { + public static function from_json( + mixed $json, + ): FailedToDeleteOnAcsSystem|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Starting time for the access schedule. + * Date and time at which Seam created the error. */ - public string|null $starts_at, + string|null $created_at, /** - * Email address of the access system user. + * @var value-of<\Seam\Resources\AcsUser\Errors\ErrorCode>|string|null */ - public string|null $email_address = null, + string|null $error_code, /** - * Full name of the access system user. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $full_name = null, + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was created from the Seam API but also exists on Mission Control. This is unsupported. Contact Seam [support](mailto:support@seam.co). + */ + final class LatchConflictWithResidentUser extends + \Seam\Resources\AcsUser\Errors + { + public static function from_json( + mixed $json, + ): LatchConflictWithResidentUser|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Phone number of the access system user. + * Date and time at which Seam created the error. */ - public string|null $phone_number = null, - ) {} + string|null $created_at, + /** + * @var value-of<\Seam\Resources\AcsUser\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case DELETED_EXTERNALLY = "deleted_externally"; + case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; + case FAILED_TO_CREATE_ON_ACS_SYSTEM = "failed_to_create_on_acs_system"; + case FAILED_TO_UPDATE_ON_ACS_SYSTEM = "failed_to_update_on_acs_system"; + case FAILED_TO_DELETE_ON_ACS_SYSTEM = "failed_to_delete_on_acs_system"; + case LATCH_CONFLICT_WITH_RESIDENT_USER = "latch_conflict_with_resident_user"; + } +} + +namespace Seam\Resources\AcsUser\PendingMutations { + /** + * Seam is in the process of pushing a user creation to the integrated access system. + */ + final class Creating extends \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json(mixed $json): Creating|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing a user deletion to the integrated access system. + */ + final class Deleting extends \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json(mixed $json): Deleting|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * User exists in Seam but has not been pushed to the provider yet. Will be created when a credential is issued. + */ + final class DeferringCreation extends + \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json(mixed $json): DeferringCreation|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + scheduled_at: $json->scheduled_at ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * Optional: When the user creation is scheduled to occur. + */ + public string|null $scheduled_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + final class UpdatingUserInformation extends + \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingUserInformation|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingUserInformation\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingUserInformation\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Old access system user information. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingUserInformation\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New access system user information. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingUserInformation\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an access schedule update to the integrated access system. + */ + final class UpdatingAccessSchedule extends + \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingAccessSchedule|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingAccessSchedule\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingAccessSchedule\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Old access schedule information. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingAccessSchedule\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New access schedule information. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingAccessSchedule\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing a suspension state update to the integrated access system. + */ + final class UpdatingSuspensionState extends + \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingSuspensionState|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingSuspensionState\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingSuspensionState\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Old user suspension state information. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingSuspensionState\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New user suspension state information. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingSuspensionState\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of pushing an access group membership update to the integrated access system. + */ + final class UpdatingGroupMembership extends + \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingGroupMembership|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingGroupMembership\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingGroupMembership\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Old access group membership. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingGroupMembership\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New access group membership. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingGroupMembership\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * A scheduled access group membership change is pending for this user. + */ + final class DeferringGroupMembershipUpdate extends + \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json( + mixed $json, + ): DeferringGroupMembershipUpdate|null { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + variant: $json->variant ?? null, + ); + } + + public function __construct( + /** + * ID of the access group involved in the scheduled change. + */ + public string|null $acs_access_group_id, + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * Whether the user is scheduled to be added to or removed from the access group. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\DeferringGroupMembershipUpdate\Variant>|string|null + */ + public string|null $variant, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of assigning or unassigning a credential to the user on the integrated access system. + */ + final class UpdatingCredentialAssignment extends + \Seam\Resources\AcsUser\PendingMutations + { + public static function from_json( + mixed $json, + ): UpdatingCredentialAssignment|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingCredentialAssignment\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\AcsUser\PendingMutations\UpdatingCredentialAssignment\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Previous credential assignment. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingCredentialAssignment\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + * + * @var value-of<\Seam\Resources\AcsUser\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New credential assignment. + */ + public \Seam\Resources\AcsUser\PendingMutations\UpdatingCredentialAssignment\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + enum MutationCode: string + { + case CREATING = "creating"; + case DELETING = "deleting"; + case DEFERRING_CREATION = "deferring_creation"; + case UPDATING_USER_INFORMATION = "updating_user_information"; + case UPDATING_ACCESS_SCHEDULE = "updating_access_schedule"; + case UPDATING_SUSPENSION_STATE = "updating_suspension_state"; + case UPDATING_GROUP_MEMBERSHIP = "updating_group_membership"; + case DEFERRING_GROUP_MEMBERSHIP_UPDATE = "deferring_group_membership_update"; + case UPDATING_CREDENTIAL_ASSIGNMENT = "updating_credential_assignment"; + } +} + +namespace Seam\Resources\AcsUser\PendingMutations\UpdatingUserInformation { + /** + * Old access system user information. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + email_address: $json->email_address ?? null, + full_name: $json->full_name ?? null, + phone_number: $json->phone_number ?? null, + ); + } + + public function __construct( + /** + * Email address of the access system user. + */ + public string|null $email_address = null, + /** + * Full name of the access system user. + */ + public string|null $full_name = null, + /** + * Phone number of the access system user. + */ + public string|null $phone_number = null, + ) {} + } + + /** + * New access system user information. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + email_address: $json->email_address ?? null, + full_name: $json->full_name ?? null, + phone_number: $json->phone_number ?? null, + ); + } + + public function __construct( + /** + * Email address of the access system user. + */ + public string|null $email_address = null, + /** + * Full name of the access system user. + */ + public string|null $full_name = null, + /** + * Phone number of the access system user. + */ + public string|null $phone_number = null, + ) {} + } +} + +namespace Seam\Resources\AcsUser\PendingMutations\UpdatingAccessSchedule { + /** + * Old access schedule information. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * Starting time for the access schedule. + */ + public string|null $ends_at, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } + + /** + * New access schedule information. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * Starting time for the access schedule. + */ + public string|null $ends_at, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } +} + +namespace Seam\Resources\AcsUser\PendingMutations\UpdatingSuspensionState { + /** + * Old user suspension state information. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(is_suspended: $json->is_suspended ?? null); + } + + public function __construct(public bool|null $is_suspended) {} + } + + /** + * New user suspension state information. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(is_suspended: $json->is_suspended ?? null); + } + + public function __construct(public bool|null $is_suspended) {} + } +} + +namespace Seam\Resources\AcsUser\PendingMutations\UpdatingGroupMembership { + /** + * Old access group membership. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + ); + } + + public function __construct( + /** + * Old access group ID. + */ + public string|null $acs_access_group_id, + ) {} + } + + /** + * New access group membership. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + ); + } + + public function __construct( + /** + * New access group ID. + */ + public string|null $acs_access_group_id, + ) {} + } +} + +namespace Seam\Resources\AcsUser\PendingMutations\DeferringGroupMembershipUpdate { + enum Variant: string + { + case ADDING = "adding"; + case REMOVING = "removing"; + } +} + +namespace Seam\Resources\AcsUser\PendingMutations\UpdatingCredentialAssignment { + /** + * Previous credential assignment. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + acs_credential_id: $json->acs_credential_id ?? null, + ); + } + + public function __construct( + /** + * Previous credential ID. + */ + public string|null $acs_credential_id, + ) {} + } + + /** + * New credential assignment. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + acs_credential_id: $json->acs_credential_id ?? null, + ); + } + + public function __construct( + /** + * New credential ID. + */ + public string|null $acs_credential_id, + ) {} + } +} + +namespace Seam\Resources\AcsUser\Warnings { + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is being deleted from the [access system](https://docs.seam.co/low-level-apis/access-systems). This is a temporary state, and the access system user will be deleted shortly. + */ + final class BeingDeleted extends \Seam\Resources\AcsUser\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * @var value-of<\Seam\Resources\AcsUser\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) 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. + */ + final class SaltoKsUserNotSubscribed extends + \Seam\Resources\AcsUser\Warnings + { + public static function from_json( + mixed $json, + ): SaltoKsUserNotSubscribed|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * @var value-of<\Seam\Resources\AcsUser\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) 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. + */ + final class AcsUserInactive extends \Seam\Resources\AcsUser\Warnings + { + public static function from_json(mixed $json): AcsUserInactive|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * @var value-of<\Seam\Resources\AcsUser\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * An unknown issue occurred while syncing the state of this [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) with the provider. This issue may affect the proper functioning of this user. + */ + final class UnknownIssueWithAcsUser extends \Seam\Resources\AcsUser\Warnings + { + public static function from_json( + mixed $json, + ): UnknownIssueWithAcsUser|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * @var value-of<\Seam\Resources\AcsUser\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was created on Latch Mission Control. Please use the Latch Mission Control to manage this user. + */ + final class LatchResidentUser extends \Seam\Resources\AcsUser\Warnings + { + public static function from_json(mixed $json): LatchResidentUser|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * @var value-of<\Seam\Resources\AcsUser\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case BEING_DELETED = "being_deleted"; + case SALTO_KS_USER_NOT_SUBSCRIBED = "salto_ks_user_not_subscribed"; + case ACS_USER_INACTIVE = "acs_user_inactive"; + case UNKNOWN_ISSUE_WITH_ACS_USER = "unknown_issue_with_acs_user"; + case LATCH_RESIDENT_USER = "latch_resident_user"; } } diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 783e7d1d..7977ece8 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -2,7 +2,7 @@ namespace Seam\Resources { /** - * Locking a door is pending. + * Base class for actions whose completion is tracked asynchronously. Known action_type values use subclasses; unknown values use this base class and retain their raw discriminator. */ class ActionAttempt { @@ -11,17 +11,106 @@ public static function from_json(mixed $json): ActionAttempt|null if (!$json) { return null; } - return new self( - action_attempt_id: $json->action_attempt_id ?? null, - action_type: $json->action_type ?? null, - error: isset($json->error) - ? ActionAttempt\Error::from_json($json->error) - : null, - result: isset($json->result) - ? ActionAttempt\Result::from_json($json->result) - : null, - status: $json->status ?? null, - ); + $discriminant = is_string($json->action_type ?? null) + ? \Seam\Resources\ActionAttempt\ActionType::tryFrom( + $json->action_type, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\ActionAttempt\ActionType::LOCK_DOOR + => \Seam\Resources\ActionAttempt\LockDoor::from_json($json), + \Seam\Resources\ActionAttempt\ActionType::UNLOCK_DOOR + => \Seam\Resources\ActionAttempt\UnlockDoor::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::SCAN_CREDENTIAL + => \Seam\Resources\ActionAttempt\ScanCredential::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::ENCODE_CREDENTIAL + => \Seam\Resources\ActionAttempt\EncodeCredential::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::SCAN_TO_ASSIGN_CREDENTIAL + => \Seam\Resources\ActionAttempt\ScanToAssignCredential::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::ASSIGN_CREDENTIAL + => \Seam\Resources\ActionAttempt\AssignCredential::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::RESET_SANDBOX_WORKSPACE + => \Seam\Resources\ActionAttempt\ResetSandboxWorkspace::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::SET_FAN_MODE + => \Seam\Resources\ActionAttempt\SetFanMode::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::SET_HVAC_MODE + => \Seam\Resources\ActionAttempt\SetHvacMode::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::ACTIVATE_CLIMATE_PRESET + => \Seam\Resources\ActionAttempt\ActivateClimatePreset::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::SIMULATE_KEYPAD_CODE_ENTRY + => \Seam\Resources\ActionAttempt\SimulateKeypadCodeEntry::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::SIMULATE_MANUAL_LOCK_VIA_KEYPAD + => \Seam\Resources\ActionAttempt\SimulateManualLockViaKeypad::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::PUSH_THERMOSTAT_PROGRAMS + => \Seam\Resources\ActionAttempt\PushThermostatPrograms::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::CONFIGURE_AUTO_LOCK + => \Seam\Resources\ActionAttempt\ConfigureAutoLock::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::SYNC_ACCESS_CODES + => \Seam\Resources\ActionAttempt\SyncAccessCodes::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::CREATE_ACCESS_CODE + => \Seam\Resources\ActionAttempt\CreateAccessCode::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::DELETE_ACCESS_CODE + => \Seam\Resources\ActionAttempt\DeleteAccessCode::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::UPDATE_ACCESS_CODE + => \Seam\Resources\ActionAttempt\UpdateAccessCode::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::CREATE_NOISE_THRESHOLD + => \Seam\Resources\ActionAttempt\CreateNoiseThreshold::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::DELETE_NOISE_THRESHOLD + => \Seam\Resources\ActionAttempt\DeleteNoiseThreshold::from_json( + $json, + ), + \Seam\Resources\ActionAttempt\ActionType::UPDATE_NOISE_THRESHOLD + => \Seam\Resources\ActionAttempt\UpdateNoiseThreshold::from_json( + $json, + ), + default => new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + status: $json->status ?? null, + ), + }; } public function __construct( @@ -29,12 +118,19 @@ public function __construct( * ID of the action attempt. */ public string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ public string|null $action_type, /** - * Error associated with the action. + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\Error|null $error, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null */ - public ActionAttempt\Error|null $error, - public ActionAttempt\Result|null $result, public string|null $status, ) {} } @@ -42,7 +138,7 @@ public function __construct( namespace Seam\Resources\ActionAttempt { /** - * Error associated with the action. + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. */ class Error { @@ -62,127 +158,2091 @@ public function __construct( * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * Type of the error. + */ public string|null $type, ) {} } - class Result + /** + * Locking a door is pending. + */ + final class LockDoor extends \Seam\Resources\ActionAttempt { - public static function from_json(mixed $json): Result|null + public static function from_json(mixed $json): LockDoor|null { if (!$json) { return null; } return new self( - access_code: $json->access_code ?? null, - access_method: $json->access_method ?? null, - access_method_id: $json->access_method_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_on_encoder: isset( - $json->acs_credential_on_encoder, - ) - ? Result\AcsCredentialOnEncoder::from_json( - $json->acs_credential_on_encoder, + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, ) : null, - acs_credential_on_seam: isset($json->acs_credential_on_seam) - ? Result\AcsCredentialOnSeam::from_json( - $json->acs_credential_on_seam, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\LockDoor\Result::from_json( + $json->result, ) : null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\LockDoor\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Unlocking a door is pending. + */ + final class UnlockDoor extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): UnlockDoor|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\UnlockDoor\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\UnlockDoor\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Reading credential data from the physical encoder is pending. + */ + final class ScanCredential extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): ScanCredential|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\ScanCredential\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * 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. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\ScanCredential\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Encoding credential data from the physical encoder onto a card is pending. + */ + final class EncodeCredential extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): EncodeCredential|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\EncodeCredential\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\EncodeCredential\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Scanning a physical card and assigning the credential is pending. + */ + final class ScanToAssignCredential extends \Seam\Resources\ActionAttempt + { + public static function from_json( + mixed $json, + ): ScanToAssignCredential|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of a scan to assign attempt. If the attempt was successful, includes the credential data that was scanned and assigned. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Assigning a credential to an access method is pending. + */ + final class AssignCredential extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): AssignCredential|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\AssignCredential\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of assigning a credential. If successful, includes the updated access method with the assigned credential. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\AssignCredential\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Resetting a sandbox workspace is pending. + */ + final class ResetSandboxWorkspace extends \Seam\Resources\ActionAttempt + { + public static function from_json( + mixed $json, + ): ResetSandboxWorkspace|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Setting the fan mode is pending. + */ + final class SetFanMode extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): SetFanMode|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Setting the HVAC mode is pending. + */ + final class SetHvacMode extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): SetHvacMode|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Activating a climate preset is pending. + */ + final class ActivateClimatePreset extends \Seam\Resources\ActionAttempt + { + public static function from_json( + mixed $json, + ): ActivateClimatePreset|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Simulating a keypad code entry is pending. + */ + final class SimulateKeypadCodeEntry extends \Seam\Resources\ActionAttempt + { + public static function from_json( + mixed $json, + ): SimulateKeypadCodeEntry|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Simulating a manual lock action using a keypad is pending. + */ + final class SimulateManualLockViaKeypad extends + \Seam\Resources\ActionAttempt + { + public static function from_json( + mixed $json, + ): SimulateManualLockViaKeypad|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Pushing thermostat weekly programs is pending. + */ + final class PushThermostatPrograms extends \Seam\Resources\ActionAttempt + { + public static function from_json( + mixed $json, + ): PushThermostatPrograms|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + /** + * Configuring the auto-lock is pending. + */ + final class ConfigureAutoLock extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): ConfigureAutoLock|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + final class SyncAccessCodes extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): SyncAccessCodes|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + final class CreateAccessCode extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): CreateAccessCode|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\CreateAccessCode\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\CreateAccessCode\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + final class DeleteAccessCode extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): DeleteAccessCode|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + final class UpdateAccessCode extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): UpdateAccessCode|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\UpdateAccessCode\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\UpdateAccessCode\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + final class CreateNoiseThreshold extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): CreateNoiseThreshold|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\CreateNoiseThreshold\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\CreateNoiseThreshold\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + final class DeleteNoiseThreshold extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): DeleteNoiseThreshold|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: $json->result ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public mixed $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + final class UpdateNoiseThreshold extends \Seam\Resources\ActionAttempt + { + public static function from_json(mixed $json): UpdateNoiseThreshold|null + { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? \Seam\Resources\ActionAttempt\Error::from_json( + $json->error, + ) + : null, + result: isset($json->result) + ? \Seam\Resources\ActionAttempt\UpdateNoiseThreshold\Result::from_json( + $json->result, + ) + : null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * ID of the action attempt. + */ + string|null $action_attempt_id, + /** + * Action attempt to track the status of locking a door. + * + * @var value-of<\Seam\Resources\ActionAttempt\ActionType>|string|null + */ + string|null $action_type, + /** + * Error associated with the action. Null while the action attempt is pending or when this value does not apply. + */ + \Seam\Resources\ActionAttempt\Error|null $error, + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + public \Seam\Resources\ActionAttempt\UpdateNoiseThreshold\Result|null $result, + /** + * @var value-of<\Seam\Resources\ActionAttempt\Status>|string|null + */ + string|null $status, + ) { + parent::__construct( + action_attempt_id: $action_attempt_id, + action_type: $action_type, + error: $error, + status: $status, + ); + } + } + + enum ActionType: string + { + case LOCK_DOOR = "LOCK_DOOR"; + case UNLOCK_DOOR = "UNLOCK_DOOR"; + case SCAN_CREDENTIAL = "SCAN_CREDENTIAL"; + case ENCODE_CREDENTIAL = "ENCODE_CREDENTIAL"; + case SCAN_TO_ASSIGN_CREDENTIAL = "SCAN_TO_ASSIGN_CREDENTIAL"; + case ASSIGN_CREDENTIAL = "ASSIGN_CREDENTIAL"; + case RESET_SANDBOX_WORKSPACE = "RESET_SANDBOX_WORKSPACE"; + case SET_FAN_MODE = "SET_FAN_MODE"; + case SET_HVAC_MODE = "SET_HVAC_MODE"; + case ACTIVATE_CLIMATE_PRESET = "ACTIVATE_CLIMATE_PRESET"; + case SIMULATE_KEYPAD_CODE_ENTRY = "SIMULATE_KEYPAD_CODE_ENTRY"; + case SIMULATE_MANUAL_LOCK_VIA_KEYPAD = "SIMULATE_MANUAL_LOCK_VIA_KEYPAD"; + case PUSH_THERMOSTAT_PROGRAMS = "PUSH_THERMOSTAT_PROGRAMS"; + case CONFIGURE_AUTO_LOCK = "CONFIGURE_AUTO_LOCK"; + case SYNC_ACCESS_CODES = "SYNC_ACCESS_CODES"; + case CREATE_ACCESS_CODE = "CREATE_ACCESS_CODE"; + case DELETE_ACCESS_CODE = "DELETE_ACCESS_CODE"; + case UPDATE_ACCESS_CODE = "UPDATE_ACCESS_CODE"; + case CREATE_NOISE_THRESHOLD = "CREATE_NOISE_THRESHOLD"; + case DELETE_NOISE_THRESHOLD = "DELETE_NOISE_THRESHOLD"; + case UPDATE_NOISE_THRESHOLD = "UPDATE_NOISE_THRESHOLD"; + } + + enum Status: string + { + case SUCCESS = "success"; + case PENDING = "pending"; + case ERROR = "error"; + } +} + +namespace Seam\Resources\ActionAttempt\LockDoor { + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self( + was_confirmed_by_device: $json->was_confirmed_by_device ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the device confirmed that the lock action occurred. + */ + public bool|null $was_confirmed_by_device = null, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\UnlockDoor { + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self( + was_confirmed_by_device: $json->was_confirmed_by_device ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the device confirmed that the unlock action occurred. + */ + public bool|null $was_confirmed_by_device = null, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\ScanCredential { + /** + * 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. Null while the action attempt is pending or when this value does not apply. + */ + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self( + acs_credential_on_encoder: isset( + $json->acs_credential_on_encoder, + ) + ? \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnEncoder::from_json( + $json->acs_credential_on_encoder, + ) + : null, + acs_credential_on_seam: isset($json->acs_credential_on_seam) + ? \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam::from_json( + $json->acs_credential_on_seam, + ) + : null, + warnings: array_map( + fn( + $w, + ) => \Seam\Resources\ActionAttempt\ScanCredential\Result\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), + ); + } + + public function __construct( + /** + * Snapshot of credential data read from the physical encoder. + */ + public \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnEncoder|null $acs_credential_on_encoder, + /** + * Corresponding credential data as stored on Seam and the access system. + */ + public \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam|null $acs_credential_on_seam, + /** + * 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. + * + * @var list<\Seam\Resources\ActionAttempt\ScanCredential\Result\Warnings> + */ + public array $warnings, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\ScanCredential\Result { + /** + * Snapshot of credential data read from the physical encoder. + */ + class AcsCredentialOnEncoder + { + public static function from_json( + mixed $json, + ): AcsCredentialOnEncoder|null { + if (!$json) { + return null; + } + return new self( + card_number: $json->card_number ?? null, + created_at: $json->created_at ?? null, + ends_at: $json->ends_at ?? null, + is_issued: $json->is_issued ?? null, + starts_at: $json->starts_at ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnEncoder\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + ); + } + + public function __construct( + /** + * A number or string that physically identifies the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) will stop being usable. + */ + public string|null $ends_at, + /** + * Indicates whether the credential has been issued (encoded onto a card). + */ + public bool|null $is_issued, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) becomes usable. + */ + public string|null $starts_at, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnEncoder\VisionlineMetadata|null $visionline_metadata = null, + ) {} + } + + /** + * Corresponding credential data as stored on Seam and the access system. + */ + class AcsCredentialOnSeam + { + public static function from_json(mixed $json): AcsCredentialOnSeam|null + { + if (!$json) { + return null; + } + return new self( + access_method: $json->access_method ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn( + $e, + ) => \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\Errors::from_json( + $e, + ), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + warnings: array_map( + fn( + $w, + ) => \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( + $json->assa_abloy_vostio_metadata, + ) + ? \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + card_number: $json->card_number ?? null, + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + is_issued: $json->is_issued ?? null, + is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? + null, + is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? + null, + is_one_time_use: $json->is_one_time_use ?? null, + issued_at: $json->issued_at ?? null, + latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? + null, + parent_acs_credential_id: $json->parent_acs_credential_id ?? + null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + ); + } + + public function __construct( + /** + * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\AccessMethod>|string|null + */ + public string|null $access_method, + /** + * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_credential_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_system_id, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $display_name, + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list<\Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\Errors> + */ + public array $errors, + public bool|null $is_managed, + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list<\Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\Warnings> + */ + public array $warnings, + /** + * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $workspace_id, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id = null, + /** + * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $acs_user_id = null, + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\AkilesMetadata|null $akiles_metadata = null, + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, + /** + * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number = null, + /** + * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $code = null, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + */ + public string|null $ends_at = null, + /** + * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\ExternalType>|string|null + */ + public string|null $external_type = null, + /** + * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $external_type_display_name = null, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. + */ + public bool|null $is_issued = null, + /** + * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. + */ + public bool|null $is_latest_desired_state_synced_with_provider = null, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). + */ + public bool|null $is_multi_phone_sync_credential = null, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use = null, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. + */ + public string|null $issued_at = null, + /** + * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. + */ + public string|null $latest_desired_state_synced_with_provider_at = null, + /** + * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $parent_acs_credential_id = null, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at = null, + /** + * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $user_identity_id = null, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public \Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata = null, + ) {} + } + + /** + * 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. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + warning_code: $json->warning_code ?? null, + warning_message: $json->warning_message ?? null, + ); + } + + public function __construct( + /** + * Indicates a warning related to scanning a credential. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanCredential\Result\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $warning_message, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnEncoder { + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + cancelled: $json->cancelled ?? null, + card_format: $json->card_format ?? null, + card_holder: $json->card_holder ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + discarded: $json->discarded ?? null, + expired: $json->expired ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + number_of_issued_cards: $json->number_of_issued_cards ?? null, + overridden: $json->overridden ?? null, + overwritten: $json->overwritten ?? null, + pending_auto_update: $json->pending_auto_update ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is cancelled. + */ + public bool|null $cancelled = null, + /** + * Format of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnEncoder\VisionlineMetadata\CardFormat>|string|null + */ + public string|null $card_format = null, + /** + * Holder of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_holder = null, + /** + * Card ID for the Visionline card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_id = null, + /** + * IDs of the common [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list|null + */ + public array|null $common_acs_entrance_ids = null, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is discarded. + */ + public bool|null $discarded = null, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is expired. + */ + public bool|null $expired = null, + /** + * IDs of the guest [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list|null + */ + public array|null $guest_acs_entrance_ids = null, + /** + * Number of issued cards associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public float|null $number_of_issued_cards = null, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overridden. + */ + public bool|null $overridden = null, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overwritten. + */ + public bool|null $overwritten = null, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is pending auto-update. + */ + public bool|null $pending_auto_update = null, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnEncoder\VisionlineMetadata { + enum CardFormat: string + { + case TL_CODE = "TLCode"; + case RFID48 = "rfid48"; + } +} + +namespace Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam { + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AkilesMetadata + { + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self(member_pin_id: $json->member_pin_id ?? null); + } + + public function __construct( + /** + * ID of the Akiles member PIN. + */ + public string|null $member_pin_id = null, + ) {} + } + + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AssaAbloyVostioMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + door_names: $json->door_names ?? null, + endpoint_id: $json->endpoint_id ?? null, + key_id: $json->key_id ?? null, + key_issuing_request_id: $json->key_issuing_request_id ?? null, + override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? + null, + ); + } + + public function __construct( + /** + * 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. + */ + public bool|null $auto_join = null, + /** + * Names of the doors to which to grant access in the Vostio access system. + * + * @var list|null + */ + public array|null $door_names = null, + /** + * Endpoint ID in the Vostio access system. + */ + public string|null $endpoint_id = null, + /** + * Key ID in the Vostio access system. + */ + public string|null $key_id = null, + /** + * Key issuing request ID in the Vostio access system. + */ + public string|null $key_issuing_request_id = null, + /** + * IDs of the guest entrances to override in the Vostio access system. + * + * @var list|null + */ + public array|null $override_guest_acs_entrance_ids = null, + ) {} + } + + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + public string|null $error_code, + public string|null $message, + ) {} + } + + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + card_function_type: $json->card_function_type ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + credential_id: $json->credential_id ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + is_valid: $json->is_valid ?? null, + joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? + null, + ); + } + + public function __construct( + /** + * 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. + */ + public bool|null $auto_join = null, + /** + * Card function type in the Visionline access system. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\VisionlineMetadata\CardFunctionType>|string|null + */ + public string|null $card_function_type = null, + /** + * ID of the card in the Visionline access system. + */ + public string|null $card_id = null, + /** + * Common entrance IDs in the Visionline access system. + * + * @var list|null + */ + public array|null $common_acs_entrance_ids = null, + /** + * ID of the credential in the Visionline access system. + */ + public string|null $credential_id = null, + /** + * Guest entrance IDs in the Visionline access system. + * + * @var list|null + */ + public array|null $guest_acs_entrance_ids = null, + /** + * Indicates whether the credential is valid. + */ + public bool|null $is_valid = null, + /** + * IDs of the credentials to which you want to join. + * + * @var list|null + */ + public array|null $joiner_acs_credential_ids = null, + ) {} + } + + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + /** + * The PIN code that was assigned instead. + */ + public string|null $new_code = null, + /** + * The originally requested PIN code that could not be used. + */ + public string|null $original_code = null, + ) {} + } + + enum AccessMethod: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } + + enum ExternalType: string + { + case PTI_CARD = "pti_card"; + case BRIVO_CREDENTIAL = "brivo_credential"; + case HID_CREDENTIAL = "hid_credential"; + case VISIONLINE_CARD = "visionline_card"; + case SALTO_KS_CREDENTIAL = "salto_ks_credential"; + case ASSA_ABLOY_VOSTIO_KEY = "assa_abloy_vostio_key"; + case SALTO_SPACE_KEY = "salto_space_key"; + case LATCH_ACCESS = "latch_access"; + case DORMAKABA_AMBIANCE_CREDENTIAL = "dormakaba_ambiance_credential"; + case HOTEK_CARD = "hotek_card"; + case SALTO_KS_TAG = "salto_ks_tag"; + case AVIGILON_ALTA_CREDENTIAL = "avigilon_alta_credential"; + case KISI_CREDENTIAL = "kisi_credential"; + case AKILES_CREDENTIAL = "akiles_credential"; + } +} + +namespace Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\VisionlineMetadata { + enum CardFunctionType: string + { + case GUEST = "guest"; + case STAFF = "staff"; + } +} + +namespace Seam\Resources\ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam\Warnings { + enum WarningCode: string + { + case WAITING_TO_BE_ISSUED = "waiting_to_be_issued"; + case SCHEDULE_EXTERNALLY_MODIFIED = "schedule_externally_modified"; + case SCHEDULE_MODIFIED = "schedule_modified"; + case BEING_DELETED = "being_deleted"; + case UNKNOWN_ISSUE_WITH_ACS_CREDENTIAL = "unknown_issue_with_acs_credential"; + case NEEDS_TO_BE_REISSUED = "needs_to_be_reissued"; + case REQUESTED_CODE_UNAVAILABLE = "requested_code_unavailable"; + } +} + +namespace Seam\Resources\ActionAttempt\ScanCredential\Result\Warnings { + enum WarningCode: string + { + case ACS_CREDENTIAL_ON_ENCODER_OUT_OF_SYNC = "acs_credential_on_encoder_out_of_sync"; + case ACS_CREDENTIAL_ON_SEAM_NOT_FOUND = "acs_credential_on_seam_not_found"; + } +} + +namespace Seam\Resources\ActionAttempt\EncodeCredential { + /** + * Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. Null while the action attempt is pending or when this value does not apply. + */ + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self( + access_method: $json->access_method ?? null, + acs_credential_id: $json->acs_credential_id ?? null, acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn( + $e, + ) => \Seam\Resources\ActionAttempt\EncodeCredential\Result\Errors::from_json( + $e, + ), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + warnings: array_map( + fn( + $w, + ) => \Seam\Resources\ActionAttempt\EncodeCredential\Result\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, acs_user_id: $json->acs_user_id ?? null, akiles_metadata: isset($json->akiles_metadata) - ? Result\AkilesMetadata::from_json($json->akiles_metadata) + ? \Seam\Resources\ActionAttempt\EncodeCredential\Result\AkilesMetadata::from_json( + $json->akiles_metadata, + ) : null, assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - ? Result\AssaAbloyVostioMetadata::from_json( + ? \Seam\Resources\ActionAttempt\EncodeCredential\Result\AssaAbloyVostioMetadata::from_json( $json->assa_abloy_vostio_metadata, ) : null, card_number: $json->card_number ?? null, - client_session_token: $json->client_session_token ?? null, code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? - null, - display_name: $json->display_name ?? null, ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => Result\Errors::from_json($e), - $json->errors ?? [], - ), external_type: $json->external_type ?? null, external_type_display_name: $json->external_type_display_name ?? null, - instant_key_url: $json->instant_key_url ?? null, - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, is_issued: $json->is_issued ?? null, is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? null, - is_managed: $json->is_managed ?? null, is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? null, is_one_time_use: $json->is_one_time_use ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, issued_at: $json->issued_at ?? null, latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? null, - mode: $json->mode ?? null, - noise_threshold: $json->noise_threshold ?? null, parent_acs_credential_id: $json->parent_acs_credential_id ?? null, - pending_mutations: array_map( - fn($p) => Result\PendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), starts_at: $json->starts_at ?? null, user_identity_id: $json->user_identity_id ?? null, visionline_metadata: isset($json->visionline_metadata) - ? Result\VisionlineMetadata::from_json( + ? \Seam\Resources\ActionAttempt\EncodeCredential\Result\VisionlineMetadata::from_json( $json->visionline_metadata, ) : null, - warnings: array_map( - fn($w) => Result\Warnings::from_json($w), - $json->warnings ?? [], - ), - was_confirmed_by_device: $json->was_confirmed_by_device ?? null, - workspace_id: $json->workspace_id ?? null, ); } public function __construct( - /** - * @var array|\stdClass|null - */ - public array|\stdClass|null $access_code, /** * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\ActionAttempt\EncodeCredential\Result\AccessMethod>|string|null */ public string|null $access_method, - /** - * ID of the access method. - */ - public string|null $access_method_id, /** * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $acs_credential_id, - /** - * Snapshot of credential data read from the physical encoder. - */ - public Result\AcsCredentialOnEncoder|null $acs_credential_on_encoder, - /** - * Corresponding credential data as stored on Seam and the access system. - */ - public Result\AcsCredentialOnSeam|null $acs_credential_on_seam, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -191,26 +2251,30 @@ public function __construct( * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ public string|null $connected_account_id, - public string|null $created_at, - public string|null $display_name, - public array $errors, /** - * Indicates whether Seam manages the credential. + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. */ - public bool|null $is_managed, + public string|null $created_at, /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. */ - public string|null $mode, + public string|null $display_name, /** - * @var array|\stdClass|null + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list<\Seam\Resources\ActionAttempt\EncodeCredential\Result\Errors> */ - public array|\stdClass|null $noise_threshold, + public array $errors, + public bool|null $is_managed, /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list<\Seam\Resources\ActionAttempt\EncodeCredential\Result\Warnings> */ - public array $pending_mutations, public array $warnings, + /** + * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ public string|null $workspace_id, /** * ID of the credential pool to which the credential belongs. @@ -223,30 +2287,27 @@ public function __construct( /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public Result\AkilesMetadata|null $akiles_metadata = null, + public \Seam\Resources\ActionAttempt\EncodeCredential\Result\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, + public \Seam\Resources\ActionAttempt\EncodeCredential\Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ public string|null $card_number = null, /** - * Token of the client session associated with the access method. + * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $client_session_token = null, public string|null $code = null, - /** - * ID of the customization profile associated with the access method. - */ - public string|null $customization_profile_id = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ public string|null $ends_at = null, /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + * + * @var value-of<\Seam\Resources\ActionAttempt\EncodeCredential\Result\ExternalType>|string|null */ public string|null $external_type = null, /** @@ -254,17 +2315,8 @@ public function __construct( */ public string|null $external_type_display_name = null, /** - * URL of the Instant Key for mobile key access methods. - */ - public string|null $instant_key_url = null, - /** - * 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. - */ - public bool|null $is_assignment_required = null, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. */ - public bool|null $is_encoding_required = null, public bool|null $is_issued = null, /** * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. @@ -279,99 +2331,303 @@ public function __construct( */ public bool|null $is_one_time_use = null, /** - * 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. - */ - public bool|null $is_ready_for_assignment = null, - /** - * 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. + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. */ - public bool|null $is_ready_for_encoding = null, public string|null $issued_at = null, /** * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. */ public string|null $latest_desired_state_synced_with_provider_at = null, /** - * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $parent_acs_credential_id = null, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at = null, + /** + * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $user_identity_id = null, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public \Seam\Resources\ActionAttempt\EncodeCredential\Result\VisionlineMetadata|null $visionline_metadata = null, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\EncodeCredential\Result { + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AkilesMetadata + { + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self(member_pin_id: $json->member_pin_id ?? null); + } + + public function __construct( + /** + * ID of the Akiles member PIN. + */ + public string|null $member_pin_id = null, + ) {} + } + + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AssaAbloyVostioMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + door_names: $json->door_names ?? null, + endpoint_id: $json->endpoint_id ?? null, + key_id: $json->key_id ?? null, + key_issuing_request_id: $json->key_issuing_request_id ?? null, + override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? + null, + ); + } + + public function __construct( + /** + * 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. + */ + public bool|null $auto_join = null, + /** + * Names of the doors to which to grant access in the Vostio access system. + * + * @var list|null + */ + public array|null $door_names = null, + /** + * Endpoint ID in the Vostio access system. + */ + public string|null $endpoint_id = null, + /** + * Key ID in the Vostio access system. + */ + public string|null $key_id = null, + /** + * Key issuing request ID in the Vostio access system. + */ + public string|null $key_issuing_request_id = null, + /** + * IDs of the guest entrances to override in the Vostio access system. + * + * @var list|null + */ + public array|null $override_guest_acs_entrance_ids = null, + ) {} + } + + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + public string|null $error_code, + public string|null $message, + ) {} + } + + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + card_function_type: $json->card_function_type ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + credential_id: $json->credential_id ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + is_valid: $json->is_valid ?? null, + joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? + null, + ); + } + + public function __construct( + /** + * 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. + */ + public bool|null $auto_join = null, + /** + * Card function type in the Visionline access system. + * + * @var value-of<\Seam\Resources\ActionAttempt\EncodeCredential\Result\VisionlineMetadata\CardFunctionType>|string|null + */ + public string|null $card_function_type = null, + /** + * ID of the card in the Visionline access system. + */ + public string|null $card_id = null, + /** + * Common entrance IDs in the Visionline access system. + * + * @var list|null + */ + public array|null $common_acs_entrance_ids = null, + /** + * ID of the credential in the Visionline access system. */ - public string|null $parent_acs_credential_id = null, + public string|null $credential_id = null, /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + * Guest entrance IDs in the Visionline access system. + * + * @var list|null */ - public string|null $starts_at = null, + public array|null $guest_acs_entrance_ids = null, /** - * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + * Indicates whether the credential is valid. */ - public string|null $user_identity_id = null, + public bool|null $is_valid = null, /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * IDs of the credentials to which you want to join. + * + * @var list|null */ - public Result\VisionlineMetadata|null $visionline_metadata = null, - public bool|null $was_confirmed_by_device = null, + public array|null $joiner_acs_credential_ids = null, ) {} } -} -namespace Seam\Resources\ActionAttempt\Result { /** - * Snapshot of credential data read from the physical encoder. + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - class AcsCredentialOnEncoder + class Warnings { - public static function from_json( - mixed $json, - ): AcsCredentialOnEncoder|null { + public static function from_json(mixed $json): Warnings|null + { if (!$json) { return null; } return new self( - card_number: $json->card_number ?? null, created_at: $json->created_at ?? null, - ends_at: $json->ends_at ?? null, - is_issued: $json->is_issued ?? null, - starts_at: $json->starts_at ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? AcsCredentialOnEncoder\VisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, ); } public function __construct( /** - * A number or string that physically identifies the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + * Date and time at which Seam created the warning. */ public string|null $created_at, /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) will stop being usable. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $ends_at, + public string|null $message, /** - * Indicates whether the credential has been issued (encoded onto a card). + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ActionAttempt\EncodeCredential\Result\Warnings\WarningCode>|string|null */ - public bool|null $is_issued, + public string|null $warning_code, /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) becomes usable. + * The PIN code that was assigned instead. */ - public string|null $starts_at, + public string|null $new_code = null, /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * The originally requested PIN code that could not be used. */ - public AcsCredentialOnEncoder\VisionlineMetadata|null $visionline_metadata = null, + public string|null $original_code = null, ) {} } + enum AccessMethod: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } + + enum ExternalType: string + { + case PTI_CARD = "pti_card"; + case BRIVO_CREDENTIAL = "brivo_credential"; + case HID_CREDENTIAL = "hid_credential"; + case VISIONLINE_CARD = "visionline_card"; + case SALTO_KS_CREDENTIAL = "salto_ks_credential"; + case ASSA_ABLOY_VOSTIO_KEY = "assa_abloy_vostio_key"; + case SALTO_SPACE_KEY = "salto_space_key"; + case LATCH_ACCESS = "latch_access"; + case DORMAKABA_AMBIANCE_CREDENTIAL = "dormakaba_ambiance_credential"; + case HOTEK_CARD = "hotek_card"; + case SALTO_KS_TAG = "salto_ks_tag"; + case AVIGILON_ALTA_CREDENTIAL = "avigilon_alta_credential"; + case KISI_CREDENTIAL = "kisi_credential"; + case AKILES_CREDENTIAL = "akiles_credential"; + } +} + +namespace Seam\Resources\ActionAttempt\EncodeCredential\Result\VisionlineMetadata { + enum CardFunctionType: string + { + case GUEST = "guest"; + case STAFF = "staff"; + } +} + +namespace Seam\Resources\ActionAttempt\EncodeCredential\Result\Warnings { + enum WarningCode: string + { + case WAITING_TO_BE_ISSUED = "waiting_to_be_issued"; + case SCHEDULE_EXTERNALLY_MODIFIED = "schedule_externally_modified"; + case SCHEDULE_MODIFIED = "schedule_modified"; + case BEING_DELETED = "being_deleted"; + case UNKNOWN_ISSUE_WITH_ACS_CREDENTIAL = "unknown_issue_with_acs_credential"; + case NEEDS_TO_BE_REISSUED = "needs_to_be_reissued"; + case REQUESTED_CODE_UNAVAILABLE = "requested_code_unavailable"; + } +} + +namespace Seam\Resources\ActionAttempt\ScanToAssignCredential { /** - * Corresponding credential data as stored on Seam and the access system. + * Result of a scan to assign attempt. If the attempt was successful, includes the credential data that was scanned and assigned. Null while the action attempt is pending or when this value does not apply. */ - class AcsCredentialOnSeam + class Result { - public static function from_json(mixed $json): AcsCredentialOnSeam|null + public static function from_json(mixed $json): Result|null { if (!$json) { return null; @@ -379,38 +2635,51 @@ public static function from_json(mixed $json): AcsCredentialOnSeam|null return new self( access_method: $json->access_method ?? null, acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn( + $e, + ) => \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\Errors::from_json( + $e, + ), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + warnings: array_map( + fn( + $w, + ) => \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, acs_user_id: $json->acs_user_id ?? null, akiles_metadata: isset($json->akiles_metadata) - ? AcsCredentialOnSeam\AkilesMetadata::from_json( + ? \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\AkilesMetadata::from_json( $json->akiles_metadata, ) : null, assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - ? AcsCredentialOnSeam\AssaAbloyVostioMetadata::from_json( + ? \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\AssaAbloyVostioMetadata::from_json( $json->assa_abloy_vostio_metadata, ) : null, card_number: $json->card_number ?? null, code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => AcsCredentialOnSeam\Errors::from_json($e), - $json->errors ?? [], - ), external_type: $json->external_type ?? null, external_type_display_name: $json->external_type_display_name ?? null, is_issued: $json->is_issued ?? null, is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? null, - is_managed: $json->is_managed ?? null, is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? null, is_one_time_use: $json->is_one_time_use ?? null, @@ -422,21 +2691,18 @@ public static function from_json(mixed $json): AcsCredentialOnSeam|null starts_at: $json->starts_at ?? null, user_identity_id: $json->user_identity_id ?? null, visionline_metadata: isset($json->visionline_metadata) - ? AcsCredentialOnSeam\VisionlineMetadata::from_json( + ? \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\VisionlineMetadata::from_json( $json->visionline_metadata, ) : null, - warnings: array_map( - fn($w) => AcsCredentialOnSeam\Warnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, ); } public function __construct( /** * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\AccessMethod>|string|null */ public string|null $access_method, /** @@ -461,11 +2727,18 @@ public function __construct( public string|null $display_name, /** * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list<\Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\Errors> */ public array $errors, - public bool|null $is_managed, + /** + * Indicates whether Seam manages the credential. + */ + public true|null $is_managed, /** * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * + * @var list<\Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\Warnings> */ public array $warnings, /** @@ -483,11 +2756,11 @@ public function __construct( /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\AkilesMetadata|null $akiles_metadata = null, + public \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, + public \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -502,6 +2775,8 @@ public function __construct( public string|null $ends_at = null, /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\ExternalType>|string|null */ public string|null $external_type = null, /** @@ -547,10 +2822,12 @@ public function __construct( /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata = null, + public \Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\VisionlineMetadata|null $visionline_metadata = null, ) {} } +} +namespace Seam\Resources\ActionAttempt\ScanToAssignCredential\Result { /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -601,6 +2878,8 @@ public function __construct( public bool|null $auto_join = null, /** * Names of the doors to which to grant access in the Vostio access system. + * + * @var list|null */ public array|null $door_names = null, /** @@ -617,11 +2896,16 @@ public function __construct( public string|null $key_issuing_request_id = null, /** * IDs of the guest entrances to override in the Vostio access system. + * + * @var list|null */ public array|null $override_guest_acs_entrance_ids = null, ) {} } + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ class Errors { public static function from_json(mixed $json): Errors|null @@ -641,61 +2925,8 @@ public function __construct( * Date and time at which Seam created the error. */ public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} - } - - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - class PendingMutations - { - public static function from_json(mixed $json): PendingMutations|null - { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? PendingMutations\From::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? PendingMutations\To::from_json($json->to) - : null, - ); - } - - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous access time configuration. - */ - public PendingMutations\From|null $from, - /** - * Detailed description of the mutation. - */ public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of updating the access times for this access method. - */ - public string|null $mutation_code, - /** - * New access time configuration. - */ - public PendingMutations\To|null $to, ) {} } @@ -729,6 +2960,8 @@ public function __construct( public bool|null $auto_join = null, /** * Card function type in the Visionline access system. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\VisionlineMetadata\CardFunctionType>|string|null */ public string|null $card_function_type = null, /** @@ -737,6 +2970,8 @@ public function __construct( public string|null $card_id = null, /** * Common entrance IDs in the Visionline access system. + * + * @var list|null */ public array|null $common_acs_entrance_ids = null, /** @@ -745,6 +2980,8 @@ public function __construct( public string|null $credential_id = null, /** * Guest entrance IDs in the Visionline access system. + * + * @var list|null */ public array|null $guest_acs_entrance_ids = null, /** @@ -753,11 +2990,16 @@ public function __construct( public bool|null $is_valid = null, /** * IDs of the credentials to which you want to join. + * + * @var list|null */ public array|null $joiner_acs_credential_ids = null, ) {} } + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ class Warnings { public static function from_json(mixed $json): Warnings|null @@ -768,12 +3010,9 @@ public static function from_json(mixed $json): Warnings|null return new self( created_at: $json->created_at ?? null, message: $json->message ?? null, + warning_code: $json->warning_code ?? null, new_code: $json->new_code ?? null, - original_access_method_id: $json->original_access_method_id ?? - null, original_code: $json->original_code ?? null, - warning_code: $json->warning_code ?? null, - warning_message: $json->warning_message ?? null, ); } @@ -786,181 +3025,214 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - public string|null $warning_code, /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\Warnings\WarningCode>|string|null */ - public string|null $warning_message, + public string|null $warning_code, /** * The PIN code that was assigned instead. */ public string|null $new_code = null, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id = null, /** * The originally requested PIN code that could not be used. */ public string|null $original_code = null, ) {} } + + enum AccessMethod: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } + + enum ExternalType: string + { + case PTI_CARD = "pti_card"; + case BRIVO_CREDENTIAL = "brivo_credential"; + case HID_CREDENTIAL = "hid_credential"; + case VISIONLINE_CARD = "visionline_card"; + case SALTO_KS_CREDENTIAL = "salto_ks_credential"; + case ASSA_ABLOY_VOSTIO_KEY = "assa_abloy_vostio_key"; + case SALTO_SPACE_KEY = "salto_space_key"; + case LATCH_ACCESS = "latch_access"; + case DORMAKABA_AMBIANCE_CREDENTIAL = "dormakaba_ambiance_credential"; + case HOTEK_CARD = "hotek_card"; + case SALTO_KS_TAG = "salto_ks_tag"; + case AVIGILON_ALTA_CREDENTIAL = "avigilon_alta_credential"; + case KISI_CREDENTIAL = "kisi_credential"; + case AKILES_CREDENTIAL = "akiles_credential"; + } +} + +namespace Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\VisionlineMetadata { + enum CardFunctionType: string + { + case GUEST = "guest"; + case STAFF = "staff"; + } +} + +namespace Seam\Resources\ActionAttempt\ScanToAssignCredential\Result\Warnings { + enum WarningCode: string + { + case WAITING_TO_BE_ISSUED = "waiting_to_be_issued"; + case SCHEDULE_EXTERNALLY_MODIFIED = "schedule_externally_modified"; + case SCHEDULE_MODIFIED = "schedule_modified"; + case BEING_DELETED = "being_deleted"; + case UNKNOWN_ISSUE_WITH_ACS_CREDENTIAL = "unknown_issue_with_acs_credential"; + case NEEDS_TO_BE_REISSUED = "needs_to_be_reissued"; + case REQUESTED_CODE_UNAVAILABLE = "requested_code_unavailable"; + } } -namespace Seam\Resources\ActionAttempt\Result\AcsCredentialOnEncoder { +namespace Seam\Resources\ActionAttempt\AssignCredential { /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Result of assigning a credential. If successful, includes the updated access method with the assigned credential. Null while the action attempt is pending or when this value does not apply. */ - class VisionlineMetadata + class Result { - public static function from_json(mixed $json): VisionlineMetadata|null + public static function from_json(mixed $json): Result|null { if (!$json) { return null; } return new self( - cancelled: $json->cancelled ?? null, - card_format: $json->card_format ?? null, - card_holder: $json->card_holder ?? null, - card_id: $json->card_id ?? null, - common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, - discarded: $json->discarded ?? null, - expired: $json->expired ?? null, - guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, - number_of_issued_cards: $json->number_of_issued_cards ?? null, - overridden: $json->overridden ?? null, - overwritten: $json->overwritten ?? null, - pending_auto_update: $json->pending_auto_update ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn( + $e, + ) => \Seam\Resources\ActionAttempt\AssignCredential\Result\Errors::from_json( + $e, + ), + $json->errors ?? [], + ), + is_issued: $json->is_issued ?? null, + issued_at: $json->issued_at ?? null, + mode: $json->mode ?? null, + pending_mutations: array_map( + fn( + $p, + ) => \Seam\Resources\ActionAttempt\AssignCredential\Result\PendingMutations::from_json( + $p, + ), + $json->pending_mutations ?? [], + ), + warnings: array_map( + fn( + $w, + ) => \Seam\Resources\ActionAttempt\AssignCredential\Result\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + client_session_token: $json->client_session_token ?? null, + code: $json->code ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + instant_key_url: $json->instant_key_url ?? null, + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, ); } public function __construct( /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is cancelled. - */ - public bool|null $cancelled = null, - /** - * Format of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * ID of the access method. */ - public string|null $card_format = null, + public string|null $access_method_id, /** - * Holder of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Date and time at which the access method was created. */ - public string|null $card_holder = null, + public string|null $created_at, /** - * Card ID for the Visionline card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Display name of the access method. */ - public string|null $card_id = null, + public string|null $display_name, /** - * IDs of the common [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * + * @var list<\Seam\Resources\ActionAttempt\AssignCredential\Result\Errors> */ - public array|null $common_acs_entrance_ids = null, + public array $errors, /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is discarded. + * Indicates whether the access method has been issued. */ - public bool|null $discarded = null, + public bool|null $is_issued, /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is expired. + * Date and time at which the access method was issued. */ - public bool|null $expired = null, + public string|null $issued_at, /** - * IDs of the guest [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\ActionAttempt\AssignCredential\Result\Mode>|string|null */ - public array|null $guest_acs_entrance_ids = null, + public string|null $mode, /** - * Number of issued cards associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + * + * @var list<\Seam\Resources\ActionAttempt\AssignCredential\Result\PendingMutations> */ - public float|null $number_of_issued_cards = null, + public array $pending_mutations, /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overridden. + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * + * @var list<\Seam\Resources\ActionAttempt\AssignCredential\Result\Warnings> */ - public bool|null $overridden = null, + public array $warnings, /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overwritten. + * ID of the Seam workspace associated with the access method. */ - public bool|null $overwritten = null, + public string|null $workspace_id, /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is pending auto-update. + * Token of the client session associated with the access method. */ - public bool|null $pending_auto_update = null, - ) {} - } -} - -namespace Seam\Resources\ActionAttempt\Result\AcsCredentialOnSeam { - /** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - class AkilesMetadata - { - public static function from_json(mixed $json): AkilesMetadata|null - { - if (!$json) { - return null; - } - return new self(member_pin_id: $json->member_pin_id ?? null); - } - - public function __construct( + public string|null $client_session_token = null, /** - * ID of the Akiles member PIN. + * The actual PIN code for code access methods. */ - public string|null $member_pin_id = null, - ) {} - } - - /** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - class AssaAbloyVostioMetadata - { - public static function from_json( - mixed $json, - ): AssaAbloyVostioMetadata|null { - if (!$json) { - return null; - } - return new self( - auto_join: $json->auto_join ?? null, - door_names: $json->door_names ?? null, - endpoint_id: $json->endpoint_id ?? null, - key_id: $json->key_id ?? null, - key_issuing_request_id: $json->key_issuing_request_id ?? null, - override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? - null, - ); - } - - public function __construct( + public string|null $code = null, /** - * 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. + * ID of the customization profile associated with the access method. */ - public bool|null $auto_join = null, + public string|null $customization_profile_id = null, /** - * Names of the doors to which to grant access in the Vostio access system. + * URL of the Instant Key for mobile key access methods. */ - public array|null $door_names = null, + public string|null $instant_key_url = null, /** - * Endpoint ID in the Vostio access system. + * 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. */ - public string|null $endpoint_id = null, + public bool|null $is_assignment_required = null, /** - * Key ID in the Vostio access system. + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. */ - public string|null $key_id = null, + public bool|null $is_encoding_required = null, /** - * Key issuing request ID in the Vostio access system. + * 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. */ - public string|null $key_issuing_request_id = null, + public bool|null $is_ready_for_assignment = null, /** - * IDs of the guest entrances to override in the Vostio access system. + * 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. */ - public array|null $override_guest_acs_entrance_ids = null, + public bool|null $is_ready_for_encoding = null, ) {} } +} +namespace Seam\Resources\ActionAttempt\AssignCredential\Result { /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). */ class Errors { @@ -981,72 +3253,74 @@ public function __construct( * Date and time at which Seam created the error. */ public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ActionAttempt\AssignCredential\Result\Errors\ErrorCode>|string|null + */ public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ public string|null $message, ) {} } /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. */ - class VisionlineMetadata + class PendingMutations { - public static function from_json(mixed $json): VisionlineMetadata|null + public static function from_json(mixed $json): PendingMutations|null { if (!$json) { return null; } return new self( - auto_join: $json->auto_join ?? null, - card_function_type: $json->card_function_type ?? null, - card_id: $json->card_id ?? null, - common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, - credential_id: $json->credential_id ?? null, - guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, - is_valid: $json->is_valid ?? null, - joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? - null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\ActionAttempt\AssignCredential\Result\PendingMutations\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\ActionAttempt\AssignCredential\Result\PendingMutations\To::from_json( + $json->to, + ) + : null, ); } public function __construct( /** - * 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. - */ - public bool|null $auto_join = null, - /** - * Card function type in the Visionline access system. - */ - public string|null $card_function_type = null, - /** - * ID of the card in the Visionline access system. - */ - public string|null $card_id = null, - /** - * Common entrance IDs in the Visionline access system. + * Date and time at which the mutation was created. */ - public array|null $common_acs_entrance_ids = null, + public string|null $created_at, /** - * ID of the credential in the Visionline access system. + * Previous access time configuration. */ - public string|null $credential_id = null, + public \Seam\Resources\ActionAttempt\AssignCredential\Result\PendingMutations\From|null $from, /** - * Guest entrance IDs in the Visionline access system. + * Detailed description of the mutation. */ - public array|null $guest_acs_entrance_ids = null, + public string|null $message, /** - * Indicates whether the credential is valid. + * Mutation code to indicate that Seam is in the process of updating the access times for this access method. + * + * @var value-of<\Seam\Resources\ActionAttempt\AssignCredential\Result\PendingMutations\MutationCode>|string|null */ - public bool|null $is_valid = null, + public string|null $mutation_code, /** - * IDs of the credentials to which you want to join. + * New access time configuration. */ - public array|null $joiner_acs_credential_ids = null, + public \Seam\Resources\ActionAttempt\AssignCredential\Result\PendingMutations\To|null $to, ) {} } /** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). */ class Warnings { @@ -1058,9 +3332,9 @@ public static function from_json(mixed $json): Warnings|null return new self( created_at: $json->created_at ?? null, message: $json->message ?? null, - new_code: $json->new_code ?? null, - original_code: $json->original_code ?? null, warning_code: $json->warning_code ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, ); } @@ -1075,21 +3349,34 @@ public function __construct( public string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ActionAttempt\AssignCredential\Result\Warnings\WarningCode>|string|null */ public string|null $warning_code, /** - * The PIN code that was assigned instead. - */ - public string|null $new_code = null, - /** - * The originally requested PIN code that could not be used. + * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $original_code = null, + public string|null $original_access_method_id = null, ) {} } + + enum Mode: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } +} + +namespace Seam\Resources\ActionAttempt\AssignCredential\Result\Errors { + enum ErrorCode: string + { + case FAILED_TO_ISSUE = "failed_to_issue"; + } } -namespace Seam\Resources\ActionAttempt\Result\PendingMutations { +namespace Seam\Resources\ActionAttempt\AssignCredential\Result\PendingMutations { /** * Previous access time configuration. */ @@ -1145,4 +3432,121 @@ public function __construct( public string|null $starts_at, ) {} } + + enum MutationCode: string + { + case PROVISIONING_ACCESS = "provisioning_access"; + case REVOKING_ACCESS = "revoking_access"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + } +} + +namespace Seam\Resources\ActionAttempt\AssignCredential\Result\Warnings { + enum WarningCode: string + { + case BEING_DELETED = "being_deleted"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + case PULLED_BACKUP_ACCESS_CODE = "pulled_backup_access_code"; + case DELAY_IN_ISSUING = "delay_in_issuing"; + } +} + +namespace Seam\Resources\ActionAttempt\CreateAccessCode { + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self(access_code: $json->access_code ?? null); + } + + public function __construct( + /** + * Created access code. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $access_code, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\UpdateAccessCode { + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self(access_code: $json->access_code ?? null); + } + + public function __construct( + /** + * Updated access code. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $access_code = null, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\CreateNoiseThreshold { + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self(noise_threshold: $json->noise_threshold ?? null); + } + + public function __construct( + /** + * Created noise threshold. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $noise_threshold, + ) {} + } +} + +namespace Seam\Resources\ActionAttempt\UpdateNoiseThreshold { + /** + * Result of the action. Null while the action attempt is pending or when this value does not apply. + */ + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self(noise_threshold: $json->noise_threshold ?? null); + } + + public function __construct( + /** + * Updated noise threshold. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $noise_threshold, + ) {} + } } diff --git a/src/Resources/ClientSession.php b/src/Resources/ClientSession.php index f481f7c6..d9664152 100644 --- a/src/Resources/ClientSession.php +++ b/src/Resources/ClientSession.php @@ -24,14 +24,14 @@ public static function from_json(mixed $json): ClientSession|null connect_webview_ids: $json->connect_webview_ids ?? null, connected_account_ids: $json->connected_account_ids ?? null, created_at: $json->created_at ?? null, - customer_key: $json->customer_key ?? null, device_count: $json->device_count ?? null, expires_at: $json->expires_at ?? null, token: $json->token ?? null, user_identifier_key: $json->user_identifier_key ?? null, - user_identity_id: $json->user_identity_id ?? null, user_identity_ids: $json->user_identity_ids ?? null, workspace_id: $json->workspace_id ?? null, + customer_key: $json->customer_key ?? null, + user_identity_id: $json->user_identity_id ?? null, ); } @@ -42,10 +42,14 @@ public function __construct( public string|null $client_session_id, /** * IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + * + * @var list|null */ public array|null $connect_webview_ids, /** * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + * + * @var list|null */ public array|null $connected_account_ids, /** @@ -71,6 +75,7 @@ public function __construct( /** * IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. * + * @var list|null * @deprecated Use `user_identity_id` instead. */ public array|null $user_identity_ids, diff --git a/src/Resources/ConnectWebview.php b/src/Resources/ConnectWebview.php index 494aadc3..82db3141 100644 --- a/src/Resources/ConnectWebview.php +++ b/src/Resources/ConnectWebview.php @@ -35,7 +35,6 @@ public static function from_json(mixed $json): ConnectWebview|null custom_redirect_failure_url: $json->custom_redirect_failure_url ?? null, custom_redirect_url: $json->custom_redirect_url ?? null, - customer_key: $json->customer_key ?? null, device_selection_mode: $json->device_selection_mode ?? null, login_successful: $json->login_successful ?? null, selected_provider: $json->selected_provider ?? null, @@ -44,16 +43,21 @@ public static function from_json(mixed $json): ConnectWebview|null wait_for_device_creation: $json->wait_for_device_creation ?? null, workspace_id: $json->workspace_id ?? null, + customer_key: $json->customer_key ?? null, ); } public function __construct( /** * High-level device capabilities that the Connect Webview can accept. When creating a Connect Webview, you can specify the types of devices that it can connect to Seam. If you do not set custom `accepted_capabilities`, Seam uses a default set of `accepted_capabilities` for each provider. For example, if you create a Connect Webview that accepts SmartThing devices, without specifying `accepted_capabilities`, Seam accepts only SmartThings locks. To connect SmartThings thermostats and locks to Seam, create a Connect Webview and include both `thermostat` and `lock` in the `accepted_capabilities`. + * + * @var list|null */ public array|null $accepted_capabilities, /** * List of accepted [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). + * + * @var list|null */ public array|null $accepted_providers, /** @@ -96,6 +100,8 @@ public function __construct( public string|null $custom_redirect_url, /** * Device selection mode of the Connect Webview. Supported values: `none`, `single`, `multiple`. + * + * @var value-of<\Seam\Resources\ConnectWebview\DeviceSelectionMode>|string|null */ public string|null $device_selection_mode, /** @@ -108,6 +114,8 @@ public function __construct( public string|null $selected_provider, /** * Status of the Connect Webview. `authorized` indicates that the user has successfully logged into their device or system account, thereby completing the Connect Webview. + * + * @var value-of<\Seam\Resources\ConnectWebview\Status>|string|null */ public string|null $status, /** @@ -129,3 +137,19 @@ public function __construct( ) {} } } + +namespace Seam\Resources\ConnectWebview { + enum DeviceSelectionMode: string + { + case NONE = "none"; + case SINGLE = "single"; + case MULTIPLE = "multiple"; + } + + enum Status: string + { + case PENDING = "pending"; + case FAILED = "failed"; + case AUTHORIZED = "authorized"; + } +} diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index 77d3076f..db0ceaf7 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -13,41 +13,49 @@ public static function from_json(mixed $json): ConnectedAccount|null } return new self( accepted_capabilities: $json->accepted_capabilities ?? null, - account_type: $json->account_type ?? null, account_type_display_name: $json->account_type_display_name ?? null, automatically_manage_new_devices: $json->automatically_manage_new_devices ?? null, connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, custom_metadata: $json->custom_metadata ?? null, - customer_key: $json->customer_key ?? null, - default_checkin_time: $json->default_checkin_time ?? null, - default_checkout_time: $json->default_checkout_time ?? null, display_name: $json->display_name ?? null, errors: array_map( - fn($e) => ConnectedAccount\Errors::from_json($e), + fn( + $e, + ) => \Seam\Resources\ConnectedAccount\Errors::from_json($e), $json->errors ?? [], ), + warnings: array_map( + fn( + $w, + ) => \Seam\Resources\ConnectedAccount\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), + account_type: $json->account_type ?? null, + created_at: $json->created_at ?? null, + customer_key: $json->customer_key ?? null, + default_checkin_time: $json->default_checkin_time ?? null, + default_checkout_time: $json->default_checkout_time ?? null, ical_feed_origin: $json->ical_feed_origin ?? null, ical_url: $json->ical_url ?? null, image_url: $json->image_url ?? null, time_zone: $json->time_zone ?? null, user_identifier: isset($json->user_identifier) - ? ConnectedAccount\UserIdentifier::from_json( + ? \Seam\Resources\ConnectedAccount\UserIdentifier::from_json( $json->user_identifier, ) : null, - warnings: array_map( - fn($w) => ConnectedAccount\Warnings::from_json($w), - $json->warnings ?? [], - ), ); } public function __construct( /** * List of capabilities that were accepted during the account connection process. + * + * @var list|null */ public array|null $accepted_capabilities, /** @@ -74,10 +82,14 @@ public function __construct( public string|null $display_name, /** * Errors associated with the connected account. + * + * @var list<\Seam\Resources\ConnectedAccount\Errors> */ public array $errors, /** * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\ConnectedAccount\Warnings> */ public array $warnings, /** @@ -121,14 +133,14 @@ public function __construct( * * @deprecated Use `display_name` instead. */ - public ConnectedAccount\UserIdentifier|null $user_identifier = null, + public \Seam\Resources\ConnectedAccount\UserIdentifier|null $user_identifier = null, ) {} } } namespace Seam\Resources\ConnectedAccount { /** - * Errors associated with the connected account. + * Errors associated with the connected account. Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -137,19 +149,38 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - message: $json->message ?? null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? Errors\SaltoKsMetadata::from_json( - $json->salto_ks_metadata, - ) - : null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\ConnectedAccount\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\ConnectedAccount\Errors\ErrorCode::ACCOUNT_DISCONNECTED + => \Seam\Resources\ConnectedAccount\Errors\AccountDisconnected::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Errors\ErrorCode::BRIDGE_DISCONNECTED + => \Seam\Resources\ConnectedAccount\Errors\BridgeDisconnected::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Errors\ErrorCode::SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED + => \Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Errors\ErrorCode::DORMAKABA_SITES_DISCONNECTED + => \Seam\Resources\ConnectedAccount\Errors\DormakabaSitesDisconnected::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + ), + }; } public function __construct( @@ -159,16 +190,14 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Errors\ErrorCode>|string|null */ public string|null $error_code, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - /** - * Salto KS metadata associated with the connected account that has an error. - */ - public Errors\SaltoKsMetadata|null $salto_ks_metadata, /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ @@ -226,49 +255,323 @@ public function __construct( } /** - * Warnings associated with the connected account. + * Warnings associated with the connected account. Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { public static function from_json(mixed $json): Warnings|null { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\ConnectedAccount\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\ConnectedAccount\Warnings\WarningCode::SCHEDULED_MAINTENANCE_WINDOW + => \Seam\Resources\ConnectedAccount\Warnings\ScheduledMaintenanceWindow::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Warnings\WarningCode::UNKNOWN_ISSUE_WITH_CONNECTED_ACCOUNT + => \Seam\Resources\ConnectedAccount\Warnings\UnknownIssueWithConnectedAccount::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Warnings\WarningCode::SALTO_KS_SUBSCRIPTION_LIMIT_ALMOST_REACHED + => \Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Warnings\WarningCode::ACCOUNT_REAUTHORIZATION_REQUESTED + => \Seam\Resources\ConnectedAccount\Warnings\AccountReauthorizationRequested::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\ConnectedAccount\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Warnings\WarningCode::PROVIDER_SERVICE_UNAVAILABLE + => \Seam\Resources\ConnectedAccount\Warnings\ProviderServiceUnavailable::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Warnings\WarningCode::SETUP_REQUIRED + => \Seam\Resources\ConnectedAccount\Warnings\SetupRequired::from_json( + $json, + ), + \Seam\Resources\ConnectedAccount\Warnings\WarningCode::DORMAKABA_SITES_UNAPPROVED + => \Seam\Resources\ConnectedAccount\Warnings\DormakabaSitesUnapproved::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\ConnectedAccount\Errors { + /** + * Indicates that the account is disconnected. + */ + final class AccountDisconnected extends + \Seam\Resources\ConnectedAccount\Errors + { + public static function from_json(mixed $json): AccountDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + bool|null $is_bridge_error = null, + /** + * Indicates whether the error is related specifically to the connected account. + */ + bool|null $is_connected_account_error = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + is_bridge_error: $is_bridge_error, + is_connected_account_error: $is_connected_account_error, + message: $message, + ); + } + } + + /** + * Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/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](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system-errors-seam_bridge_disconnected). + */ + final class BridgeDisconnected extends + \Seam\Resources\ConnectedAccount\Errors + { + public static function from_json(mixed $json): BridgeDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + bool|null $is_bridge_error = null, + /** + * Indicates whether the error is related specifically to the connected account. + */ + bool|null $is_connected_account_error = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + is_bridge_error: $is_bridge_error, + is_connected_account_error: $is_connected_account_error, + message: $message, + ); + } + } + + /** + * 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. + */ + final class SaltoKsSubscriptionLimitExceeded extends + \Seam\Resources\ConnectedAccount\Errors + { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitExceeded|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, message: $json->message ?? null, salto_ks_metadata: isset($json->salto_ks_metadata) - ? Warnings\SaltoKsMetadata::from_json( + ? \Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded\SaltoKsMetadata::from_json( $json->salto_ks_metadata, ) : null, - warning_code: $json->warning_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, ); } public function __construct( /** - * Date and time at which Seam created the warning. + * Date and time at which Seam created the error. */ - public string|null $created_at, + string|null $created_at, /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Errors\ErrorCode>|string|null */ - public string|null $message, + string|null $error_code, /** - * Salto KS metadata associated with the connected account that has a warning. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public Warnings\SaltoKsMetadata|null $salto_ks_metadata, + string|null $message, /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * Salto KS metadata associated with the connected account that has an error. */ - public string|null $warning_code, - ) {} + public \Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + bool|null $is_bridge_error = null, + /** + * Indicates whether the error is related specifically to the connected account. + */ + bool|null $is_connected_account_error = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + is_bridge_error: $is_bridge_error, + is_connected_account_error: $is_connected_account_error, + message: $message, + ); + } + } + + /** + * Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. + */ + final class DormakabaSitesDisconnected extends + \Seam\Resources\ConnectedAccount\Errors + { + public static function from_json( + mixed $json, + ): DormakabaSitesDisconnected|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + bool|null $is_bridge_error = null, + /** + * Indicates whether the error is related specifically to the connected account. + */ + bool|null $is_connected_account_error = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + is_bridge_error: $is_bridge_error, + is_connected_account_error: $is_connected_account_error, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case ACCOUNT_DISCONNECTED = "account_disconnected"; + case BRIDGE_DISCONNECTED = "bridge_disconnected"; + case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; + case DORMAKABA_SITES_DISCONNECTED = "dormakaba_sites_disconnected"; } } -namespace Seam\Resources\ConnectedAccount\Errors { +namespace Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded { /** * Salto KS metadata associated with the connected account that has an error. */ @@ -281,7 +584,11 @@ public static function from_json(mixed $json): SaltoKsMetadata|null } return new self( sites: array_map( - fn($s) => SaltoKsMetadata\Sites::from_json($s), + fn( + $s, + ) => \Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded\SaltoKsMetadata\Sites::from_json( + $s, + ), $json->sites ?? [], ), ); @@ -290,13 +597,15 @@ public static function from_json(mixed $json): SaltoKsMetadata|null public function __construct( /** * Salto sites associated with the connected account that has an error. + * + * @var list<\Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded\SaltoKsMetadata\Sites>|null */ public array|null $sites = null, ) {} } } -namespace Seam\Resources\ConnectedAccount\Errors\SaltoKsMetadata { +namespace Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded\SaltoKsMetadata { /** * Salto sites associated with the connected account that has an error. */ @@ -339,6 +648,369 @@ public function __construct( } namespace Seam\Resources\ConnectedAccount\Warnings { + /** + * Indicates that scheduled downtime is planned for the connected account. + */ + final class ScheduledMaintenanceWindow extends + \Seam\Resources\ConnectedAccount\Warnings + { + public static function from_json( + mixed $json, + ): ScheduledMaintenanceWindow|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class UnknownIssueWithConnectedAccount extends + \Seam\Resources\ConnectedAccount\Warnings + { + public static function from_json( + mixed $json, + ): UnknownIssueWithConnectedAccount|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class SaltoKsSubscriptionLimitAlmostReached extends + \Seam\Resources\ConnectedAccount\Warnings + { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitAlmostReached|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? \Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Salto KS metadata associated with the connected account that has a warning. + */ + public \Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class AccountReauthorizationRequested extends + \Seam\Resources\ConnectedAccount\Warnings + { + public static function from_json( + mixed $json, + ): AccountReauthorizationRequested|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class BeingDeleted extends \Seam\Resources\ConnectedAccount\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the connected account's provider service is temporarily unavailable. Seam will automatically retry and reconnect when the service becomes available again. + */ + final class ProviderServiceUnavailable extends + \Seam\Resources\ConnectedAccount\Warnings + { + public static function from_json( + mixed $json, + ): ProviderServiceUnavailable|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class SetupRequired extends \Seam\Resources\ConnectedAccount\Warnings + { + public static function from_json(mixed $json): SetupRequired|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class DormakabaSitesUnapproved extends + \Seam\Resources\ConnectedAccount\Warnings + { + public static function from_json( + mixed $json, + ): DormakabaSitesUnapproved|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\ConnectedAccount\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case SCHEDULED_MAINTENANCE_WINDOW = "scheduled_maintenance_window"; + case UNKNOWN_ISSUE_WITH_CONNECTED_ACCOUNT = "unknown_issue_with_connected_account"; + case SALTO_KS_SUBSCRIPTION_LIMIT_ALMOST_REACHED = "salto_ks_subscription_limit_almost_reached"; + case ACCOUNT_REAUTHORIZATION_REQUESTED = "account_reauthorization_requested"; + case BEING_DELETED = "being_deleted"; + case PROVIDER_SERVICE_UNAVAILABLE = "provider_service_unavailable"; + case SETUP_REQUIRED = "setup_required"; + case DORMAKABA_SITES_UNAPPROVED = "dormakaba_sites_unapproved"; + } +} + +namespace Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached { /** * Salto KS metadata associated with the connected account that has a warning. */ @@ -351,7 +1023,11 @@ public static function from_json(mixed $json): SaltoKsMetadata|null } return new self( sites: array_map( - fn($s) => SaltoKsMetadata\Sites::from_json($s), + fn( + $s, + ) => \Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached\SaltoKsMetadata\Sites::from_json( + $s, + ), $json->sites ?? [], ), ); @@ -360,13 +1036,15 @@ public static function from_json(mixed $json): SaltoKsMetadata|null public function __construct( /** * Salto sites associated with the connected account that has a warning. + * + * @var list<\Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached\SaltoKsMetadata\Sites>|null */ public array|null $sites = null, ) {} } } -namespace Seam\Resources\ConnectedAccount\Warnings\SaltoKsMetadata { +namespace Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached\SaltoKsMetadata { /** * Salto sites associated with the connected account that has a warning. */ diff --git a/src/Resources/Device.php b/src/Resources/Device.php index ee7ed893..8e8da76f 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -12,6 +12,29 @@ public static function from_json(mixed $json): Device|null return null; } return new self( + capabilities_supported: $json->capabilities_supported ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + device_id: $json->device_id ?? null, + device_type: $json->device_type ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => \Seam\Resources\Device\Errors::from_json($e), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + properties: isset($json->properties) + ? \Seam\Resources\Device\Properties::from_json( + $json->properties, + ) + : null, + space_ids: $json->space_ids ?? null, + warnings: array_map( + fn($w) => \Seam\Resources\Device\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, can_configure_auto_lock: $json->can_configure_auto_lock ?? null, can_hvac_cool: $json->can_hvac_cool ?? null, can_hvac_heat: $json->can_hvac_heat ?? null, @@ -42,45 +65,30 @@ public static function from_json(mixed $json): Device|null can_simulate_removal: $json->can_simulate_removal ?? null, can_turn_off_hvac: $json->can_turn_off_hvac ?? null, can_unlock_with_code: $json->can_unlock_with_code ?? null, - capabilities_supported: $json->capabilities_supported ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - device_id: $json->device_id ?? null, device_manufacturer: isset($json->device_manufacturer) - ? Device\DeviceManufacturer::from_json( + ? \Seam\Resources\Device\DeviceManufacturer::from_json( $json->device_manufacturer, ) : null, device_provider: isset($json->device_provider) - ? Device\DeviceProvider::from_json($json->device_provider) + ? \Seam\Resources\Device\DeviceProvider::from_json( + $json->device_provider, + ) : null, - device_type: $json->device_type ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => Device\Errors::from_json($e), - $json->errors ?? [], - ), - is_managed: $json->is_managed ?? null, location: isset($json->location) - ? Device\Location::from_json($json->location) + ? \Seam\Resources\Device\Location::from_json( + $json->location, + ) : null, nickname: $json->nickname ?? null, - properties: isset($json->properties) - ? Device\Properties::from_json($json->properties) - : null, - space_ids: $json->space_ids ?? null, - warnings: array_map( - fn($w) => Device\Warnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, ); } public function __construct( /** * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). + * + * @var list|null */ public array|null $capabilities_supported, /** @@ -103,6 +111,8 @@ public function __construct( public string|null $device_id, /** * Type of the device. + * + * @var value-of<\Seam\Resources\Device\DeviceType>|string|null */ public string|null $device_type, /** @@ -111,6 +121,8 @@ public function __construct( public string|null $display_name, /** * 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. + * + * @var list<\Seam\Resources\Device\Errors> */ public array $errors, /** @@ -120,13 +132,17 @@ public function __construct( /** * Properties of the device. */ - public Device\Properties|null $properties, + public \Seam\Resources\Device\Properties|null $properties, /** * IDs of the spaces the device is in. + * + * @var list|null */ public array|null $space_ids, /** * 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. + * + * @var list<\Seam\Resources\Device\Warnings> */ public array $warnings, /** @@ -216,15 +232,15 @@ public function __construct( /** * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. */ - public Device\DeviceManufacturer|null $device_manufacturer = null, + public \Seam\Resources\Device\DeviceManufacturer|null $device_manufacturer = null, /** * Provider of the device. Represents the third-party service through which the device is controlled. */ - public Device\DeviceProvider|null $device_provider = null, + public \Seam\Resources\Device\DeviceProvider|null $device_provider = null, /** * Location information for the device. */ - public Device\Location|null $location = null, + public \Seam\Resources\Device\Location|null $location = null, /** * Optional nickname to describe the device, settable through Seam. */ @@ -246,8 +262,8 @@ public static function from_json(mixed $json): DeviceManufacturer|null } return new self( display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, manufacturer: $json->manufacturer ?? null, + image_url: $json->image_url ?? null, ); } @@ -280,8 +296,8 @@ public static function from_json(mixed $json): DeviceProvider|null return new self( device_provider_name: $json->device_provider_name ?? null, display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, provider_category: $json->provider_category ?? null, + image_url: $json->image_url ?? null, ); } @@ -306,7 +322,7 @@ public function __construct( } /** - * 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. + * 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. Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -315,15 +331,75 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\Device\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\Device\Errors\ErrorCode::ACCOUNT_DISCONNECTED + => \Seam\Resources\Device\Errors\AccountDisconnected::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED + => \Seam\Resources\Device\Errors\SaltoKsSubscriptionLimitExceeded::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::INSUFFICIENT_PERMISSIONS + => \Seam\Resources\Device\Errors\InsufficientPermissions::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::DORMAKABA_SITES_DISCONNECTED + => \Seam\Resources\Device\Errors\DormakabaSitesDisconnected::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::DEVICE_OFFLINE + => \Seam\Resources\Device\Errors\DeviceOffline::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::DEVICE_REMOVED + => \Seam\Resources\Device\Errors\DeviceRemoved::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::HUB_DISCONNECTED + => \Seam\Resources\Device\Errors\HubDisconnected::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::DEVICE_DISCONNECTED + => \Seam\Resources\Device\Errors\DeviceDisconnected::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::EMPTY_BACKUP_ACCESS_CODE_POOL + => \Seam\Resources\Device\Errors\EmptyBackupAccessCodePool::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::AUGUST_LOCK_NOT_AUTHORIZED + => \Seam\Resources\Device\Errors\AugustLockNotAuthorized::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::MISSING_DEVICE_CREDENTIALS + => \Seam\Resources\Device\Errors\MissingDeviceCredentials::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::AUXILIARY_HEAT_RUNNING + => \Seam\Resources\Device\Errors\AuxiliaryHeatRunning::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::SUBSCRIPTION_REQUIRED + => \Seam\Resources\Device\Errors\SubscriptionRequired::from_json( + $json, + ), + \Seam\Resources\Device\Errors\ErrorCode::BRIDGE_DISCONNECTED + => \Seam\Resources\Device\Errors\BridgeDisconnected::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -333,18 +409,14 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ public string|null $error_code, - public bool|null $is_connected_account_error, - public bool|null $is_device_error, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error = null, ) {} } @@ -399,47 +471,58 @@ public static function from_json(mixed $json): Properties|null return null; } return new self( + appearance: isset($json->appearance) + ? \Seam\Resources\Device\Properties\Appearance::from_json( + $json->appearance, + ) + : null, + model: isset($json->model) + ? \Seam\Resources\Device\Properties\Model::from_json( + $json->model, + ) + : null, + name: $json->name ?? null, + online: $json->online ?? null, accessory_keypad: isset($json->accessory_keypad) - ? Properties\AccessoryKeypad::from_json( + ? \Seam\Resources\Device\Properties\AccessoryKeypad::from_json( $json->accessory_keypad, ) : null, active_thermostat_schedule: isset( $json->active_thermostat_schedule, ) - ? Properties\ActiveThermostatSchedule::from_json( + ? \Seam\Resources\Device\Properties\ActiveThermostatSchedule::from_json( $json->active_thermostat_schedule, ) : null, active_thermostat_schedule_id: $json->active_thermostat_schedule_id ?? null, akiles_metadata: isset($json->akiles_metadata) - ? Properties\AkilesMetadata::from_json( + ? \Seam\Resources\Device\Properties\AkilesMetadata::from_json( $json->akiles_metadata, ) : null, - appearance: isset($json->appearance) - ? Properties\Appearance::from_json($json->appearance) - : null, aqara_metadata: isset($json->aqara_metadata) - ? Properties\AqaraMetadata::from_json($json->aqara_metadata) + ? \Seam\Resources\Device\Properties\AqaraMetadata::from_json( + $json->aqara_metadata, + ) : null, assa_abloy_credential_service_metadata: isset( $json->assa_abloy_credential_service_metadata, ) - ? Properties\AssaAbloyCredentialServiceMetadata::from_json( + ? \Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata::from_json( $json->assa_abloy_credential_service_metadata, ) : null, assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - ? Properties\AssaAbloyVostioMetadata::from_json( + ? \Seam\Resources\Device\Properties\AssaAbloyVostioMetadata::from_json( $json->assa_abloy_vostio_metadata, ) : null, august_metadata: isset($json->august_metadata) - ? Properties\AugustMetadata::from_json( + ? \Seam\Resources\Device\Properties\AugustMetadata::from_json( $json->august_metadata, ) : null, @@ -448,7 +531,11 @@ public static function from_json(mixed $json): Properties|null available_climate_preset_modes: $json->available_climate_preset_modes ?? null, available_climate_presets: array_map( - fn($a) => Properties\AvailableClimatePresets::from_json($a), + fn( + $a, + ) => \Seam\Resources\Device\Properties\AvailableClimatePresets::from_json( + $a, + ), $json->available_climate_presets ?? [], ), available_fan_mode_settings: $json->available_fan_mode_settings ?? @@ -456,37 +543,45 @@ public static function from_json(mixed $json): Properties|null available_hvac_mode_settings: $json->available_hvac_mode_settings ?? null, avigilon_alta_metadata: isset($json->avigilon_alta_metadata) - ? Properties\AvigilonAltaMetadata::from_json( + ? \Seam\Resources\Device\Properties\AvigilonAltaMetadata::from_json( $json->avigilon_alta_metadata, ) : null, backup_access_code_pool_enabled: $json->backup_access_code_pool_enabled ?? null, battery: isset($json->battery) - ? Properties\Battery::from_json($json->battery) + ? \Seam\Resources\Device\Properties\Battery::from_json( + $json->battery, + ) : null, battery_level: $json->battery_level ?? null, brivo_metadata: isset($json->brivo_metadata) - ? Properties\BrivoMetadata::from_json($json->brivo_metadata) + ? \Seam\Resources\Device\Properties\BrivoMetadata::from_json( + $json->brivo_metadata, + ) : null, code_constraints: array_map( - fn($c) => Properties\CodeConstraints::from_json($c), + fn( + $c, + ) => \Seam\Resources\Device\Properties\CodeConstraints::from_json( + $c, + ), $json->code_constraints ?? [], ), controlbyweb_metadata: isset($json->controlbyweb_metadata) - ? Properties\ControlbywebMetadata::from_json( + ? \Seam\Resources\Device\Properties\ControlbywebMetadata::from_json( $json->controlbyweb_metadata, ) : null, current_climate_setting: isset($json->current_climate_setting) - ? Properties\CurrentClimateSetting::from_json( + ? \Seam\Resources\Device\Properties\CurrentClimateSetting::from_json( $json->current_climate_setting, ) : null, currently_triggering_noise_threshold_ids: $json->currently_triggering_noise_threshold_ids ?? null, default_climate_setting: isset($json->default_climate_setting) - ? Properties\DefaultClimateSetting::from_json( + ? \Seam\Resources\Device\Properties\DefaultClimateSetting::from_json( $json->default_climate_setting, ) : null, @@ -494,12 +589,12 @@ public static function from_json(mixed $json): Properties|null dormakaba_oracode_metadata: isset( $json->dormakaba_oracode_metadata, ) - ? Properties\DormakabaOracodeMetadata::from_json( + ? \Seam\Resources\Device\Properties\DormakabaOracodeMetadata::from_json( $json->dormakaba_oracode_metadata, ) : null, ecobee_metadata: isset($json->ecobee_metadata) - ? Properties\EcobeeMetadata::from_json( + ? \Seam\Resources\Device\Properties\EcobeeMetadata::from_json( $json->ecobee_metadata, ) : null, @@ -507,27 +602,31 @@ public static function from_json(mixed $json): Properties|null null, fan_mode_setting: $json->fan_mode_setting ?? null, four_suites_metadata: isset($json->four_suites_metadata) - ? Properties\FourSuitesMetadata::from_json( + ? \Seam\Resources\Device\Properties\FourSuitesMetadata::from_json( $json->four_suites_metadata, ) : null, genie_metadata: isset($json->genie_metadata) - ? Properties\GenieMetadata::from_json($json->genie_metadata) + ? \Seam\Resources\Device\Properties\GenieMetadata::from_json( + $json->genie_metadata, + ) : null, has_direct_power: $json->has_direct_power ?? null, has_native_entry_events: $json->has_native_entry_events ?? null, honeywell_resideo_metadata: isset( $json->honeywell_resideo_metadata, ) - ? Properties\HoneywellResideoMetadata::from_json( + ? \Seam\Resources\Device\Properties\HoneywellResideoMetadata::from_json( $json->honeywell_resideo_metadata, ) : null, igloo_metadata: isset($json->igloo_metadata) - ? Properties\IglooMetadata::from_json($json->igloo_metadata) + ? \Seam\Resources\Device\Properties\IglooMetadata::from_json( + $json->igloo_metadata, + ) : null, igloohome_metadata: isset($json->igloohome_metadata) - ? Properties\IgloohomeMetadata::from_json( + ? \Seam\Resources\Device\Properties\IgloohomeMetadata::from_json( $json->igloohome_metadata, ) : null, @@ -539,29 +638,33 @@ public static function from_json(mixed $json): Properties|null is_temporary_manual_override_active: $json->is_temporary_manual_override_active ?? null, keynest_metadata: isset($json->keynest_metadata) - ? Properties\KeynestMetadata::from_json( + ? \Seam\Resources\Device\Properties\KeynestMetadata::from_json( $json->keynest_metadata, ) : null, keypad_battery: isset($json->keypad_battery) - ? Properties\KeypadBattery::from_json($json->keypad_battery) + ? \Seam\Resources\Device\Properties\KeypadBattery::from_json( + $json->keypad_battery, + ) : null, kisi_metadata: isset($json->kisi_metadata) - ? Properties\KisiMetadata::from_json($json->kisi_metadata) + ? \Seam\Resources\Device\Properties\KisiMetadata::from_json( + $json->kisi_metadata, + ) : null, korelock_metadata: isset($json->korelock_metadata) - ? Properties\KorelockMetadata::from_json( + ? \Seam\Resources\Device\Properties\KorelockMetadata::from_json( $json->korelock_metadata, ) : null, kwikset_metadata: isset($json->kwikset_metadata) - ? Properties\KwiksetMetadata::from_json( + ? \Seam\Resources\Device\Properties\KwiksetMetadata::from_json( $json->kwikset_metadata, ) : null, locked: $json->locked ?? null, lockly_metadata: isset($json->lockly_metadata) - ? Properties\LocklyMetadata::from_json( + ? \Seam\Resources\Device\Properties\LocklyMetadata::from_json( $json->lockly_metadata, ) : null, @@ -593,77 +696,92 @@ public static function from_json(mixed $json): Properties|null min_heating_set_point_fahrenheit: $json->min_heating_set_point_fahrenheit ?? null, minut_metadata: isset($json->minut_metadata) - ? Properties\MinutMetadata::from_json($json->minut_metadata) - : null, - model: isset($json->model) - ? Properties\Model::from_json($json->model) + ? \Seam\Resources\Device\Properties\MinutMetadata::from_json( + $json->minut_metadata, + ) : null, - name: $json->name ?? null, nest_metadata: isset($json->nest_metadata) - ? Properties\NestMetadata::from_json($json->nest_metadata) + ? \Seam\Resources\Device\Properties\NestMetadata::from_json( + $json->nest_metadata, + ) : null, noise_level_decibels: $json->noise_level_decibels ?? null, noiseaware_metadata: isset($json->noiseaware_metadata) - ? Properties\NoiseawareMetadata::from_json( + ? \Seam\Resources\Device\Properties\NoiseawareMetadata::from_json( $json->noiseaware_metadata, ) : null, nuki_metadata: isset($json->nuki_metadata) - ? Properties\NukiMetadata::from_json($json->nuki_metadata) + ? \Seam\Resources\Device\Properties\NukiMetadata::from_json( + $json->nuki_metadata, + ) : null, offline_access_codes_enabled: $json->offline_access_codes_enabled ?? null, offline_time_frame_options: array_map( - fn($o) => Properties\OfflineTimeFrameOptions::from_json($o), + fn( + $o, + ) => \Seam\Resources\Device\Properties\OfflineTimeFrameOptions::from_json( + $o, + ), $json->offline_time_frame_options ?? [], ), omnitec_metadata: isset($json->omnitec_metadata) - ? Properties\OmnitecMetadata::from_json( + ? \Seam\Resources\Device\Properties\OmnitecMetadata::from_json( $json->omnitec_metadata, ) : null, - online: $json->online ?? null, online_access_codes_enabled: $json->online_access_codes_enabled ?? null, online_time_frame_options: array_map( - fn($o) => Properties\OnlineTimeFrameOptions::from_json($o), + fn( + $o, + ) => \Seam\Resources\Device\Properties\OnlineTimeFrameOptions::from_json( + $o, + ), $json->online_time_frame_options ?? [], ), relative_humidity: $json->relative_humidity ?? null, ring_metadata: isset($json->ring_metadata) - ? Properties\RingMetadata::from_json($json->ring_metadata) + ? \Seam\Resources\Device\Properties\RingMetadata::from_json( + $json->ring_metadata, + ) : null, salto_ks_metadata: isset($json->salto_ks_metadata) - ? Properties\SaltoKsMetadata::from_json( + ? \Seam\Resources\Device\Properties\SaltoKsMetadata::from_json( $json->salto_ks_metadata, ) : null, salto_metadata: isset($json->salto_metadata) - ? Properties\SaltoMetadata::from_json($json->salto_metadata) + ? \Seam\Resources\Device\Properties\SaltoMetadata::from_json( + $json->salto_metadata, + ) : null, salto_space_credential_service_metadata: isset( $json->salto_space_credential_service_metadata, ) - ? Properties\SaltoSpaceCredentialServiceMetadata::from_json( + ? \Seam\Resources\Device\Properties\SaltoSpaceCredentialServiceMetadata::from_json( $json->salto_space_credential_service_metadata, ) : null, schlage_metadata: isset($json->schlage_metadata) - ? Properties\SchlageMetadata::from_json( + ? \Seam\Resources\Device\Properties\SchlageMetadata::from_json( $json->schlage_metadata, ) : null, seam_bridge_metadata: isset($json->seam_bridge_metadata) - ? Properties\SeamBridgeMetadata::from_json( + ? \Seam\Resources\Device\Properties\SeamBridgeMetadata::from_json( $json->seam_bridge_metadata, ) : null, sensi_metadata: isset($json->sensi_metadata) - ? Properties\SensiMetadata::from_json($json->sensi_metadata) + ? \Seam\Resources\Device\Properties\SensiMetadata::from_json( + $json->sensi_metadata, + ) : null, serial_number: $json->serial_number ?? null, smartthings_metadata: isset($json->smartthings_metadata) - ? Properties\SmartthingsMetadata::from_json( + ? \Seam\Resources\Device\Properties\SmartthingsMetadata::from_json( $json->smartthings_metadata, ) : null, @@ -675,54 +793,68 @@ public static function from_json(mixed $json): Properties|null supports_offline_access_codes: $json->supports_offline_access_codes ?? null, tado_metadata: isset($json->tado_metadata) - ? Properties\TadoMetadata::from_json($json->tado_metadata) + ? \Seam\Resources\Device\Properties\TadoMetadata::from_json( + $json->tado_metadata, + ) : null, tedee_metadata: isset($json->tedee_metadata) - ? Properties\TedeeMetadata::from_json($json->tedee_metadata) + ? \Seam\Resources\Device\Properties\TedeeMetadata::from_json( + $json->tedee_metadata, + ) : null, temperature_celsius: $json->temperature_celsius ?? null, temperature_fahrenheit: $json->temperature_fahrenheit ?? null, temperature_threshold: isset($json->temperature_threshold) - ? Properties\TemperatureThreshold::from_json( + ? \Seam\Resources\Device\Properties\TemperatureThreshold::from_json( $json->temperature_threshold, ) : null, thermostat_daily_program_period_precision_minutes: $json->thermostat_daily_program_period_precision_minutes ?? null, thermostat_daily_programs: array_map( - fn($t) => Properties\ThermostatDailyPrograms::from_json($t), + fn( + $t, + ) => \Seam\Resources\Device\Properties\ThermostatDailyPrograms::from_json( + $t, + ), $json->thermostat_daily_programs ?? [], ), thermostat_weekly_program: isset( $json->thermostat_weekly_program, ) - ? Properties\ThermostatWeeklyProgram::from_json( + ? \Seam\Resources\Device\Properties\ThermostatWeeklyProgram::from_json( $json->thermostat_weekly_program, ) : null, ttlock_metadata: isset($json->ttlock_metadata) - ? Properties\TtlockMetadata::from_json( + ? \Seam\Resources\Device\Properties\TtlockMetadata::from_json( $json->ttlock_metadata, ) : null, two_n_metadata: isset($json->two_n_metadata) - ? Properties\TwoNMetadata::from_json($json->two_n_metadata) + ? \Seam\Resources\Device\Properties\TwoNMetadata::from_json( + $json->two_n_metadata, + ) : null, ultraloq_metadata: isset($json->ultraloq_metadata) - ? Properties\UltraloqMetadata::from_json( + ? \Seam\Resources\Device\Properties\UltraloqMetadata::from_json( $json->ultraloq_metadata, ) : null, visionline_metadata: isset($json->visionline_metadata) - ? Properties\VisionlineMetadata::from_json( + ? \Seam\Resources\Device\Properties\VisionlineMetadata::from_json( $json->visionline_metadata, ) : null, wyze_metadata: isset($json->wyze_metadata) - ? Properties\WyzeMetadata::from_json($json->wyze_metadata) + ? \Seam\Resources\Device\Properties\WyzeMetadata::from_json( + $json->wyze_metadata, + ) : null, yacan_metadata: isset($json->yacan_metadata) - ? Properties\YacanMetadata::from_json($json->yacan_metadata) + ? \Seam\Resources\Device\Properties\YacanMetadata::from_json( + $json->yacan_metadata, + ) : null, ); } @@ -731,11 +863,11 @@ public function __construct( /** * Appearance-related properties, as reported by the device. */ - public Properties\Appearance|null $appearance, + public \Seam\Resources\Device\Properties\Appearance|null $appearance, /** * Device model-related properties. */ - public Properties\Model|null $model, + public \Seam\Resources\Device\Properties\Model|null $model, /** * Name of the device. * @@ -749,13 +881,13 @@ public function __construct( /** * Accessory keypad properties and state. */ - public Properties\AccessoryKeypad|null $accessory_keypad = null, + public \Seam\Resources\Device\Properties\AccessoryKeypad|null $accessory_keypad = null, /** * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). * * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. */ - public Properties\ActiveThermostatSchedule|null $active_thermostat_schedule = null, + public \Seam\Resources\Device\Properties\ActiveThermostatSchedule|null $active_thermostat_schedule = null, /** * ID of the active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ @@ -763,23 +895,23 @@ public function __construct( /** * Metadata for an Akiles device. */ - public Properties\AkilesMetadata|null $akiles_metadata = null, + public \Seam\Resources\Device\Properties\AkilesMetadata|null $akiles_metadata = null, /** * Metadata for an Aqara device. */ - public Properties\AqaraMetadata|null $aqara_metadata = null, + public \Seam\Resources\Device\Properties\AqaraMetadata|null $aqara_metadata = null, /** * ASSA ABLOY Credential Service metadata for the phone. */ - public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata = null, + public \Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata = null, /** * Metadata for an ASSA ABLOY Vostio system. */ - public Properties\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, + public \Seam\Resources\Device\Properties\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Metadata for an August device. */ - public Properties\AugustMetadata|null $august_metadata = null, + public \Seam\Resources\Device\Properties\AugustMetadata|null $august_metadata = null, /** * The delay in seconds before the lock automatically locks after being unlocked. */ @@ -790,24 +922,32 @@ public function __construct( public bool|null $auto_lock_enabled = null, /** * Climate preset modes that the thermostat supports, such as "home", "away", "wake", "sleep", "occupied", and "unoccupied". + * + * @var list|null */ public array|null $available_climate_preset_modes = null, /** * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. + * + * @var list<\Seam\Resources\Device\Properties\AvailableClimatePresets>|null */ public array|null $available_climate_presets = null, /** * Fan mode settings that the thermostat supports. + * + * @var list|null */ public array|null $available_fan_mode_settings = null, /** * HVAC mode settings that the thermostat supports. + * + * @var list|null */ public array|null $available_hvac_mode_settings = null, /** * Metadata for an Avigilon Alta system. */ - public Properties\AvigilonAltaMetadata|null $avigilon_alta_metadata = null, + public \Seam\Resources\Device\Properties\AvigilonAltaMetadata|null $avigilon_alta_metadata = null, /** * Indicates whether the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is currently enabled for the device. To disable it, set this to `false` using [/devices/update](https://docs.seam.co/api/devices/update). */ @@ -815,7 +955,7 @@ public function __construct( /** * Represents the current status of the battery charge level. */ - public Properties\Battery|null $battery = null, + public \Seam\Resources\Device\Properties\Battery|null $battery = null, /** * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. */ @@ -823,27 +963,31 @@ public function __construct( /** * Metadata for a Brivo device. */ - public Properties\BrivoMetadata|null $brivo_metadata = null, + public \Seam\Resources\Device\Properties\BrivoMetadata|null $brivo_metadata = null, /** * 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. + * + * @var list<\Seam\Resources\Device\Properties\CodeConstraints>|null */ public array|null $code_constraints = null, /** * Metadata for a ControlByWeb device. */ - public Properties\ControlbywebMetadata|null $controlbyweb_metadata = null, + public \Seam\Resources\Device\Properties\ControlbywebMetadata|null $controlbyweb_metadata = null, /** * Current climate setting. */ - public Properties\CurrentClimateSetting|null $current_climate_setting = null, + public \Seam\Resources\Device\Properties\CurrentClimateSetting|null $current_climate_setting = null, /** * Array of noise threshold IDs that are currently triggering. + * + * @var list|null */ public array|null $currently_triggering_noise_threshold_ids = null, /** * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. */ - public Properties\DefaultClimateSetting|null $default_climate_setting = null, + public \Seam\Resources\Device\Properties\DefaultClimateSetting|null $default_climate_setting = null, /** * Indicates whether the door is open. */ @@ -851,27 +995,28 @@ public function __construct( /** * Metadata for a dormakaba Oracode device. */ - public Properties\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, + public \Seam\Resources\Device\Properties\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Metadata for an ecobee device. */ - public Properties\EcobeeMetadata|null $ecobee_metadata = null, + public \Seam\Resources\Device\Properties\EcobeeMetadata|null $ecobee_metadata = null, /** * Key of the [fallback climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets/setting-the-fallback-climate-preset) for the thermostat. */ public string|null $fallback_climate_preset_key = null, /** + * @var value-of<\Seam\Resources\Device\Properties\FanModeSetting>|string|null * @deprecated Use `current_climate_setting.fan_mode_setting` instead. */ public string|null $fan_mode_setting = null, /** * Metadata for a 4SUITES device. */ - public Properties\FourSuitesMetadata|null $four_suites_metadata = null, + public \Seam\Resources\Device\Properties\FourSuitesMetadata|null $four_suites_metadata = null, /** * Metadata for a Genie device. */ - public Properties\GenieMetadata|null $genie_metadata = null, + public \Seam\Resources\Device\Properties\GenieMetadata|null $genie_metadata = null, /** * Indicates whether the device has direct power. */ @@ -883,15 +1028,15 @@ public function __construct( /** * Metadata for a Honeywell Resideo device. */ - public Properties\HoneywellResideoMetadata|null $honeywell_resideo_metadata = null, + public \Seam\Resources\Device\Properties\HoneywellResideoMetadata|null $honeywell_resideo_metadata = null, /** * Metadata for an igloo device. */ - public Properties\IglooMetadata|null $igloo_metadata = null, + public \Seam\Resources\Device\Properties\IglooMetadata|null $igloo_metadata = null, /** * Metadata for an igloohome device. */ - public Properties\IgloohomeMetadata|null $igloohome_metadata = null, + public \Seam\Resources\Device\Properties\IgloohomeMetadata|null $igloohome_metadata = null, /** * Alt text for the device image. */ @@ -919,23 +1064,23 @@ public function __construct( /** * Metadata for a KeyNest device. */ - public Properties\KeynestMetadata|null $keynest_metadata = null, + public \Seam\Resources\Device\Properties\KeynestMetadata|null $keynest_metadata = null, /** * Keypad battery status. */ - public Properties\KeypadBattery|null $keypad_battery = null, + public \Seam\Resources\Device\Properties\KeypadBattery|null $keypad_battery = null, /** * Metadata for a Kisi device. */ - public Properties\KisiMetadata|null $kisi_metadata = null, + public \Seam\Resources\Device\Properties\KisiMetadata|null $kisi_metadata = null, /** * Metadata for a Korelock device. */ - public Properties\KorelockMetadata|null $korelock_metadata = null, + public \Seam\Resources\Device\Properties\KorelockMetadata|null $korelock_metadata = null, /** * Metadata for a Kwikset device. */ - public Properties\KwiksetMetadata|null $kwikset_metadata = null, + public \Seam\Resources\Device\Properties\KwiksetMetadata|null $kwikset_metadata = null, /** * Indicates whether the lock is locked. */ @@ -943,7 +1088,7 @@ public function __construct( /** * Metadata for a Lockly device. */ - public Properties\LocklyMetadata|null $lockly_metadata = null, + public \Seam\Resources\Device\Properties\LocklyMetadata|null $lockly_metadata = null, /** * 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. */ @@ -1003,11 +1148,11 @@ public function __construct( /** * Metadata for a Minut device. */ - public Properties\MinutMetadata|null $minut_metadata = null, + public \Seam\Resources\Device\Properties\MinutMetadata|null $minut_metadata = null, /** * Metadata for a Google Nest device. */ - public Properties\NestMetadata|null $nest_metadata = null, + public \Seam\Resources\Device\Properties\NestMetadata|null $nest_metadata = null, /** * Indicates current noise level in decibels, if the device supports noise detection. */ @@ -1015,11 +1160,11 @@ public function __construct( /** * Metadata for a NoiseAware device. */ - public Properties\NoiseawareMetadata|null $noiseaware_metadata = null, + public \Seam\Resources\Device\Properties\NoiseawareMetadata|null $noiseaware_metadata = null, /** * Metadata for a Nuki device. */ - public Properties\NukiMetadata|null $nuki_metadata = null, + public \Seam\Resources\Device\Properties\NukiMetadata|null $nuki_metadata = null, /** * Indicates whether it is currently possible to use offline access codes for the device. * @@ -1028,12 +1173,14 @@ public function __construct( public bool|null $offline_access_codes_enabled = null, /** * 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. + * + * @var list<\Seam\Resources\Device\Properties\OfflineTimeFrameOptions>|null */ public array|null $offline_time_frame_options = null, /** * Metadata for an Omnitec device. */ - public Properties\OmnitecMetadata|null $omnitec_metadata = null, + public \Seam\Resources\Device\Properties\OmnitecMetadata|null $omnitec_metadata = null, /** * Indicates whether it is currently possible to use online access codes for the device. * @@ -1042,6 +1189,8 @@ public function __construct( public bool|null $online_access_codes_enabled = null, /** * 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. + * + * @var list<\Seam\Resources\Device\Properties\OnlineTimeFrameOptions>|null */ public array|null $online_time_frame_options = null, /** @@ -1051,33 +1200,33 @@ public function __construct( /** * Metadata for a Ring device. */ - public Properties\RingMetadata|null $ring_metadata = null, + public \Seam\Resources\Device\Properties\RingMetadata|null $ring_metadata = null, /** * Metadata for a Salto KS device. */ - public Properties\SaltoKsMetadata|null $salto_ks_metadata = null, + public \Seam\Resources\Device\Properties\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Metada for a Salto device. * * @deprecated Use `salto_ks_metadata` instead. */ - public Properties\SaltoMetadata|null $salto_metadata = null, + public \Seam\Resources\Device\Properties\SaltoMetadata|null $salto_metadata = null, /** * Salto Space credential service metadata for the phone. */ - public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata = null, + public \Seam\Resources\Device\Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata = null, /** * Metadata for a Schlage device. */ - public Properties\SchlageMetadata|null $schlage_metadata = null, + public \Seam\Resources\Device\Properties\SchlageMetadata|null $schlage_metadata = null, /** * Metadata for Seam Bridge. */ - public Properties\SeamBridgeMetadata|null $seam_bridge_metadata = null, + public \Seam\Resources\Device\Properties\SeamBridgeMetadata|null $seam_bridge_metadata = null, /** * Metadata for a Sensi device. */ - public Properties\SensiMetadata|null $sensi_metadata = null, + public \Seam\Resources\Device\Properties\SensiMetadata|null $sensi_metadata = null, /** * Serial number of the device. */ @@ -1085,9 +1234,11 @@ public function __construct( /** * Metadata for a SmartThings device. */ - public Properties\SmartthingsMetadata|null $smartthings_metadata = null, + public \Seam\Resources\Device\Properties\SmartthingsMetadata|null $smartthings_metadata = null, /** * Supported code lengths for access codes. + * + * @var list|null */ public array|null $supported_code_lengths = null, /** @@ -1105,11 +1256,11 @@ public function __construct( /** * Metadata for a tado° device. */ - public Properties\TadoMetadata|null $tado_metadata = null, + public \Seam\Resources\Device\Properties\TadoMetadata|null $tado_metadata = null, /** * Metadata for a Tedee device. */ - public Properties\TedeeMetadata|null $tedee_metadata = null, + public \Seam\Resources\Device\Properties\TedeeMetadata|null $tedee_metadata = null, /** * Reported temperature in °C. */ @@ -1121,48 +1272,50 @@ public function __construct( /** * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. */ - public Properties\TemperatureThreshold|null $temperature_threshold = null, + public \Seam\Resources\Device\Properties\TemperatureThreshold|null $temperature_threshold = null, /** * Precision of the thermostat's period in minutes. For example, if the thermostat supports 15-minute periods, this value is 15. All values are relative to the top of the hour, so for 15 minutes, the periods would be 0, 15, 30, and 45 minutes past the hour. */ public float|null $thermostat_daily_program_period_precision_minutes = null, /** * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + * + * @var list<\Seam\Resources\Device\Properties\ThermostatDailyPrograms>|null */ public array|null $thermostat_daily_programs = null, /** * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. */ - public Properties\ThermostatWeeklyProgram|null $thermostat_weekly_program = null, + public \Seam\Resources\Device\Properties\ThermostatWeeklyProgram|null $thermostat_weekly_program = null, /** * Metadata for a TTLock device. */ - public Properties\TtlockMetadata|null $ttlock_metadata = null, + public \Seam\Resources\Device\Properties\TtlockMetadata|null $ttlock_metadata = null, /** * Metadata for a 2N device. */ - public Properties\TwoNMetadata|null $two_n_metadata = null, + public \Seam\Resources\Device\Properties\TwoNMetadata|null $two_n_metadata = null, /** * Metadata for an Ultraloq device. */ - public Properties\UltraloqMetadata|null $ultraloq_metadata = null, + public \Seam\Resources\Device\Properties\UltraloqMetadata|null $ultraloq_metadata = null, /** * Metadata for an ASSA ABLOY Visionline system. */ - public Properties\VisionlineMetadata|null $visionline_metadata = null, + public \Seam\Resources\Device\Properties\VisionlineMetadata|null $visionline_metadata = null, /** * Metadata for a Wyze device. */ - public Properties\WyzeMetadata|null $wyze_metadata = null, + public \Seam\Resources\Device\Properties\WyzeMetadata|null $wyze_metadata = null, /** * Metadata for a Yacan device. */ - public Properties\YacanMetadata|null $yacan_metadata = null, + public \Seam\Resources\Device\Properties\YacanMetadata|null $yacan_metadata = null, ) {} } /** - * 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. + * 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. Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { @@ -1171,1082 +1324,1357 @@ public static function from_json(mixed $json): Warnings|null if (!$json) { return null; } - return new self( - active_access_code_count: $json->active_access_code_count ?? - null, - created_at: $json->created_at ?? null, - max_active_access_code_count: $json->max_active_access_code_count ?? - null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\Device\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\Device\Warnings\WarningCode::PARTIAL_BACKUP_ACCESS_CODE_POOL + => \Seam\Resources\Device\Warnings\PartialBackupAccessCodePool::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::MANY_ACTIVE_BACKUP_CODES + => \Seam\Resources\Device\Warnings\ManyActiveBackupCodes::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::THIRD_PARTY_INTEGRATION_DETECTED + => \Seam\Resources\Device\Warnings\ThirdPartyIntegrationDetected::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::TTLOCK_LOCK_GATEWAY_UNLOCKING_NOT_ENABLED + => \Seam\Resources\Device\Warnings\TtlockLockGatewayUnlockingNotEnabled::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::TTLOCK_WEAK_GATEWAY_SIGNAL + => \Seam\Resources\Device\Warnings\TtlockWeakGatewaySignal::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::POWER_SAVING_MODE + => \Seam\Resources\Device\Warnings\PowerSavingMode::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::TEMPERATURE_THRESHOLD_EXCEEDED + => \Seam\Resources\Device\Warnings\TemperatureThresholdExceeded::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::DEVICE_COMMUNICATION_DEGRADED + => \Seam\Resources\Device\Warnings\DeviceCommunicationDegraded::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::SCHEDULED_MAINTENANCE_WINDOW + => \Seam\Resources\Device\Warnings\ScheduledMaintenanceWindow::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::DEVICE_HAS_FLAKY_CONNECTION + => \Seam\Resources\Device\Warnings\DeviceHasFlakyConnection::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::SALTO_KS_OFFICE_MODE + => \Seam\Resources\Device\Warnings\SaltoKsOfficeMode::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::SALTO_KS_PRIVACY_MODE + => \Seam\Resources\Device\Warnings\SaltoKsPrivacyMode::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::PRIVACY_MODE + => \Seam\Resources\Device\Warnings\PrivacyMode::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::SALTO_KS_SUBSCRIPTION_LIMIT_ALMOST_REACHED + => \Seam\Resources\Device\Warnings\SaltoKsSubscriptionLimitAlmostReached::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::SALTO_KS_LOCK_ACCESS_CODE_SUPPORT_REMOVED + => \Seam\Resources\Device\Warnings\SaltoKsLockAccessCodeSupportRemoved::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::UNKNOWN_ISSUE_WITH_PHONE + => \Seam\Resources\Device\Warnings\UnknownIssueWithPhone::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::LOCKLY_TIME_ZONE_NOT_CONFIGURED + => \Seam\Resources\Device\Warnings\LocklyTimeZoneNotConfigured::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::ULTRALOQ_TIME_ZONE_UNKNOWN + => \Seam\Resources\Device\Warnings\UltraloqTimeZoneUnknown::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::TIME_ZONE_UNKNOWN + => \Seam\Resources\Device\Warnings\TimeZoneUnknown::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::TIME_ZONE_MISMATCH + => \Seam\Resources\Device\Warnings\TimeZoneMismatch::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::TWO_N_DEVICE_MISSING_TIMEZONE + => \Seam\Resources\Device\Warnings\TwoNDeviceMissingTimezone::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::HUB_REQUIRED_FOR_ADDITIONAL_CAPABILITIES + => \Seam\Resources\Device\Warnings\HubRequiredForAdditionalCapabilities::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::PROVIDER_ISSUE + => \Seam\Resources\Device\Warnings\ProviderIssue::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::KEYNEST_UNSUPPORTED_LOCKER + => \Seam\Resources\Device\Warnings\KeynestUnsupportedLocker::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::ACCESSORY_KEYPAD_SETUP_REQUIRED + => \Seam\Resources\Device\Warnings\AccessoryKeypadSetupRequired::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::UNRELIABLE_ONLINE_STATUS + => \Seam\Resources\Device\Warnings\UnreliableOnlineStatus::from_json( + $json, + ), + \Seam\Resources\Device\Warnings\WarningCode::MAX_ACCESS_CODES_REACHED + => \Seam\Resources\Device\Warnings\MaxAccessCodesReached::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; } public function __construct( - /** - * Number of active access codes on the device when the warning was set. - */ - public int|null $active_access_code_count, /** * Date and time at which Seam created the warning. */ public string|null $created_at, - /** - * Maximum number of active access codes supported by the device. - */ - public int|null $max_active_access_code_count, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null */ public string|null $warning_code, ) {} } + + enum DeviceType: string + { + case AKUVOX_LOCK = "akuvox_lock"; + case AUGUST_LOCK = "august_lock"; + case BRIVO_ACCESS_POINT = "brivo_access_point"; + case BUTTERFLYMX_PANEL = "butterflymx_panel"; + case AVIGILON_ALTA_ENTRY = "avigilon_alta_entry"; + case DOORKING_LOCK = "doorking_lock"; + case GENIE_DOOR = "genie_door"; + case IGLOO_LOCK = "igloo_lock"; + case LINEAR_LOCK = "linear_lock"; + case LOCKLY_LOCK = "lockly_lock"; + case KWIKSET_LOCK = "kwikset_lock"; + case NUKI_LOCK = "nuki_lock"; + case SALTO_LOCK = "salto_lock"; + case SCHLAGE_LOCK = "schlage_lock"; + case SMARTTHINGS_LOCK = "smartthings_lock"; + case WYZE_LOCK = "wyze_lock"; + case YALE_LOCK = "yale_lock"; + case TWO_N_INTERCOM = "two_n_intercom"; + case CONTROLBYWEB_DEVICE = "controlbyweb_device"; + case TTLOCK_LOCK = "ttlock_lock"; + case IGLOOHOME_LOCK = "igloohome_lock"; + case FOUR_SUITES_DOOR = "four_suites_door"; + case DORMAKABA_ORACODE_DOOR = "dormakaba_oracode_door"; + case TEDEE_LOCK = "tedee_lock"; + case AKILES_LOCK = "akiles_lock"; + case ULTRALOQ_LOCK = "ultraloq_lock"; + case YACAN_LOCK = "yacan_lock"; + case KEYINCODE_LOCK = "keyincode_lock"; + case OMNITEC_LOCK = "omnitec_lock"; + case KISI_LOCK = "kisi_lock"; + case AQARA_LOCK = "aqara_lock"; + case KEYNEST_KEY = "keynest_key"; + case NOISEAWARE_ACTIVITY_ZONE = "noiseaware_activity_zone"; + case MINUT_SENSOR = "minut_sensor"; + case ECOBEE_THERMOSTAT = "ecobee_thermostat"; + case NEST_THERMOSTAT = "nest_thermostat"; + case HONEYWELL_RESIDEO_THERMOSTAT = "honeywell_resideo_thermostat"; + case TADO_THERMOSTAT = "tado_thermostat"; + case SENSI_THERMOSTAT = "sensi_thermostat"; + case SMARTTHINGS_THERMOSTAT = "smartthings_thermostat"; + case IOS_PHONE = "ios_phone"; + case ANDROID_PHONE = "android_phone"; + case RING_CAMERA = "ring_camera"; + } } -namespace Seam\Resources\Device\Properties { +namespace Seam\Resources\Device\Errors { /** - * Accessory keypad properties and state. + * Indicates that the account is disconnected. */ - class AccessoryKeypad + final class AccountDisconnected extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): AccessoryKeypad|null + public static function from_json(mixed $json): AccountDisconnected|null { if (!$json) { return null; } return new self( - battery: isset($json->battery) - ? AccessoryKeypad\Battery::from_json($json->battery) - : null, - is_connected: $json->is_connected ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Indicates if an accessory keypad is connected to the device. + * Date and time at which Seam created the error. */ - public bool|null $is_connected, + string|null $created_at, /** - * Keypad battery properties. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public AccessoryKeypad\Battery|null $battery = null, - ) {} + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Appearance-related properties, as reported by the device. + * Indicates that the Salto site user limit has been reached. */ - class Appearance + final class SaltoKsSubscriptionLimitExceeded extends + \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): Appearance|null - { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitExceeded|null { if (!$json) { return null; } - return new self(name: $json->name ?? null); + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); } public function __construct( /** - * Name of the device as seen from the provider API and application, not settable through Seam. + * Date and time at which Seam created the error. */ - public string|null $name, - ) {} + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Represents the current status of the battery charge level. + * 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. */ - class Battery + final class InsufficientPermissions extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): Battery|null - { + public static function from_json( + mixed $json, + ): InsufficientPermissions|null { if (!$json) { return null; } return new self( - level: $json->level ?? null, - status: $json->status ?? null, - ); - } - - public function __construct( - /** - * Battery charge level as a value between 0 and 1, inclusive. + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. */ - public float|null $level, + string|null $created_at, /** - * 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. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public string|null $status, - ) {} + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Device model-related properties. + * Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. */ - class Model + final class DormakabaSitesDisconnected extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): Model|null - { + public static function from_json( + mixed $json, + ): DormakabaSitesDisconnected|null { if (!$json) { return null; } return new self( - accessory_keypad_supported: $json->accessory_keypad_supported ?? - null, - can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? - null, - display_name: $json->display_name ?? null, - has_built_in_keypad: $json->has_built_in_keypad ?? null, - manufacturer_display_name: $json->manufacturer_display_name ?? - null, - offline_access_codes_supported: $json->offline_access_codes_supported ?? - null, - online_access_codes_supported: $json->online_access_codes_supported ?? + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Display name of the device model. - */ - public string|null $display_name, - /** - * Display name that corresponds to the manufacturer-specific terminology for the device. - */ - public string|null $manufacturer_display_name, - /** - * @deprecated use device.properties.model.can_connect_accessory_keypad + * Date and time at which Seam created the error. */ - public bool|null $accessory_keypad_supported = null, + string|null $created_at, /** - * Indicates whether the device can connect a accessory keypad. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public bool|null $can_connect_accessory_keypad = null, + string|null $error_code, /** - * Indicates whether the device has a built in accessory keypad. + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. */ - public bool|null $has_built_in_keypad = null, + public true|null $is_connected_account_error, /** - * @deprecated use device.can_program_offline_access_codes. + * Indicates that the error is not a device error. */ - public bool|null $offline_access_codes_supported = null, + public false|null $is_device_error, /** - * @deprecated use device.can_program_online_access_codes. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public bool|null $online_access_codes_supported = null, - ) {} + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * ASSA ABLOY Credential Service metadata for the phone. + * Indicates that the device is offline. */ - class AssaAbloyCredentialServiceMetadata + final class DeviceOffline extends \Seam\Resources\Device\Errors { - public static function from_json( - mixed $json, - ): AssaAbloyCredentialServiceMetadata|null { + public static function from_json(mixed $json): DeviceOffline|null + { if (!$json) { return null; } return new self( - endpoints: array_map( - fn( - $e, - ) => AssaAbloyCredentialServiceMetadata\Endpoints::from_json( - $e, - ), - $json->endpoints ?? [], - ), - has_active_endpoint: $json->has_active_endpoint ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Endpoints associated with the phone. + * Date and time at which Seam created the error. */ - public array|null $endpoints = null, + string|null $created_at, /** - * Indicates whether the credential service has active endpoints associated with the phone. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public bool|null $has_active_endpoint = null, - ) {} + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Salto Space credential service metadata for the phone. + * Indicates that the device has been removed. */ - class SaltoSpaceCredentialServiceMetadata + final class DeviceRemoved extends \Seam\Resources\Device\Errors { - public static function from_json( - mixed $json, - ): SaltoSpaceCredentialServiceMetadata|null { + public static function from_json(mixed $json): DeviceRemoved|null + { if (!$json) { return null; } - return new self(has_active_phone: $json->has_active_phone ?? null); + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); } public function __construct( /** - * Indicates whether the credential service has an active associated phone. + * Date and time at which Seam created the error. */ - public bool|null $has_active_phone = null, - ) {} + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Metadata for an Akiles device. + * Indicates that the hub is disconnected. */ - class AkilesMetadata + final class HubDisconnected extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): AkilesMetadata|null + public static function from_json(mixed $json): HubDisconnected|null { if (!$json) { return null; } return new self( - _member_group_id: $json->_member_group_id ?? null, - gadget_id: $json->gadget_id ?? null, - gadget_name: $json->gadget_name ?? null, - product_name: $json->product_name ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Group ID to which to add users for an Akiles device. + * Date and time at which Seam created the error. */ - public string|null $_member_group_id = null, + string|null $created_at, /** - * Gadget ID for an Akiles device. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public string|null $gadget_id = null, + string|null $error_code, /** - * Gadget name for an Akiles device. + * Indicates that the error is a device error. */ - public string|null $gadget_name = null, + public true|null $is_device_error, /** - * Product name for an Akiles device. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $product_name = null, - ) {} + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Metadata for an Aqara device. + * Indicates that the device is disconnected. */ - class AqaraMetadata + final class DeviceDisconnected extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): AqaraMetadata|null + public static function from_json(mixed $json): DeviceDisconnected|null { if (!$json) { return null; } return new self( - device_name: $json->device_name ?? null, - did: $json->did ?? null, - firmware_version: $json->firmware_version ?? null, - model: $json->model ?? null, - model_type: $json->model_type ?? null, - parent_did: $json->parent_did ?? null, - position_id: $json->position_id ?? null, - time_zone: $json->time_zone ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Device name for an Aqara device. - */ - public string|null $device_name = null, - /** - * Device ID (did) for an Aqara device. - */ - public string|null $did = null, - /** - * Firmware version for an Aqara device. - */ - public string|null $firmware_version = null, - /** - * Model identifier for an Aqara device. - */ - public string|null $model = null, - /** - * Model type for an Aqara device. + * Date and time at which Seam created the error. */ - public float|null $model_type = null, + string|null $created_at, /** - * Parent gateway device ID for an Aqara device. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public string|null $parent_did = null, + string|null $error_code, /** - * Position (room) ID for an Aqara device. + * Indicates that the error is a device error. */ - public string|null $position_id = null, + public true|null $is_device_error, /** - * Time zone reported for an Aqara device (e.g. GMT-07:00). + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $time_zone = null, - ) {} + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Metadata for an ASSA ABLOY Vostio system. + * Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. */ - class AssaAbloyVostioMetadata + final class EmptyBackupAccessCodePool extends \Seam\Resources\Device\Errors { public static function from_json( mixed $json, - ): AssaAbloyVostioMetadata|null { + ): EmptyBackupAccessCodePool|null { if (!$json) { return null; } - return new self(encoder_name: $json->encoder_name ?? null); + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); } public function __construct( /** - * Encoder name for an ASSA ABLOY Vostio system. + * Date and time at which Seam created the error. */ - public string|null $encoder_name = null, - ) {} + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Metadata for an August device. + * Indicates that the user is not authorized to use the August lock. */ - class AugustMetadata + final class AugustLockNotAuthorized extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): AugustMetadata|null - { + public static function from_json( + mixed $json, + ): AugustLockNotAuthorized|null { if (!$json) { return null; } return new self( - has_keypad: $json->has_keypad ?? null, - house_id: $json->house_id ?? null, - house_name: $json->house_name ?? null, - keypad_battery_level: $json->keypad_battery_level ?? null, - lock_id: $json->lock_id ?? null, - lock_name: $json->lock_name ?? null, - model: $json->model ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Indicates whether an August device has a keypad. + * Date and time at which Seam created the error. */ - public bool|null $has_keypad = null, + string|null $created_at, /** - * House ID for an August device. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public string|null $house_id = null, - /** - * House name for an August device. - */ - public string|null $house_name = null, - /** - * Keypad battery level for an August device. - */ - public string|null $keypad_battery_level = null, - /** - * Lock ID for an August device. - */ - public string|null $lock_id = null, + string|null $error_code, /** - * Lock name for an August device. + * Indicates that the error is a device error. */ - public string|null $lock_name = null, + public true|null $is_device_error, /** - * Model for an August device. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $model = null, - ) {} + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Metadata for an Avigilon Alta system. + * Indicates that device credentials are missing. */ - class AvigilonAltaMetadata + final class MissingDeviceCredentials extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): AvigilonAltaMetadata|null - { + public static function from_json( + mixed $json, + ): MissingDeviceCredentials|null { if (!$json) { return null; } return new self( - entry_name: $json->entry_name ?? null, - entry_relays_total_count: $json->entry_relays_total_count ?? - null, - org_name: $json->org_name ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - zone_id: $json->zone_id ?? null, - zone_name: $json->zone_name ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Entry name for an Avigilon Alta system. - */ - public string|null $entry_name = null, - /** - * Total count of entry relays for an Avigilon Alta system. - */ - public float|null $entry_relays_total_count = null, - /** - * Organization name for an Avigilon Alta system. - */ - public string|null $org_name = null, - /** - * Site ID for an Avigilon Alta system. + * Date and time at which Seam created the error. */ - public float|null $site_id = null, + string|null $created_at, /** - * Site name for an Avigilon Alta system. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public string|null $site_name = null, + string|null $error_code, /** - * Zone ID for an Avigilon Alta system. + * Indicates that the error is a device error. */ - public float|null $zone_id = null, + public true|null $is_device_error, /** - * Zone name for an Avigilon Alta system. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $zone_name = null, - ) {} + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Metadata for a Brivo device. + * Indicates that the auxiliary heat is running. */ - class BrivoMetadata + final class AuxiliaryHeatRunning extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): BrivoMetadata|null + public static function from_json(mixed $json): AuxiliaryHeatRunning|null { if (!$json) { return null; } return new self( - activation_enabled: $json->activation_enabled ?? null, - device_name: $json->device_name ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Indicates whether the Brivo access point has activation (remote unlock) enabled. + * Date and time at which Seam created the error. */ - public bool|null $activation_enabled = null, + string|null $created_at, /** - * Device name for a Brivo device. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public string|null $device_name = null, - ) {} + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Metadata for a ControlByWeb device. + * Indicates that a subscription is required to connect. */ - class ControlbywebMetadata + final class SubscriptionRequired extends \Seam\Resources\Device\Errors { - public static function from_json(mixed $json): ControlbywebMetadata|null + public static function from_json(mixed $json): SubscriptionRequired|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - relay_name: $json->relay_name ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Device ID for a ControlByWeb device. + * Date and time at which Seam created the error. */ - public string|null $device_id = null, + string|null $created_at, /** - * Device name for a ControlByWeb device. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public string|null $device_name = null, + string|null $error_code, /** - * Relay name for a ControlByWeb device. + * Indicates that the error is a device error. */ - public string|null $relay_name = null, - ) {} + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Metadata for a dormakaba Oracode device. + * Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/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](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system-errors-seam_bridge_disconnected). */ - class DormakabaOracodeMetadata + final class BridgeDisconnected extends \Seam\Resources\Device\Errors { - public static function from_json( - mixed $json, - ): DormakabaOracodeMetadata|null { + public static function from_json(mixed $json): BridgeDisconnected|null + { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, - door_id: $json->door_id ?? null, - door_is_wireless: $json->door_is_wireless ?? null, - door_name: $json->door_name ?? null, - iana_timezone: $json->iana_timezone ?? null, - predefined_time_slots: array_map( - fn( - $p, - ) => DormakabaOracodeMetadata\PredefinedTimeSlots::from_json( - $p, - ), - $json->predefined_time_slots ?? [], - ), - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, ); } public function __construct( /** - * Device ID for a dormakaba Oracode device. - */ - public string|null $device_id = null, - /** - * Door ID for a dormakaba Oracode device. - */ - public float|null $door_id = null, - /** - * Indicates whether a door is wireless for a dormakaba Oracode device. - */ - public bool|null $door_is_wireless = null, - /** - * Door name for a dormakaba Oracode device. + * Date and time at which Seam created the error. */ - public string|null $door_name = null, + string|null $created_at, /** - * IANA time zone for a dormakaba Oracode device. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Errors\ErrorCode>|string|null */ - public string|null $iana_timezone = null, + string|null $error_code, /** - * Predefined time slots for a dormakaba Oracode device. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public array|null $predefined_time_slots = null, + string|null $message, /** - * Site ID for a dormakaba Oracode device. - * - * @deprecated Previously marked as "@DEPRECATED." + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public float|null $site_id = null, + public bool|null $is_bridge_error = null, /** - * Site name for a dormakaba Oracode device. + * Indicates whether the error is related specifically to the connected account. */ - public string|null $site_name = null, - ) {} + public bool|null $is_connected_account_error = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case ACCOUNT_DISCONNECTED = "account_disconnected"; + case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; + case INSUFFICIENT_PERMISSIONS = "insufficient_permissions"; + case DORMAKABA_SITES_DISCONNECTED = "dormakaba_sites_disconnected"; + case DEVICE_OFFLINE = "device_offline"; + case DEVICE_REMOVED = "device_removed"; + case HUB_DISCONNECTED = "hub_disconnected"; + case DEVICE_DISCONNECTED = "device_disconnected"; + case EMPTY_BACKUP_ACCESS_CODE_POOL = "empty_backup_access_code_pool"; + case AUGUST_LOCK_NOT_AUTHORIZED = "august_lock_not_authorized"; + case MISSING_DEVICE_CREDENTIALS = "missing_device_credentials"; + case AUXILIARY_HEAT_RUNNING = "auxiliary_heat_running"; + case SUBSCRIPTION_REQUIRED = "subscription_required"; + case BRIDGE_DISCONNECTED = "bridge_disconnected"; } +} +namespace Seam\Resources\Device\Properties { /** - * Metadata for an ecobee device. + * Accessory keypad properties and state. */ - class EcobeeMetadata + class AccessoryKeypad { - public static function from_json(mixed $json): EcobeeMetadata|null + public static function from_json(mixed $json): AccessoryKeypad|null { if (!$json) { return null; } return new self( - device_name: $json->device_name ?? null, - ecobee_device_id: $json->ecobee_device_id ?? null, + is_connected: $json->is_connected ?? null, + battery: isset($json->battery) + ? \Seam\Resources\Device\Properties\AccessoryKeypad\Battery::from_json( + $json->battery, + ) + : null, ); } public function __construct( /** - * Device name for an ecobee device. + * Indicates if an accessory keypad is connected to the device. */ - public string|null $device_name = null, + public bool|null $is_connected, /** - * Device ID for an ecobee device. + * Keypad battery properties. */ - public string|null $ecobee_device_id = null, + public \Seam\Resources\Device\Properties\AccessoryKeypad\Battery|null $battery = null, ) {} } /** - * Metadata for a 4SUITES device. + * Appearance-related properties, as reported by the device. */ - class FourSuitesMetadata + class Appearance { - public static function from_json(mixed $json): FourSuitesMetadata|null + public static function from_json(mixed $json): Appearance|null { if (!$json) { return null; } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - reclose_delay_in_seconds: $json->reclose_delay_in_seconds ?? - null, - ); + return new self(name: $json->name ?? null); } public function __construct( /** - * Device ID for a 4SUITES device. - */ - public float|null $device_id = null, - /** - * Device name for a 4SUITES device. - */ - public string|null $device_name = null, - /** - * Reclose delay, in seconds, for a 4SUITES device. + * Name of the device as seen from the provider API and application, not settable through Seam. */ - public float|null $reclose_delay_in_seconds = null, + public string|null $name, ) {} } /** - * Metadata for a Genie device. + * Represents the current status of the battery charge level. */ - class GenieMetadata + class Battery { - public static function from_json(mixed $json): GenieMetadata|null + public static function from_json(mixed $json): Battery|null { if (!$json) { return null; } return new self( - device_name: $json->device_name ?? null, - door_name: $json->door_name ?? null, + level: $json->level ?? null, + status: $json->status ?? null, ); } public function __construct( /** - * Lock name for a Genie device. + * Battery charge level as a value between 0 and 1, inclusive. */ - public string|null $device_name = null, + public float|null $level, /** - * Door name for a Genie device. + * 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. + * + * @var value-of<\Seam\Resources\Device\Properties\Battery\Status>|string|null */ - public string|null $door_name = null, + public string|null $status, ) {} } /** - * Metadata for a Honeywell Resideo device. + * Device model-related properties. */ - class HoneywellResideoMetadata + class Model { - public static function from_json( - mixed $json, - ): HoneywellResideoMetadata|null { + public static function from_json(mixed $json): Model|null + { if (!$json) { return null; } return new self( - device_name: $json->device_name ?? null, - honeywell_resideo_device_id: $json->honeywell_resideo_device_id ?? + display_name: $json->display_name ?? null, + manufacturer_display_name: $json->manufacturer_display_name ?? null, - ); + accessory_keypad_supported: $json->accessory_keypad_supported ?? + null, + can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? + null, + has_built_in_keypad: $json->has_built_in_keypad ?? null, + offline_access_codes_supported: $json->offline_access_codes_supported ?? + null, + online_access_codes_supported: $json->online_access_codes_supported ?? + null, + ); } public function __construct( /** - * Device name for a Honeywell Resideo device. + * Display name of the device model. */ - public string|null $device_name = null, + public string|null $display_name, /** - * Device ID for a Honeywell Resideo device. + * Display name that corresponds to the manufacturer-specific terminology for the device. */ - public string|null $honeywell_resideo_device_id = null, + public string|null $manufacturer_display_name, + /** + * @deprecated use device.properties.model.can_connect_accessory_keypad + */ + public bool|null $accessory_keypad_supported = null, + /** + * Indicates whether the device can connect a accessory keypad. + */ + public bool|null $can_connect_accessory_keypad = null, + /** + * Indicates whether the device has a built in accessory keypad. + */ + public bool|null $has_built_in_keypad = null, + /** + * @deprecated use device.can_program_offline_access_codes. + */ + public bool|null $offline_access_codes_supported = null, + /** + * @deprecated use device.can_program_online_access_codes. + */ + public bool|null $online_access_codes_supported = null, ) {} } /** - * Metadata for an igloo device. + * ASSA ABLOY Credential Service metadata for the phone. */ - class IglooMetadata + class AssaAbloyCredentialServiceMetadata { - public static function from_json(mixed $json): IglooMetadata|null - { + public static function from_json( + mixed $json, + ): AssaAbloyCredentialServiceMetadata|null { if (!$json) { return null; } return new self( - bridge_id: $json->bridge_id ?? null, - device_id: $json->device_id ?? null, - model: $json->model ?? null, + endpoints: array_map( + fn( + $e, + ) => \Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata\Endpoints::from_json( + $e, + ), + $json->endpoints ?? [], + ), + has_active_endpoint: $json->has_active_endpoint ?? null, ); } public function __construct( /** - * Bridge ID for an igloo device. + * Endpoints associated with the phone. + * + * @var list<\Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata\Endpoints>|null */ - public string|null $bridge_id = null, + public array|null $endpoints = null, /** - * Device ID for an igloo device. + * Indicates whether the credential service has active endpoints associated with the phone. */ - public string|null $device_id = null, + public bool|null $has_active_endpoint = null, + ) {} + } + + /** + * Salto Space credential service metadata for the phone. + */ + class SaltoSpaceCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): SaltoSpaceCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self(has_active_phone: $json->has_active_phone ?? null); + } + + public function __construct( /** - * Model for an igloo device. + * Indicates whether the credential service has an active associated phone. */ - public string|null $model = null, + public bool|null $has_active_phone = null, ) {} } /** - * Metadata for an igloohome device. + * Metadata for an Akiles device. */ - class IgloohomeMetadata + class AkilesMetadata { - public static function from_json(mixed $json): IgloohomeMetadata|null + public static function from_json(mixed $json): AkilesMetadata|null { if (!$json) { return null; } return new self( - bridge_id: $json->bridge_id ?? null, - bridge_name: $json->bridge_name ?? null, - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - is_accessory_keypad_linked_to_bridge: $json->is_accessory_keypad_linked_to_bridge ?? - null, - keypad_id: $json->keypad_id ?? null, + _member_group_id: $json->_member_group_id ?? null, + gadget_id: $json->gadget_id ?? null, + gadget_name: $json->gadget_name ?? null, + product_name: $json->product_name ?? null, ); } public function __construct( /** - * Bridge ID for an igloohome device. - */ - public string|null $bridge_id = null, - /** - * Bridge name for an igloohome device. - */ - public string|null $bridge_name = null, - /** - * Device ID for an igloohome device. + * Group ID to which to add users for an Akiles device. */ - public string|null $device_id = null, + public string|null $_member_group_id = null, /** - * Device name for an igloohome device. + * Gadget ID for an Akiles device. */ - public string|null $device_name = null, + public string|null $gadget_id = null, /** - * Indicates whether a keypad is linked to a bridge for an igloohome device. + * Gadget name for an Akiles device. */ - public bool|null $is_accessory_keypad_linked_to_bridge = null, + public string|null $gadget_name = null, /** - * Keypad ID for an igloohome device. + * Product name for an Akiles device. */ - public string|null $keypad_id = null, + public string|null $product_name = null, ) {} } /** - * Metadata for a KeyNest device. + * Metadata for an Aqara device. */ - class KeynestMetadata + class AqaraMetadata { - public static function from_json(mixed $json): KeynestMetadata|null + public static function from_json(mixed $json): AqaraMetadata|null { if (!$json) { return null; } return new self( - address: $json->address ?? null, - current_or_last_store_id: $json->current_or_last_store_id ?? - null, - current_status: $json->current_status ?? null, - current_user_company: $json->current_user_company ?? null, - current_user_email: $json->current_user_email ?? null, - current_user_name: $json->current_user_name ?? null, - current_user_phone_number: $json->current_user_phone_number ?? - null, - default_office_id: $json->default_office_id ?? null, device_name: $json->device_name ?? null, - fob_id: $json->fob_id ?? null, - handover_method: $json->handover_method ?? null, - has_photo: $json->has_photo ?? null, - is_quadient_locker: $json->is_quadient_locker ?? null, - key_id: $json->key_id ?? null, - key_notes: $json->key_notes ?? null, - keynest_app_user: $json->keynest_app_user ?? null, - last_movement: $json->last_movement ?? null, - property_id: $json->property_id ?? null, - property_postcode: $json->property_postcode ?? null, - status_type: $json->status_type ?? null, - subscription_plan: $json->subscription_plan ?? null, + did: $json->did ?? null, + firmware_version: $json->firmware_version ?? null, + model: $json->model ?? null, + model_type: $json->model_type ?? null, + parent_did: $json->parent_did ?? null, + position_id: $json->position_id ?? null, + time_zone: $json->time_zone ?? null, ); } public function __construct( /** - * Address for a KeyNest device. - */ - public string|null $address = null, - /** - * Current or last store ID for a KeyNest device. - */ - public float|null $current_or_last_store_id = null, - /** - * Current status for a KeyNest device. - */ - public string|null $current_status = null, - /** - * Current user company for a KeyNest device. - */ - public string|null $current_user_company = null, - /** - * Current user email for a KeyNest device. - */ - public string|null $current_user_email = null, - /** - * Current user name for a KeyNest device. - */ - public string|null $current_user_name = null, - /** - * Current user phone number for a KeyNest device. - */ - public string|null $current_user_phone_number = null, - /** - * Default office ID for a KeyNest device. - */ - public float|null $default_office_id = null, - /** - * Device name for a KeyNest device. + * Device name for an Aqara device. */ public string|null $device_name = null, /** - * Fob ID for a KeyNest device. - */ - public float|null $fob_id = null, - /** - * Handover method for a KeyNest device. - */ - public string|null $handover_method = null, - /** - * Whether the KeyNest device has a photo. - */ - public bool|null $has_photo = null, - /** - * Whether the key is in a locker that does not support the access codes API. - */ - public bool|null $is_quadient_locker = null, - /** - * Key ID for a KeyNest device. - */ - public string|null $key_id = null, - /** - * Key notes for a KeyNest device. + * Device ID (did) for an Aqara device. */ - public string|null $key_notes = null, + public string|null $did = null, /** - * KeyNest app user for a KeyNest device. + * Firmware version for an Aqara device. */ - public string|null $keynest_app_user = null, + public string|null $firmware_version = null, /** - * Last movement timestamp for a KeyNest device. + * Model identifier for an Aqara device. */ - public string|null $last_movement = null, + public string|null $model = null, /** - * Property ID for a KeyNest device. + * Model type for an Aqara device. */ - public string|null $property_id = null, + public float|null $model_type = null, /** - * Property postcode for a KeyNest device. + * Parent gateway device ID for an Aqara device. */ - public string|null $property_postcode = null, + public string|null $parent_did = null, /** - * Status type for a KeyNest device. + * Position (room) ID for an Aqara device. */ - public string|null $status_type = null, + public string|null $position_id = null, /** - * Subscription plan for a KeyNest device. + * Time zone reported for an Aqara device (e.g. GMT-07:00). */ - public string|null $subscription_plan = null, + public string|null $time_zone = null, ) {} } /** - * Metadata for a Kisi device. + * Metadata for an ASSA ABLOY Vostio system. */ - class KisiMetadata + class AssaAbloyVostioMetadata { - public static function from_json(mixed $json): KisiMetadata|null - { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { if (!$json) { return null; } - return new self( - description: $json->description ?? null, - lock_id: $json->lock_id ?? null, - lock_name: $json->lock_name ?? null, - place_name: $json->place_name ?? null, - ); + return new self(encoder_name: $json->encoder_name ?? null); } public function __construct( /** - * Description for a Kisi device. - */ - public string|null $description = null, - /** - * Lock ID for a Kisi device. - */ - public float|null $lock_id = null, - /** - * Lock name for a Kisi device. - */ - public string|null $lock_name = null, - /** - * Place name for a Kisi device. + * Encoder name for an ASSA ABLOY Vostio system. */ - public string|null $place_name = null, + public string|null $encoder_name = null, ) {} } /** - * Metadata for a Korelock device. + * Metadata for an August device. */ - class KorelockMetadata + class AugustMetadata { - public static function from_json(mixed $json): KorelockMetadata|null + public static function from_json(mixed $json): AugustMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - firmware_version: $json->firmware_version ?? null, - location_id: $json->location_id ?? null, - model_code: $json->model_code ?? null, - serial_number: $json->serial_number ?? null, - wifi_signal_strength: $json->wifi_signal_strength ?? null, + has_keypad: $json->has_keypad ?? null, + house_id: $json->house_id ?? null, + house_name: $json->house_name ?? null, + keypad_battery_level: $json->keypad_battery_level ?? null, + lock_id: $json->lock_id ?? null, + lock_name: $json->lock_name ?? null, + model: $json->model ?? null, ); } public function __construct( /** - * Device ID for a Korelock device. + * Indicates whether an August device has a keypad. */ - public string|null $device_id = null, + public bool|null $has_keypad = null, /** - * Device name for a Korelock device. + * House ID for an August device. */ - public string|null $device_name = null, + public string|null $house_id = null, /** - * Firmware version for a Korelock device. + * House name for an August device. */ - public string|null $firmware_version = null, + public string|null $house_name = null, /** - * Location ID for a Korelock device. Required for timebound access codes. + * Keypad battery level for an August device. */ - public string|null $location_id = null, + public string|null $keypad_battery_level = null, /** - * Model code for a Korelock device. + * Lock ID for an August device. */ - public string|null $model_code = null, + public string|null $lock_id = null, /** - * Serial number for a Korelock device. + * Lock name for an August device. */ - public string|null $serial_number = null, + public string|null $lock_name = null, /** - * WiFi signal strength (0-1) for a Korelock device. + * Model for an August device. */ - public float|null $wifi_signal_strength = null, + public string|null $model = null, ) {} } /** - * Metadata for a Kwikset device. + * Metadata for an Avigilon Alta system. */ - class KwiksetMetadata + class AvigilonAltaMetadata { - public static function from_json(mixed $json): KwiksetMetadata|null + public static function from_json(mixed $json): AvigilonAltaMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - model_number: $json->model_number ?? null, + entry_name: $json->entry_name ?? null, + entry_relays_total_count: $json->entry_relays_total_count ?? + null, + org_name: $json->org_name ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + zone_id: $json->zone_id ?? null, + zone_name: $json->zone_name ?? null, ); } public function __construct( /** - * Device ID for a Kwikset device. + * Entry name for an Avigilon Alta system. */ - public string|null $device_id = null, + public string|null $entry_name = null, /** - * Device name for a Kwikset device. + * Total count of entry relays for an Avigilon Alta system. */ - public string|null $device_name = null, + public float|null $entry_relays_total_count = null, /** - * Model number for a Kwikset device. + * Organization name for an Avigilon Alta system. */ - public string|null $model_number = null, + public string|null $org_name = null, + /** + * Site ID for an Avigilon Alta system. + */ + public float|null $site_id = null, + /** + * Site name for an Avigilon Alta system. + */ + public string|null $site_name = null, + /** + * Zone ID for an Avigilon Alta system. + */ + public float|null $zone_id = null, + /** + * Zone name for an Avigilon Alta system. + */ + public string|null $zone_name = null, ) {} } /** - * Metadata for a Lockly device. + * Metadata for a Brivo device. */ - class LocklyMetadata + class BrivoMetadata { - public static function from_json(mixed $json): LocklyMetadata|null + public static function from_json(mixed $json): BrivoMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, + activation_enabled: $json->activation_enabled ?? null, device_name: $json->device_name ?? null, - model: $json->model ?? null, ); } public function __construct( /** - * Device ID for a Lockly device. + * Indicates whether the Brivo access point has activation (remote unlock) enabled. */ - public string|null $device_id = null, + public bool|null $activation_enabled = null, /** - * Device name for a Lockly device. + * Device name for a Brivo device. */ public string|null $device_name = null, - /** - * Model for a Lockly device. - */ - public string|null $model = null, ) {} } /** - * Metadata for a Minut device. + * Metadata for a ControlByWeb device. */ - class MinutMetadata + class ControlbywebMetadata { - public static function from_json(mixed $json): MinutMetadata|null + public static function from_json(mixed $json): ControlbywebMetadata|null { if (!$json) { return null; @@ -2254,127 +2682,130 @@ public static function from_json(mixed $json): MinutMetadata|null return new self( device_id: $json->device_id ?? null, device_name: $json->device_name ?? null, - latest_sensor_values: isset($json->latest_sensor_values) - ? MinutMetadata\LatestSensorValues::from_json( - $json->latest_sensor_values, - ) - : null, + relay_name: $json->relay_name ?? null, ); } public function __construct( /** - * Device ID for a Minut device. + * Device ID for a ControlByWeb device. */ public string|null $device_id = null, /** - * Device name for a Minut device. + * Device name for a ControlByWeb device. */ public string|null $device_name = null, /** - * Latest sensor values for a Minut device. + * Relay name for a ControlByWeb device. */ - public MinutMetadata\LatestSensorValues|null $latest_sensor_values = null, + public string|null $relay_name = null, ) {} } /** - * Metadata for a Google Nest device. + * Metadata for a dormakaba Oracode device. */ - class NestMetadata + class DormakabaOracodeMetadata { - public static function from_json(mixed $json): NestMetadata|null - { + public static function from_json( + mixed $json, + ): DormakabaOracodeMetadata|null { if (!$json) { return null; } return new self( - device_custom_name: $json->device_custom_name ?? null, - device_name: $json->device_name ?? null, - display_name: $json->display_name ?? null, - nest_device_id: $json->nest_device_id ?? null, - nest_structure_id: $json->nest_structure_id ?? null, - structure_name: $json->structure_name ?? null, + device_id: $json->device_id ?? null, + door_id: $json->door_id ?? null, + door_is_wireless: $json->door_is_wireless ?? null, + door_name: $json->door_name ?? null, + iana_timezone: $json->iana_timezone ?? null, + predefined_time_slots: array_map( + fn( + $p, + ) => \Seam\Resources\Device\Properties\DormakabaOracodeMetadata\PredefinedTimeSlots::from_json( + $p, + ), + $json->predefined_time_slots ?? [], + ), + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, ); } public function __construct( /** - * Custom device name for a Google Nest device. The device owner sets this value. + * Device ID for a dormakaba Oracode device. */ - public string|null $device_custom_name = null, + public string|null $device_id = null, /** - * Device name for a Google Nest device. Google sets this value. + * Door ID for a dormakaba Oracode device. */ - public string|null $device_name = null, + public float|null $door_id = null, /** - * Display name for a Google Nest device. + * Indicates whether a door is wireless for a dormakaba Oracode device. */ - public string|null $display_name = null, + public bool|null $door_is_wireless = null, /** - * Device ID for a Google Nest device. + * Door name for a dormakaba Oracode device. */ - public string|null $nest_device_id = null, + public string|null $door_name = null, /** - * ID of the Google Nest structure containing the device. + * IANA time zone for a dormakaba Oracode device. */ - public string|null $nest_structure_id = null, + public string|null $iana_timezone = null, /** - * Name of the Google Nest structure containing the device. The device owner sets this value. + * Predefined time slots for a dormakaba Oracode device. + * + * @var list<\Seam\Resources\Device\Properties\DormakabaOracodeMetadata\PredefinedTimeSlots>|null */ - public string|null $structure_name = null, + public array|null $predefined_time_slots = null, + /** + * Site ID for a dormakaba Oracode device. + * + * @deprecated Previously marked as "@DEPRECATED." + */ + public float|null $site_id = null, + /** + * Site name for a dormakaba Oracode device. + */ + public string|null $site_name = null, ) {} } /** - * Metadata for a NoiseAware device. + * Metadata for an ecobee device. */ - class NoiseawareMetadata + class EcobeeMetadata { - public static function from_json(mixed $json): NoiseawareMetadata|null + public static function from_json(mixed $json): EcobeeMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, - device_model: $json->device_model ?? null, device_name: $json->device_name ?? null, - noise_level_decibel: $json->noise_level_decibel ?? null, - noise_level_nrs: $json->noise_level_nrs ?? null, + ecobee_device_id: $json->ecobee_device_id ?? null, ); } public function __construct( /** - * Device ID for a NoiseAware device. - */ - public string|null $device_id = null, - /** - * Device model for a NoiseAware device. - */ - public string|null $device_model = null, - /** - * Device name for a NoiseAware device. + * Device name for an ecobee device. */ public string|null $device_name = null, /** - * Noise level, in decibels, for a NoiseAware device. - */ - public float|null $noise_level_decibel = null, - /** - * Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. + * Device ID for an ecobee device. */ - public float|null $noise_level_nrs = null, + public string|null $ecobee_device_id = null, ) {} } /** - * Metadata for a Nuki device. + * Metadata for a 4SUITES device. */ - class NukiMetadata + class FourSuitesMetadata { - public static function from_json(mixed $json): NukiMetadata|null + public static function from_json(mixed $json): FourSuitesMetadata|null { if (!$json) { return null; @@ -2382,358 +2813,336 @@ public static function from_json(mixed $json): NukiMetadata|null return new self( device_id: $json->device_id ?? null, device_name: $json->device_name ?? null, - keypad_2_paired: $json->keypad_2_paired ?? null, - keypad_battery_critical: $json->keypad_battery_critical ?? null, - keypad_paired: $json->keypad_paired ?? null, - ); + reclose_delay_in_seconds: $json->reclose_delay_in_seconds ?? + null, + ); } public function __construct( /** - * Device ID for a Nuki device. + * Device ID for a 4SUITES device. */ - public string|null $device_id = null, + public float|null $device_id = null, /** - * Device name for a Nuki device. + * Device name for a 4SUITES device. */ public string|null $device_name = null, /** - * Indicates whether keypad 2 is paired for a Nuki device. - */ - public bool|null $keypad_2_paired = null, - /** - * Indicates whether the keypad battery is in a critical state for a Nuki device. - */ - public bool|null $keypad_battery_critical = null, - /** - * Indicates whether the keypad is paired for a Nuki device. + * Reclose delay, in seconds, for a 4SUITES device. */ - public bool|null $keypad_paired = null, + public float|null $reclose_delay_in_seconds = null, ) {} } /** - * Metadata for an Omnitec device. + * Metadata for a Genie device. */ - class OmnitecMetadata + class GenieMetadata { - public static function from_json(mixed $json): OmnitecMetadata|null + public static function from_json(mixed $json): GenieMetadata|null { if (!$json) { return null; } return new self( - has_gateway: $json->has_gateway ?? null, - lock_alias: $json->lock_alias ?? null, - lock_id: $json->lock_id ?? null, - lock_mac: $json->lock_mac ?? null, - lock_name: $json->lock_name ?? null, - time_zone: $json->time_zone ?? null, - timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, + device_name: $json->device_name ?? null, + door_name: $json->door_name ?? null, ); } public function __construct( /** - * Whether the Omnitec lock has a connected gateway for remote operations. - */ - public bool|null $has_gateway = null, - /** - * Operator-assigned alias for an Omnitec device. - */ - public string|null $lock_alias = null, - /** - * Lock ID for an Omnitec device. - */ - public float|null $lock_id = null, - /** - * Bluetooth MAC address for an Omnitec device. - */ - public string|null $lock_mac = null, - /** - * Lock name for an Omnitec device. - */ - public string|null $lock_name = null, - /** - * IANA time zone for the Omnitec device, used to schedule time-bound access codes at the correct local time (accounting for DST). + * Lock name for a Genie device. */ - public string|null $time_zone = null, + public string|null $device_name = null, /** - * Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. + * Door name for a Genie device. */ - public float|null $timezone_raw_offset_ms = null, + public string|null $door_name = null, ) {} } /** - * Metadata for a Ring device. + * Metadata for a Honeywell Resideo device. */ - class RingMetadata + class HoneywellResideoMetadata { - public static function from_json(mixed $json): RingMetadata|null - { + public static function from_json( + mixed $json, + ): HoneywellResideoMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, device_name: $json->device_name ?? null, + honeywell_resideo_device_id: $json->honeywell_resideo_device_id ?? + null, ); } public function __construct( /** - * Device ID for a Ring device. + * Device name for a Honeywell Resideo device. */ - public string|null $device_id = null, + public string|null $device_name = null, /** - * Device name for a Ring device. + * Device ID for a Honeywell Resideo device. */ - public string|null $device_name = null, + public string|null $honeywell_resideo_device_id = null, ) {} } /** - * Metadata for a Salto KS device. + * Metadata for an igloo device. */ - class SaltoKsMetadata + class IglooMetadata { - public static function from_json(mixed $json): SaltoKsMetadata|null + public static function from_json(mixed $json): IglooMetadata|null { if (!$json) { return null; } return new self( - battery_level: $json->battery_level ?? null, - customer_reference: $json->customer_reference ?? null, - has_custom_pin_subscription: $json->has_custom_pin_subscription ?? - null, - lock_id: $json->lock_id ?? null, - lock_type: $json->lock_type ?? null, - locked_state: $json->locked_state ?? null, + bridge_id: $json->bridge_id ?? null, + device_id: $json->device_id ?? null, model: $json->model ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, ); } public function __construct( /** - * Battery level for a Salto KS device. - */ - public string|null $battery_level = null, - /** - * Customer reference for a Salto KS device. - */ - public string|null $customer_reference = null, - /** - * Indicates whether the site has a Salto KS subscription that supports custom PINs. - */ - public bool|null $has_custom_pin_subscription = null, - /** - * Lock ID for a Salto KS device. - */ - public string|null $lock_id = null, - /** - * Lock type for a Salto KS device. + * Bridge ID for an igloo device. */ - public string|null $lock_type = null, + public string|null $bridge_id = null, /** - * Locked state for a Salto KS device. + * Device ID for an igloo device. */ - public string|null $locked_state = null, + public string|null $device_id = null, /** - * Model for a Salto KS device. + * Model for an igloo device. */ public string|null $model = null, - /** - * Site ID for the Salto KS site to which the device belongs. - */ - public string|null $site_id = null, - /** - * Site name for the Salto KS site to which the device belongs. - */ - public string|null $site_name = null, ) {} } /** - * Metada for a Salto device. - * - * @deprecated Use `salto_ks_metadata` instead. + * Metadata for an igloohome device. */ - class SaltoMetadata + class IgloohomeMetadata { - public static function from_json(mixed $json): SaltoMetadata|null + public static function from_json(mixed $json): IgloohomeMetadata|null { if (!$json) { return null; } return new self( - battery_level: $json->battery_level ?? null, - customer_reference: $json->customer_reference ?? null, - lock_id: $json->lock_id ?? null, - lock_type: $json->lock_type ?? null, - locked_state: $json->locked_state ?? null, - model: $json->model ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, + bridge_id: $json->bridge_id ?? null, + bridge_name: $json->bridge_name ?? null, + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + is_accessory_keypad_linked_to_bridge: $json->is_accessory_keypad_linked_to_bridge ?? + null, + keypad_id: $json->keypad_id ?? null, ); } public function __construct( /** - * Battery level for a Salto device. - */ - public string|null $battery_level = null, - /** - * Customer reference for a Salto device. - */ - public string|null $customer_reference = null, - /** - * Lock ID for a Salto device. + * Bridge ID for an igloohome device. */ - public string|null $lock_id = null, + public string|null $bridge_id = null, /** - * Lock type for a Salto device. + * Bridge name for an igloohome device. */ - public string|null $lock_type = null, + public string|null $bridge_name = null, /** - * Locked state for a Salto device. + * Device ID for an igloohome device. */ - public string|null $locked_state = null, + public string|null $device_id = null, /** - * Model for a Salto device. + * Device name for an igloohome device. */ - public string|null $model = null, + public string|null $device_name = null, /** - * Site ID for the Salto KS site to which the device belongs. + * Indicates whether a keypad is linked to a bridge for an igloohome device. */ - public string|null $site_id = null, + public bool|null $is_accessory_keypad_linked_to_bridge = null, /** - * Site name for the Salto KS site to which the device belongs. + * Keypad ID for an igloohome device. */ - public string|null $site_name = null, + public string|null $keypad_id = null, ) {} } /** - * Metadata for a Schlage device. + * Metadata for a KeyNest device. */ - class SchlageMetadata + class KeynestMetadata { - public static function from_json(mixed $json): SchlageMetadata|null + public static function from_json(mixed $json): KeynestMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, + address: $json->address ?? null, + current_or_last_store_id: $json->current_or_last_store_id ?? + null, + current_status: $json->current_status ?? null, + current_user_company: $json->current_user_company ?? null, + current_user_email: $json->current_user_email ?? null, + current_user_name: $json->current_user_name ?? null, + current_user_phone_number: $json->current_user_phone_number ?? + null, + default_office_id: $json->default_office_id ?? null, device_name: $json->device_name ?? null, - model: $json->model ?? null, + fob_id: $json->fob_id ?? null, + handover_method: $json->handover_method ?? null, + has_photo: $json->has_photo ?? null, + is_quadient_locker: $json->is_quadient_locker ?? null, + key_id: $json->key_id ?? null, + key_notes: $json->key_notes ?? null, + keynest_app_user: $json->keynest_app_user ?? null, + last_movement: $json->last_movement ?? null, + property_id: $json->property_id ?? null, + property_postcode: $json->property_postcode ?? null, + status_type: $json->status_type ?? null, + subscription_plan: $json->subscription_plan ?? null, ); } public function __construct( /** - * Device ID for a Schlage device. + * Address for a KeyNest device. */ - public string|null $device_id = null, + public string|null $address = null, /** - * Device name for a Schlage device. + * Current or last store ID for a KeyNest device. */ - public string|null $device_name = null, + public float|null $current_or_last_store_id = null, /** - * Model for a Schlage device. + * Current status for a KeyNest device. */ - public string|null $model = null, - ) {} - } - - /** - * Metadata for Seam Bridge. - */ - class SeamBridgeMetadata - { - public static function from_json(mixed $json): SeamBridgeMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_num: $json->device_num ?? null, - name: $json->name ?? null, - unlock_method: $json->unlock_method ?? null, - ); - } - - public function __construct( + public string|null $current_status = null, /** - * Device number for Seam Bridge. + * Current user company for a KeyNest device. */ - public float|null $device_num = null, + public string|null $current_user_company = null, /** - * Name for Seam Bridge. + * Current user email for a KeyNest device. */ - public string|null $name = null, + public string|null $current_user_email = null, /** - * Unlock method for Seam Bridge. + * Current user name for a KeyNest device. */ - public string|null $unlock_method = null, + public string|null $current_user_name = null, + /** + * Current user phone number for a KeyNest device. + */ + public string|null $current_user_phone_number = null, + /** + * Default office ID for a KeyNest device. + */ + public float|null $default_office_id = null, + /** + * Device name for a KeyNest device. + */ + public string|null $device_name = null, + /** + * Fob ID for a KeyNest device. + */ + public float|null $fob_id = null, + /** + * Handover method for a KeyNest device. + */ + public string|null $handover_method = null, + /** + * Whether the KeyNest device has a photo. + */ + public bool|null $has_photo = null, + /** + * Whether the key is in a locker that does not support the access codes API. + */ + public bool|null $is_quadient_locker = null, + /** + * Key ID for a KeyNest device. + */ + public string|null $key_id = null, + /** + * Key notes for a KeyNest device. + */ + public string|null $key_notes = null, + /** + * KeyNest app user for a KeyNest device. + */ + public string|null $keynest_app_user = null, + /** + * Last movement timestamp for a KeyNest device. + */ + public string|null $last_movement = null, + /** + * Property ID for a KeyNest device. + */ + public string|null $property_id = null, + /** + * Property postcode for a KeyNest device. + */ + public string|null $property_postcode = null, + /** + * Status type for a KeyNest device. + */ + public string|null $status_type = null, + /** + * Subscription plan for a KeyNest device. + */ + public string|null $subscription_plan = null, ) {} } /** - * Metadata for a Sensi device. + * Metadata for a Kisi device. */ - class SensiMetadata + class KisiMetadata { - public static function from_json(mixed $json): SensiMetadata|null + public static function from_json(mixed $json): KisiMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - dual_setpoints_not_supported: $json->dual_setpoints_not_supported ?? - null, - enforced_setpoint_range_celsius: $json->enforced_setpoint_range_celsius ?? - null, - product_type: $json->product_type ?? null, + description: $json->description ?? null, + lock_id: $json->lock_id ?? null, + lock_name: $json->lock_name ?? null, + place_name: $json->place_name ?? null, ); } public function __construct( /** - * Device ID for a Sensi device. - */ - public string|null $device_id = null, - /** - * Device name for a Sensi device. + * Description for a Kisi device. */ - public string|null $device_name = null, + public string|null $description = null, /** - * Set to true when the device does not support the /dual-setpoints API endpoint. + * Lock ID for a Kisi device. */ - public bool|null $dual_setpoints_not_supported = null, + public float|null $lock_id = null, /** - * Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. + * Lock name for a Kisi device. */ - public array|null $enforced_setpoint_range_celsius = null, + public string|null $lock_name = null, /** - * Product type for a Sensi device. + * Place name for a Kisi device. */ - public string|null $product_type = null, + public string|null $place_name = null, ) {} } /** - * Metadata for a SmartThings device. + * Metadata for a Korelock device. */ - class SmartthingsMetadata + class KorelockMetadata { - public static function from_json(mixed $json): SmartthingsMetadata|null + public static function from_json(mixed $json): KorelockMetadata|null { if (!$json) { return null; @@ -2741,1719 +3150,3735 @@ public static function from_json(mixed $json): SmartthingsMetadata|null return new self( device_id: $json->device_id ?? null, device_name: $json->device_name ?? null, + firmware_version: $json->firmware_version ?? null, location_id: $json->location_id ?? null, - model: $json->model ?? null, + model_code: $json->model_code ?? null, + serial_number: $json->serial_number ?? null, + wifi_signal_strength: $json->wifi_signal_strength ?? null, ); } public function __construct( /** - * Device ID for a SmartThings device. + * Device ID for a Korelock device. */ public string|null $device_id = null, /** - * Device name for a SmartThings device. + * Device name for a Korelock device. */ public string|null $device_name = null, /** - * Location ID for a SmartThings device. + * Firmware version for a Korelock device. + */ + public string|null $firmware_version = null, + /** + * Location ID for a Korelock device. Required for timebound access codes. */ public string|null $location_id = null, /** - * Model for a SmartThings device. + * Model code for a Korelock device. */ - public string|null $model = null, + public string|null $model_code = null, + /** + * Serial number for a Korelock device. + */ + public string|null $serial_number = null, + /** + * WiFi signal strength (0-1) for a Korelock device. + */ + public float|null $wifi_signal_strength = null, ) {} } /** - * Metadata for a tado° device. + * Metadata for a Kwikset device. */ - class TadoMetadata + class KwiksetMetadata { - public static function from_json(mixed $json): TadoMetadata|null + public static function from_json(mixed $json): KwiksetMetadata|null { if (!$json) { return null; } return new self( - device_type: $json->device_type ?? null, - serial_no: $json->serial_no ?? null, + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + model_number: $json->model_number ?? null, ); } public function __construct( /** - * Device type for a tado° device. + * Device ID for a Kwikset device. */ - public string|null $device_type = null, + public string|null $device_id = null, /** - * Serial number for a tado° device. + * Device name for a Kwikset device. */ - public string|null $serial_no = null, + public string|null $device_name = null, + /** + * Model number for a Kwikset device. + */ + public string|null $model_number = null, ) {} } /** - * Metadata for a Tedee device. + * Metadata for a Lockly device. */ - class TedeeMetadata + class LocklyMetadata { - public static function from_json(mixed $json): TedeeMetadata|null + public static function from_json(mixed $json): LocklyMetadata|null { if (!$json) { return null; } return new self( - bridge_id: $json->bridge_id ?? null, - bridge_name: $json->bridge_name ?? null, device_id: $json->device_id ?? null, - device_model: $json->device_model ?? null, device_name: $json->device_name ?? null, - keypad_id: $json->keypad_id ?? null, - serial_number: $json->serial_number ?? null, + model: $json->model ?? null, ); } public function __construct( /** - * Bridge ID for a Tedee device. - */ - public float|null $bridge_id = null, - /** - * Bridge name for a Tedee device. - */ - public string|null $bridge_name = null, - /** - * Device ID for a Tedee device. - */ - public float|null $device_id = null, - /** - * Device model for a Tedee device. + * Device ID for a Lockly device. */ - public string|null $device_model = null, + public string|null $device_id = null, /** - * Device name for a Tedee device. + * Device name for a Lockly device. */ public string|null $device_name = null, /** - * Keypad ID for a Tedee device. - */ - public float|null $keypad_id = null, - /** - * Serial number for a Tedee device. + * Model for a Lockly device. */ - public string|null $serial_number = null, + public string|null $model = null, ) {} } /** - * Metadata for a TTLock device. + * Metadata for a Minut device. */ - class TtlockMetadata + class MinutMetadata { - public static function from_json(mixed $json): TtlockMetadata|null + public static function from_json(mixed $json): MinutMetadata|null { if (!$json) { return null; } return new self( - feature_value: $json->feature_value ?? null, - features: isset($json->features) - ? TtlockMetadata\Features::from_json($json->features) + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + latest_sensor_values: isset($json->latest_sensor_values) + ? \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues::from_json( + $json->latest_sensor_values, + ) : null, - has_gateway: $json->has_gateway ?? null, - lock_alias: $json->lock_alias ?? null, - lock_id: $json->lock_id ?? null, - timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, - wireless_keypads: array_map( - fn($w) => TtlockMetadata\WirelessKeypads::from_json($w), - $json->wireless_keypads ?? [], - ), ); } public function __construct( /** - * Feature value for a TTLock device. - */ - public string|null $feature_value = null, - /** - * Features for a TTLock device. - */ - public TtlockMetadata\Features|null $features = null, - /** - * Indicates whether a TTLock device has a gateway. - */ - public bool|null $has_gateway = null, - /** - * Lock alias for a TTLock device. - */ - public string|null $lock_alias = null, - /** - * Lock ID for a TTLock device. + * Device ID for a Minut device. */ - public float|null $lock_id = null, + public string|null $device_id = null, /** - * Lock-side timezone offset in milliseconds east of UTC, as configured in the TTLock app. Source of truth for the lock's wall-clock interpretation of access code start/end times — a misconfigured value here is the typical cause of customer "codes offset by N hours" reports. Diagnostic only; Seam does not convert times based on this value. + * Device name for a Minut device. */ - public float|null $timezone_raw_offset_ms = null, + public string|null $device_name = null, /** - * Wireless keypads for a TTLock device. + * Latest sensor values for a Minut device. */ - public array|null $wireless_keypads = null, + public \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues|null $latest_sensor_values = null, ) {} } /** - * Metadata for a 2N device. + * Metadata for a Google Nest device. */ - class TwoNMetadata + class NestMetadata { - public static function from_json(mixed $json): TwoNMetadata|null + public static function from_json(mixed $json): NestMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, + device_custom_name: $json->device_custom_name ?? null, device_name: $json->device_name ?? null, - ); - } + display_name: $json->display_name ?? null, + nest_device_id: $json->nest_device_id ?? null, + nest_structure_id: $json->nest_structure_id ?? null, + structure_name: $json->structure_name ?? null, + ); + } public function __construct( /** - * Device ID for a 2N device. + * Custom device name for a Google Nest device. The device owner sets this value. */ - public float|null $device_id = null, + public string|null $device_custom_name = null, /** - * Device name for a 2N device. + * Device name for a Google Nest device. Google sets this value. */ public string|null $device_name = null, + /** + * Display name for a Google Nest device. + */ + public string|null $display_name = null, + /** + * Device ID for a Google Nest device. + */ + public string|null $nest_device_id = null, + /** + * ID of the Google Nest structure containing the device. + */ + public string|null $nest_structure_id = null, + /** + * Name of the Google Nest structure containing the device. The device owner sets this value. + */ + public string|null $structure_name = null, ) {} } /** - * Metadata for an Ultraloq device. + * Metadata for a NoiseAware device. */ - class UltraloqMetadata + class NoiseawareMetadata { - public static function from_json(mixed $json): UltraloqMetadata|null + public static function from_json(mixed $json): NoiseawareMetadata|null { if (!$json) { return null; } return new self( device_id: $json->device_id ?? null, + device_model: $json->device_model ?? null, device_name: $json->device_name ?? null, - device_type: $json->device_type ?? null, - time_zone: $json->time_zone ?? null, + noise_level_decibel: $json->noise_level_decibel ?? null, + noise_level_nrs: $json->noise_level_nrs ?? null, ); } public function __construct( /** - * Device ID for an Ultraloq device. + * Device ID for a NoiseAware device. */ public string|null $device_id = null, /** - * Device name for an Ultraloq device. + * Device model for a NoiseAware device. + * + * @var value-of<\Seam\Resources\Device\Properties\NoiseawareMetadata\DeviceModel>|string|null */ - public string|null $device_name = null, + public string|null $device_model = null, /** - * Device type for an Ultraloq device. + * Device name for a NoiseAware device. */ - public string|null $device_type = null, + public string|null $device_name = null, /** - * IANA timezone for the Ultraloq device. + * Noise level, in decibels, for a NoiseAware device. */ - public string|null $time_zone = null, - ) {} - } - - /** - * Metadata for an ASSA ABLOY Visionline system. - */ - class VisionlineMetadata - { - public static function from_json(mixed $json): VisionlineMetadata|null - { - if (!$json) { - return null; - } - return new self(encoder_id: $json->encoder_id ?? null); - } - - public function __construct( + public float|null $noise_level_decibel = null, /** - * Encoder ID for an ASSA ABLOY Visionline system. + * Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. */ - public string|null $encoder_id = null, + public float|null $noise_level_nrs = null, ) {} } /** - * Metadata for a Wyze device. + * Metadata for a Nuki device. */ - class WyzeMetadata + class NukiMetadata { - public static function from_json(mixed $json): WyzeMetadata|null + public static function from_json(mixed $json): NukiMetadata|null { if (!$json) { return null; } return new self( device_id: $json->device_id ?? null, - device_info_model: $json->device_info_model ?? null, device_name: $json->device_name ?? null, - keypad_uuid: $json->keypad_uuid ?? null, - locker_status_hardlock: $json->locker_status_hardlock ?? null, - product_model: $json->product_model ?? null, - product_name: $json->product_name ?? null, - product_type: $json->product_type ?? null, + keypad_2_paired: $json->keypad_2_paired ?? null, + keypad_battery_critical: $json->keypad_battery_critical ?? null, + keypad_paired: $json->keypad_paired ?? null, ); } public function __construct( /** - * Device ID for a Wyze device. + * Device ID for a Nuki device. */ public string|null $device_id = null, /** - * Device information model for a Wyze device. - */ - public string|null $device_info_model = null, - /** - * Device name for a Wyze device. + * Device name for a Nuki device. */ public string|null $device_name = null, /** - * Keypad UUID for a Wyze device. - */ - public string|null $keypad_uuid = null, - /** - * Locker status (hardlock) for a Wyze device. - */ - public float|null $locker_status_hardlock = null, - /** - * Product model for a Wyze device. + * Indicates whether keypad 2 is paired for a Nuki device. */ - public string|null $product_model = null, + public bool|null $keypad_2_paired = null, /** - * Product name for a Wyze device. + * Indicates whether the keypad battery is in a critical state for a Nuki device. */ - public string|null $product_name = null, + public bool|null $keypad_battery_critical = null, /** - * Product type for a Wyze device. + * Indicates whether the keypad is paired for a Nuki device. */ - public string|null $product_type = null, + public bool|null $keypad_paired = null, ) {} } /** - * Metadata for a Yacan device. + * Metadata for an Omnitec device. */ - class YacanMetadata + class OmnitecMetadata { - public static function from_json(mixed $json): YacanMetadata|null + public static function from_json(mixed $json): OmnitecMetadata|null { if (!$json) { return null; } return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - device_type: $json->device_type ?? null, - serial_number: $json->serial_number ?? null, + has_gateway: $json->has_gateway ?? null, + lock_alias: $json->lock_alias ?? null, + lock_id: $json->lock_id ?? null, + lock_mac: $json->lock_mac ?? null, + lock_name: $json->lock_name ?? null, + time_zone: $json->time_zone ?? null, + timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, ); } public function __construct( /** - * Device ID for a Yacan device. + * Whether the Omnitec lock has a connected gateway for remote operations. */ - public string|null $device_id = null, + public bool|null $has_gateway = null, /** - * Device name for a Yacan device. + * Operator-assigned alias for an Omnitec device. */ - public string|null $device_name = null, + public string|null $lock_alias = null, /** - * Device type for a Yacan device. + * Lock ID for an Omnitec device. */ - public string|null $device_type = null, + public float|null $lock_id = null, /** - * Serial number for a Yacan device. + * Bluetooth MAC address for an Omnitec device. */ - public string|null $serial_number = null, + public string|null $lock_mac = null, + /** + * Lock name for an Omnitec device. + */ + public string|null $lock_name = null, + /** + * IANA time zone for the Omnitec device, used to schedule time-bound access codes at the correct local time (accounting for DST). + */ + public string|null $time_zone = null, + /** + * Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. + */ + public float|null $timezone_raw_offset_ms = null, ) {} } /** - * 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. + * Metadata for a Ring device. */ - class CodeConstraints + class RingMetadata { - public static function from_json(mixed $json): CodeConstraints|null + public static function from_json(mixed $json): RingMetadata|null { if (!$json) { return null; } return new self( - constraint_type: $json->constraint_type ?? null, - max_length: $json->max_length ?? null, - min_length: $json->min_length ?? null, + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, ); } public function __construct( - public string|null $constraint_type, /** - * Maximum name length constraint for access codes. + * Device ID for a Ring device. */ - public float|null $max_length = null, + public string|null $device_id = null, /** - * Minimum name length constraint for access codes. + * Device name for a Ring device. */ - public float|null $min_length = null, + public string|null $device_name = null, ) {} } /** - * Keypad battery status. + * Metadata for a Salto KS device. */ - class KeypadBattery + class SaltoKsMetadata { - public static function from_json(mixed $json): KeypadBattery|null + public static function from_json(mixed $json): SaltoKsMetadata|null { - if (!$json) { - return null; - } - return new self(level: $json->level ?? null); - } - - public function __construct( - /** - * Keypad battery charge level. - */ - public float|null $level, - ) {} - } - - /** - * 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. - */ - class OfflineTimeFrameOptions - { - public static function from_json( - mixed $json, - ): OfflineTimeFrameOptions|null { if (!$json) { return null; } return new self( - display_name: $json->display_name ?? null, - end_date_recurrence_rule: $json->end_date_recurrence_rule ?? - null, - matching_start_end_time: $json->matching_start_end_time ?? null, - max_duration: $json->max_duration ?? null, - min_duration: $json->min_duration ?? null, - start_date_recurrence_rule: $json->start_date_recurrence_rule ?? + battery_level: $json->battery_level ?? null, + customer_reference: $json->customer_reference ?? null, + has_custom_pin_subscription: $json->has_custom_pin_subscription ?? null, - time_pairs: array_map( - fn($t) => OfflineTimeFrameOptions\TimePairs::from_json($t), - $json->time_pairs ?? [], - ), - time_zone: $json->time_zone ?? null, + lock_id: $json->lock_id ?? null, + lock_type: $json->lock_type ?? null, + locked_state: $json->locked_state ?? null, + model: $json->model ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, ); } public function __construct( /** - * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). + * Battery level for a Salto KS device. */ - public string|null $display_name, + public string|null $battery_level = null, /** - * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. + * Customer reference for a Salto KS device. */ - public string|null $end_date_recurrence_rule = null, + public string|null $customer_reference = null, /** - * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. + * Indicates whether the site has a Salto KS subscription that supports custom PINs. */ - public true|null $matching_start_end_time = null, + public bool|null $has_custom_pin_subscription = null, /** - * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. + * Lock ID for a Salto KS device. */ - public string|null $max_duration = null, + public string|null $lock_id = null, /** - * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. + * Lock type for a Salto KS device. */ - public string|null $min_duration = null, + public string|null $lock_type = null, /** - * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. + * Locked state for a Salto KS device. */ - public string|null $start_date_recurrence_rule = null, + public string|null $locked_state = null, /** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + * Model for a Salto KS device. */ - public array|null $time_pairs = null, + public string|null $model = null, /** - * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. + * Site ID for the Salto KS site to which the device belongs. */ - public string|null $time_zone = null, + public string|null $site_id = null, + /** + * Site name for the Salto KS site to which the device belongs. + */ + public string|null $site_name = null, ) {} } /** - * 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. + * Metada for a Salto device. + * + * @deprecated Use `salto_ks_metadata` instead. */ - class OnlineTimeFrameOptions + class SaltoMetadata { - public static function from_json( - mixed $json, - ): OnlineTimeFrameOptions|null { + public static function from_json(mixed $json): SaltoMetadata|null + { if (!$json) { return null; } return new self( - display_name: $json->display_name ?? null, - end_date_recurrence_rule: $json->end_date_recurrence_rule ?? - null, - matching_start_end_time: $json->matching_start_end_time ?? null, - max_duration: $json->max_duration ?? null, - min_duration: $json->min_duration ?? null, - start_date_recurrence_rule: $json->start_date_recurrence_rule ?? - null, - time_pairs: array_map( - fn($t) => OnlineTimeFrameOptions\TimePairs::from_json($t), - $json->time_pairs ?? [], - ), - time_zone: $json->time_zone ?? null, + battery_level: $json->battery_level ?? null, + customer_reference: $json->customer_reference ?? null, + lock_id: $json->lock_id ?? null, + lock_type: $json->lock_type ?? null, + locked_state: $json->locked_state ?? null, + model: $json->model ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, ); } public function __construct( /** - * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). + * Battery level for a Salto device. */ - public string|null $display_name, + public string|null $battery_level = null, /** - * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. + * Customer reference for a Salto device. */ - public string|null $end_date_recurrence_rule = null, + public string|null $customer_reference = null, /** - * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. + * Lock ID for a Salto device. */ - public true|null $matching_start_end_time = null, + public string|null $lock_id = null, /** - * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. + * Lock type for a Salto device. */ - public string|null $max_duration = null, + public string|null $lock_type = null, /** - * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. + * Locked state for a Salto device. */ - public string|null $min_duration = null, + public string|null $locked_state = null, /** - * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. + * Model for a Salto device. */ - public string|null $start_date_recurrence_rule = null, + public string|null $model = null, /** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + * Site ID for the Salto KS site to which the device belongs. */ - public array|null $time_pairs = null, + public string|null $site_id = null, /** - * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. + * Site name for the Salto KS site to which the device belongs. */ - public string|null $time_zone = null, + public string|null $site_name = null, ) {} } /** - * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - * - * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. + * Metadata for a Schlage device. */ - class ActiveThermostatSchedule + class SchlageMetadata { - public static function from_json( - mixed $json, - ): ActiveThermostatSchedule|null { + public static function from_json(mixed $json): SchlageMetadata|null + { if (!$json) { return null; } return new self( - climate_preset_key: $json->climate_preset_key ?? null, - created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => ActiveThermostatSchedule\Errors::from_json($e), - $json->errors ?? [], - ), - is_override_allowed: $json->is_override_allowed ?? null, - max_override_period_minutes: $json->max_override_period_minutes ?? - null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - thermostat_schedule_id: $json->thermostat_schedule_id ?? null, - workspace_id: $json->workspace_id ?? null, + device_name: $json->device_name ?? null, + model: $json->model ?? null, ); } public function __construct( /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $climate_preset_key, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. - */ - public string|null $created_at, - /** - * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. - */ - public string|null $device_id, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public array $errors, - /** - * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * Device ID for a Schlage device. */ - public string|null $name, + public string|null $device_id = null, /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + * Device name for a Schlage device. */ - public string|null $starts_at, + public string|null $device_name = null, /** - * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * Model for a Schlage device. */ - public string|null $thermostat_schedule_id, + public string|null $model = null, + ) {} + } + + /** + * Metadata for Seam Bridge. + */ + class SeamBridgeMetadata + { + public static function from_json(mixed $json): SeamBridgeMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_num: $json->device_num ?? null, + name: $json->name ?? null, + unlock_method: $json->unlock_method ?? null, + ); + } + + public function __construct( /** - * ID of the workspace that contains the thermostat schedule. + * Device number for Seam Bridge. */ - public string|null $workspace_id, + public float|null $device_num = null, /** - * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. + * Name for Seam Bridge. */ - public bool|null $is_override_allowed = null, + public string|null $name = null, /** - * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * Unlock method for Seam Bridge. + * + * @var value-of<\Seam\Resources\Device\Properties\SeamBridgeMetadata\UnlockMethod>|string|null */ - public int|null $max_override_period_minutes = null, + public string|null $unlock_method = null, ) {} } /** - * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. + * Metadata for a Sensi device. */ - class AvailableClimatePresets + class SensiMetadata { - public static function from_json( - mixed $json, - ): AvailableClimatePresets|null { + public static function from_json(mixed $json): SensiMetadata|null + { if (!$json) { return null; } return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? - null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? AvailableClimatePresets\EcobeeMetadata::from_json( - $json->ecobee_metadata, - ) - : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + dual_setpoints_not_supported: $json->dual_setpoints_not_supported ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + enforced_setpoint_range_celsius: $json->enforced_setpoint_range_celsius ?? null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, + product_type: $json->product_type ?? null, ); } public function __construct( /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + * Device ID for a Sensi device. */ - public bool|null $can_delete, + public string|null $device_id = null, /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + * Device name for a Sensi device. */ - public bool|null $can_edit, + public string|null $device_name = null, /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + * Set to true when the device does not support the /dual-setpoints API endpoint. */ - public bool|null $can_use_with_thermostat_daily_programs, + public bool|null $dual_setpoints_not_supported = null, /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + * Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. + * + * @var list|null */ - public string|null $climate_preset_key, + public array|null $enforced_setpoint_range_celsius = null, /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + * Product type for a Sensi device. */ - public string|null $display_name, + public string|null $product_type = null, + ) {} + } + + /** + * Metadata for a SmartThings device. + */ + class SmartthingsMetadata + { + public static function from_json(mixed $json): SmartthingsMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + location_id: $json->location_id ?? null, + model: $json->model ?? null, + ); + } + + public function __construct( /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' + * Device ID for a SmartThings device. */ - public bool|null $manual_override_allowed, + public string|null $device_id = null, /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + * Device name for a SmartThings device. */ - public string|null $climate_preset_mode = null, + public string|null $device_name = null, /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Location ID for a SmartThings device. */ - public float|null $cooling_set_point_celsius = null, + public string|null $location_id = null, /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Model for a SmartThings device. */ - public float|null $cooling_set_point_fahrenheit = null, + public string|null $model = null, + ) {} + } + + /** + * Metadata for a tado° device. + */ + class TadoMetadata + { + public static function from_json(mixed $json): TadoMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_type: $json->device_type ?? null, + serial_no: $json->serial_no ?? null, + ); + } + + public function __construct( /** - * Metadata specific to the Ecobee climate, if applicable. + * Device type for a tado° device. */ - public AvailableClimatePresets\EcobeeMetadata|null $ecobee_metadata = null, + public string|null $device_type = null, /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + * Serial number for a tado° device. */ - public string|null $fan_mode_setting = null, + public string|null $serial_no = null, + ) {} + } + + /** + * Metadata for a Tedee device. + */ + class TedeeMetadata + { + public static function from_json(mixed $json): TedeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + bridge_id: $json->bridge_id ?? null, + bridge_name: $json->bridge_name ?? null, + device_id: $json->device_id ?? null, + device_model: $json->device_model ?? null, + device_name: $json->device_name ?? null, + keypad_id: $json->keypad_id ?? null, + serial_number: $json->serial_number ?? null, + ); + } + + public function __construct( /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Bridge ID for a Tedee device. */ - public float|null $heating_set_point_celsius = null, + public float|null $bridge_id = null, /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Bridge name for a Tedee device. */ - public float|null $heating_set_point_fahrenheit = null, + public string|null $bridge_name = null, /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + * Device ID for a Tedee device. */ - public string|null $hvac_mode_setting = null, + public float|null $device_id = null, /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + * Device model for a Tedee device. */ - public string|null $name = null, + public string|null $device_model = null, + /** + * Device name for a Tedee device. + */ + public string|null $device_name = null, + /** + * Keypad ID for a Tedee device. + */ + public float|null $keypad_id = null, + /** + * Serial number for a Tedee device. + */ + public string|null $serial_number = null, ) {} } /** - * Current climate setting. + * Metadata for a TTLock device. */ - class CurrentClimateSetting + class TtlockMetadata { - public static function from_json( - mixed $json, - ): CurrentClimateSetting|null { + public static function from_json(mixed $json): TtlockMetadata|null + { if (!$json) { return null; } return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? - null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? CurrentClimateSetting\EcobeeMetadata::from_json( - $json->ecobee_metadata, + feature_value: $json->feature_value ?? null, + features: isset($json->features) + ? \Seam\Resources\Device\Properties\TtlockMetadata\Features::from_json( + $json->features, ) : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? - null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, + has_gateway: $json->has_gateway ?? null, + lock_alias: $json->lock_alias ?? null, + lock_id: $json->lock_id ?? null, + timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, + wireless_keypads: array_map( + fn( + $w, + ) => \Seam\Resources\Device\Properties\TtlockMetadata\WirelessKeypads::from_json( + $w, + ), + $json->wireless_keypads ?? [], + ), ); } public function __construct( /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + * Feature value for a TTLock device. */ - public bool|null $can_delete = null, + public string|null $feature_value = null, /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + * Features for a TTLock device. */ - public bool|null $can_edit = null, + public \Seam\Resources\Device\Properties\TtlockMetadata\Features|null $features = null, /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + * Indicates whether a TTLock device has a gateway. */ - public bool|null $can_use_with_thermostat_daily_programs = null, + public bool|null $has_gateway = null, /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + * Lock alias for a TTLock device. */ - public string|null $climate_preset_key = null, + public string|null $lock_alias = null, /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + * Lock ID for a TTLock device. */ - public string|null $climate_preset_mode = null, + public float|null $lock_id = null, /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Lock-side timezone offset in milliseconds east of UTC, as configured in the TTLock app. Source of truth for the lock's wall-clock interpretation of access code start/end times — a misconfigured value here is the typical cause of customer "codes offset by N hours" reports. Diagnostic only; Seam does not convert times based on this value. */ - public float|null $cooling_set_point_celsius = null, + public float|null $timezone_raw_offset_ms = null, /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Wireless keypads for a TTLock device. + * + * @var list<\Seam\Resources\Device\Properties\TtlockMetadata\WirelessKeypads>|null */ - public float|null $cooling_set_point_fahrenheit = null, + public array|null $wireless_keypads = null, + ) {} + } + + /** + * Metadata for a 2N device. + */ + class TwoNMetadata + { + public static function from_json(mixed $json): TwoNMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + ); + } + + public function __construct( /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + * Device ID for a 2N device. */ - public string|null $display_name = null, + public float|null $device_id = null, /** - * Metadata specific to the Ecobee climate, if applicable. + * Device name for a 2N device. */ - public CurrentClimateSetting\EcobeeMetadata|null $ecobee_metadata = null, + public string|null $device_name = null, + ) {} + } + + /** + * Metadata for an Ultraloq device. + */ + class UltraloqMetadata + { + public static function from_json(mixed $json): UltraloqMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + device_type: $json->device_type ?? null, + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + * Device ID for an Ultraloq device. */ - public string|null $fan_mode_setting = null, + public string|null $device_id = null, /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Device name for an Ultraloq device. */ - public float|null $heating_set_point_celsius = null, + public string|null $device_name = null, /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Device type for an Ultraloq device. */ - public float|null $heating_set_point_fahrenheit = null, + public string|null $device_type = null, /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + * IANA timezone for the Ultraloq device. */ - public string|null $hvac_mode_setting = null, + public string|null $time_zone = null, + ) {} + } + + /** + * Metadata for an ASSA ABLOY Visionline system. + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self(encoder_id: $json->encoder_id ?? null); + } + + public function __construct( /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' + * Encoder ID for an ASSA ABLOY Visionline system. */ - public bool|null $manual_override_allowed = null, - /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + public string|null $encoder_id = null, + ) {} + } + + /** + * Metadata for a Wyze device. + */ + class WyzeMetadata + { + public static function from_json(mixed $json): WyzeMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_info_model: $json->device_info_model ?? null, + device_name: $json->device_name ?? null, + keypad_uuid: $json->keypad_uuid ?? null, + locker_status_hardlock: $json->locker_status_hardlock ?? null, + product_model: $json->product_model ?? null, + product_name: $json->product_name ?? null, + product_type: $json->product_type ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Wyze device. + */ + public string|null $device_id = null, + /** + * Device information model for a Wyze device. + */ + public string|null $device_info_model = null, + /** + * Device name for a Wyze device. + */ + public string|null $device_name = null, + /** + * Keypad UUID for a Wyze device. + */ + public string|null $keypad_uuid = null, + /** + * Locker status (hardlock) for a Wyze device. + */ + public float|null $locker_status_hardlock = null, + /** + * Product model for a Wyze device. + */ + public string|null $product_model = null, + /** + * Product name for a Wyze device. + */ + public string|null $product_name = null, + /** + * Product type for a Wyze device. + */ + public string|null $product_type = null, + ) {} + } + + /** + * Metadata for a Yacan device. + */ + class YacanMetadata + { + public static function from_json(mixed $json): YacanMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + device_type: $json->device_type ?? null, + serial_number: $json->serial_number ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Yacan device. + */ + public string|null $device_id = null, + /** + * Device name for a Yacan device. + */ + public string|null $device_name = null, + /** + * Device type for a Yacan device. + */ + public string|null $device_type = null, + /** + * Serial number for a Yacan device. + */ + public string|null $serial_number = null, + ) {} + } + + /** + * 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. + */ + class CodeConstraints + { + public static function from_json(mixed $json): CodeConstraints|null + { + if (!$json) { + return null; + } + return new self( + constraint_type: $json->constraint_type ?? null, + max_length: $json->max_length ?? null, + min_length: $json->min_length ?? null, + ); + } + + public function __construct( + /** + * @var value-of<\Seam\Resources\Device\Properties\CodeConstraints\ConstraintType>|string|null + */ + public string|null $constraint_type, + /** + * Maximum name length constraint for access codes. + */ + public float|null $max_length = null, + /** + * Minimum name length constraint for access codes. + */ + public float|null $min_length = null, + ) {} + } + + /** + * Keypad battery status. + */ + class KeypadBattery + { + public static function from_json(mixed $json): KeypadBattery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); + } + + public function __construct( + /** + * Keypad battery charge level. + */ + public float|null $level, + ) {} + } + + /** + * 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. + */ + class OfflineTimeFrameOptions + { + public static function from_json( + mixed $json, + ): OfflineTimeFrameOptions|null { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_date_recurrence_rule: $json->end_date_recurrence_rule ?? + null, + matching_start_end_time: $json->matching_start_end_time ?? null, + max_duration: $json->max_duration ?? null, + min_duration: $json->min_duration ?? null, + start_date_recurrence_rule: $json->start_date_recurrence_rule ?? + null, + time_pairs: array_map( + fn( + $t, + ) => \Seam\Resources\Device\Properties\OfflineTimeFrameOptions\TimePairs::from_json( + $t, + ), + $json->time_pairs ?? [], + ), + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( + /** + * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). + */ + public string|null $display_name, + /** + * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $end_date_recurrence_rule = null, + /** + * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. + */ + public true|null $matching_start_end_time = null, + /** + * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. + */ + public string|null $max_duration = null, + /** + * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. + */ + public string|null $min_duration = null, + /** + * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $start_date_recurrence_rule = null, + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + * + * @var list<\Seam\Resources\Device\Properties\OfflineTimeFrameOptions\TimePairs>|null + */ + public array|null $time_pairs = null, + /** + * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. + */ + public string|null $time_zone = null, + ) {} + } + + /** + * 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. + */ + class OnlineTimeFrameOptions + { + public static function from_json( + mixed $json, + ): OnlineTimeFrameOptions|null { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_date_recurrence_rule: $json->end_date_recurrence_rule ?? + null, + matching_start_end_time: $json->matching_start_end_time ?? null, + max_duration: $json->max_duration ?? null, + min_duration: $json->min_duration ?? null, + start_date_recurrence_rule: $json->start_date_recurrence_rule ?? + null, + time_pairs: array_map( + fn( + $t, + ) => \Seam\Resources\Device\Properties\OnlineTimeFrameOptions\TimePairs::from_json( + $t, + ), + $json->time_pairs ?? [], + ), + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( + /** + * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). + */ + public string|null $display_name, + /** + * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $end_date_recurrence_rule = null, + /** + * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. + */ + public true|null $matching_start_end_time = null, + /** + * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. + */ + public string|null $max_duration = null, + /** + * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. + */ + public string|null $min_duration = null, + /** + * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $start_date_recurrence_rule = null, + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + * + * @var list<\Seam\Resources\Device\Properties\OnlineTimeFrameOptions\TimePairs>|null + */ + public array|null $time_pairs = null, + /** + * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. + */ + public string|null $time_zone = null, + ) {} + } + + /** + * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * + * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. + */ + class ActiveThermostatSchedule + { + public static function from_json( + mixed $json, + ): ActiveThermostatSchedule|null { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn( + $e, + ) => \Seam\Resources\Device\Properties\ActiveThermostatSchedule\Errors::from_json( + $e, + ), + $json->errors ?? [], + ), + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + thermostat_schedule_id: $json->thermostat_schedule_id ?? null, + workspace_id: $json->workspace_id ?? null, + is_override_allowed: $json->is_override_allowed ?? null, + max_override_period_minutes: $json->max_override_period_minutes ?? + null, + ); + } + + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $climate_preset_key, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. + */ + public string|null $created_at, + /** + * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. + */ + public string|null $device_id, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * + * @var list<\Seam\Resources\Device\Properties\ActiveThermostatSchedule\Errors> + */ + public array $errors, + /** + * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $name, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $thermostat_schedule_id, + /** + * ID of the workspace that contains the thermostat schedule. + */ + public string|null $workspace_id, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. + */ + public bool|null $is_override_allowed = null, + /** + * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + */ + public int|null $max_override_period_minutes = null, + ) {} + } + + /** + * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. + */ + class AvailableClimatePresets + { + public static function from_json( + mixed $json, + ): AvailableClimatePresets|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + display_name: $json->display_name ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + ecobee_metadata: isset($json->ecobee_metadata) + ? \Seam\Resources\Device\Properties\AvailableClimatePresets\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + * + * @var value-of<\Seam\Resources\Device\Properties\AvailableClimatePresets\ClimatePresetMode>|string|null + */ + public string|null $climate_preset_mode = null, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius = null, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit = null, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public \Seam\Resources\Device\Properties\AvailableClimatePresets\EcobeeMetadata|null $ecobee_metadata = null, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + * + * @var value-of<\Seam\Resources\Device\Properties\AvailableClimatePresets\FanModeSetting>|string|null + */ + public string|null $fan_mode_setting = null, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius = null, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit = null, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + * + * @var value-of<\Seam\Resources\Device\Properties\AvailableClimatePresets\HvacModeSetting>|string|null + */ + public string|null $hvac_mode_setting = null, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name = null, + ) {} + } + + /** + * Current climate setting. + */ + class CurrentClimateSetting + { + public static function from_json( + mixed $json, + ): CurrentClimateSetting|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + display_name: $json->display_name ?? null, + ecobee_metadata: isset($json->ecobee_metadata) + ? \Seam\Resources\Device\Properties\CurrentClimateSetting\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete = null, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit = null, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs = null, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key = null, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + * + * @var value-of<\Seam\Resources\Device\Properties\CurrentClimateSetting\ClimatePresetMode>|string|null + */ + public string|null $climate_preset_mode = null, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius = null, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit = null, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name = null, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public \Seam\Resources\Device\Properties\CurrentClimateSetting\EcobeeMetadata|null $ecobee_metadata = null, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + * + * @var value-of<\Seam\Resources\Device\Properties\CurrentClimateSetting\FanModeSetting>|string|null + */ + public string|null $fan_mode_setting = null, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius = null, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit = null, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + * + * @var value-of<\Seam\Resources\Device\Properties\CurrentClimateSetting\HvacModeSetting>|string|null + */ + public string|null $hvac_mode_setting = null, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed = null, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name = null, + ) {} + } + + /** + * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. + */ + class DefaultClimateSetting + { + public static function from_json( + mixed $json, + ): DefaultClimateSetting|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + display_name: $json->display_name ?? null, + ecobee_metadata: isset($json->ecobee_metadata) + ? \Seam\Resources\Device\Properties\DefaultClimateSetting\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete = null, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit = null, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs = null, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key = null, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + * + * @var value-of<\Seam\Resources\Device\Properties\DefaultClimateSetting\ClimatePresetMode>|string|null + */ + public string|null $climate_preset_mode = null, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius = null, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit = null, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name = null, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public \Seam\Resources\Device\Properties\DefaultClimateSetting\EcobeeMetadata|null $ecobee_metadata = null, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + * + * @var value-of<\Seam\Resources\Device\Properties\DefaultClimateSetting\FanModeSetting>|string|null + */ + public string|null $fan_mode_setting = null, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius = null, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit = null, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + * + * @var value-of<\Seam\Resources\Device\Properties\DefaultClimateSetting\HvacModeSetting>|string|null + */ + public string|null $hvac_mode_setting = null, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed = null, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name = null, + ) {} + } + + /** + * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + class TemperatureThreshold + { + public static function from_json(mixed $json): TemperatureThreshold|null + { + if (!$json) { + return null; + } + return new self( + lower_limit_celsius: $json->lower_limit_celsius ?? null, + lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, + upper_limit_celsius: $json->upper_limit_celsius ?? null, + upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, + ); + } + + public function __construct( + /** + * Lower limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $lower_limit_celsius, + /** + * Lower limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $lower_limit_fahrenheit, + /** + * Upper limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $upper_limit_celsius, + /** + * Upper limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $upper_limit_fahrenheit, + ) {} + } + + /** + * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + class ThermostatDailyPrograms + { + public static function from_json( + mixed $json, + ): ThermostatDailyPrograms|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + name: $json->name ?? null, + periods: array_map( + fn( + $p, + ) => \Seam\Resources\Device\Properties\ThermostatDailyPrograms\Periods::from_json( + $p, + ), + $json->periods ?? [], + ), + thermostat_daily_program_id: $json->thermostat_daily_program_id ?? + null, + workspace_id: $json->workspace_id ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the thermostat daily program was created. + */ + public string|null $created_at, + /** + * ID of the thermostat device on which the thermostat daily program is configured. + */ + public string|null $device_id, + /** + * User-friendly name to identify the thermostat daily program. + */ + public string|null $name, + /** + * Array of thermostat daily program periods. + * + * @var list<\Seam\Resources\Device\Properties\ThermostatDailyPrograms\Periods> + */ + public array $periods, + /** + * ID of the thermostat daily program. + */ + public string|null $thermostat_daily_program_id, + /** + * ID of the workspace that contains the thermostat daily program. + */ + public string|null $workspace_id, + ) {} + } + + /** + * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + class ThermostatWeeklyProgram + { + public static function from_json( + mixed $json, + ): ThermostatWeeklyProgram|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + friday_program_id: $json->friday_program_id ?? null, + monday_program_id: $json->monday_program_id ?? null, + saturday_program_id: $json->saturday_program_id ?? null, + sunday_program_id: $json->sunday_program_id ?? null, + thursday_program_id: $json->thursday_program_id ?? null, + tuesday_program_id: $json->tuesday_program_id ?? null, + wednesday_program_id: $json->wednesday_program_id ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the thermostat weekly program was created. + */ + public string|null $created_at, + /** + * ID of the thermostat daily program to run on Fridays. + */ + public string|null $friday_program_id, + /** + * ID of the thermostat daily program to run on Mondays. + */ + public string|null $monday_program_id, + /** + * ID of the thermostat daily program to run on Saturdays. + */ + public string|null $saturday_program_id, + /** + * ID of the thermostat daily program to run on Sundays. + */ + public string|null $sunday_program_id, + /** + * ID of the thermostat daily program to run on Thursdays. + */ + public string|null $thursday_program_id, + /** + * ID of the thermostat daily program to run on Tuesdays. + */ + public string|null $tuesday_program_id, + /** + * ID of the thermostat daily program to run on Wednesdays. + */ + public string|null $wednesday_program_id, + ) {} + } + + enum FanModeSetting: string + { + case AUTO = "auto"; + case ON = "on"; + case CIRCULATE = "circulate"; + } +} + +namespace Seam\Resources\Device\Properties\AccessoryKeypad { + /** + * Keypad battery properties. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); + } + + public function __construct(public float|null $level) {} + } +} + +namespace Seam\Resources\Device\Properties\Battery { + enum Status: string + { + case CRITICAL = "critical"; + case LOW = "low"; + case GOOD = "good"; + case FULL = "full"; + } +} + +namespace Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata { + /** + * Endpoints associated with the phone. + */ + class Endpoints + { + public static function from_json(mixed $json): Endpoints|null + { + if (!$json) { + return null; + } + return new self( + endpoint_id: $json->endpoint_id ?? null, + is_active: $json->is_active ?? null, + ); + } + + public function __construct( + /** + * ID of the associated endpoint. + */ + public string|null $endpoint_id = null, + /** + * Indicated whether the endpoint is active. + */ + public bool|null $is_active = null, + ) {} + } +} + +namespace Seam\Resources\Device\Properties\DormakabaOracodeMetadata { + /** + * Predefined time slots for a dormakaba Oracode device. + */ + class PredefinedTimeSlots + { + public static function from_json(mixed $json): PredefinedTimeSlots|null + { + if (!$json) { + return null; + } + return new self( + check_in_time: $json->check_in_time ?? null, + check_out_time: $json->check_out_time ?? null, + dormakaba_oracode_user_level_id: $json->dormakaba_oracode_user_level_id ?? + null, + dormakaba_oracode_user_level_prefix: $json->dormakaba_oracode_user_level_prefix ?? + null, + is_24_hour: $json->is_24_hour ?? null, + is_biweekly_mode: $json->is_biweekly_mode ?? null, + is_master: $json->is_master ?? null, + is_one_shot: $json->is_one_shot ?? null, + name: $json->name ?? null, + prefix: $json->prefix ?? null, + ); + } + + public function __construct( + /** + * Check in time for a time slot for a dormakaba Oracode device. + */ + public string|null $check_in_time = null, + /** + * Checkout time for a time slot for a dormakaba Oracode device. + */ + public string|null $check_out_time = null, + /** + * ID of a user level for a dormakaba Oracode device. + */ + public string|null $dormakaba_oracode_user_level_id = null, + /** + * Prefix for a user level for a dormakaba Oracode device. + */ + public float|null $dormakaba_oracode_user_level_prefix = null, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a 24-hour time slot. + */ + public bool|null $is_24_hour = null, + /** + * Indicates whether a time slot for a dormakaba Oracode device is in biweekly mode. + */ + public bool|null $is_biweekly_mode = null, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a master time slot. + */ + public bool|null $is_master = null, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a one-shot time slot. + */ + public bool|null $is_one_shot = null, + /** + * Name of a time slot for a dormakaba Oracode device. + */ + public string|null $name = null, + /** + * Prefix for a time slot for a dormakaba Oracode device. + */ + public float|null $prefix = null, + ) {} + } +} + +namespace Seam\Resources\Device\Properties\MinutMetadata { + /** + * Latest sensor values for a Minut device. + */ + class LatestSensorValues + { + public static function from_json(mixed $json): LatestSensorValues|null + { + if (!$json) { + return null; + } + return new self( + accelerometer_z: isset($json->accelerometer_z) + ? \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\AccelerometerZ::from_json( + $json->accelerometer_z, + ) + : null, + humidity: isset($json->humidity) + ? \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\Humidity::from_json( + $json->humidity, + ) + : null, + pressure: isset($json->pressure) + ? \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\Pressure::from_json( + $json->pressure, + ) + : null, + sound: isset($json->sound) + ? \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\Sound::from_json( + $json->sound, + ) + : null, + temperature: isset($json->temperature) + ? \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\Temperature::from_json( + $json->temperature, + ) + : null, + ); + } + + public function __construct( + /** + * Latest accelerometer Z-axis reading for a Minut device. + */ + public \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\AccelerometerZ|null $accelerometer_z = null, + /** + * Latest humidity reading for a Minut device. + */ + public \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\Humidity|null $humidity = null, + /** + * Latest pressure reading for a Minut device. + */ + public \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\Pressure|null $pressure = null, + /** + * Latest sound reading for a Minut device. + */ + public \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\Sound|null $sound = null, + /** + * Latest temperature reading for a Minut device. + */ + public \Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues\Temperature|null $temperature = null, + ) {} + } +} + +namespace Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues { + /** + * Latest accelerometer Z-axis reading for a Minut device. + */ + class AccelerometerZ + { + public static function from_json(mixed $json): AccelerometerZ|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest accelerometer Z-axis reading for a Minut device. + */ + public string|null $time = null, + /** + * Value of latest accelerometer Z-axis reading for a Minut device. + */ + public float|null $value = null, + ) {} + } + + /** + * Latest humidity reading for a Minut device. + */ + class Humidity + { + public static function from_json(mixed $json): Humidity|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest humidity reading for a Minut device. + */ + public string|null $time = null, + /** + * Value of latest humidity reading for a Minut device. + */ + public float|null $value = null, + ) {} + } + + /** + * Latest pressure reading for a Minut device. + */ + class Pressure + { + public static function from_json(mixed $json): Pressure|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest pressure reading for a Minut device. + */ + public string|null $time = null, + /** + * Value of latest pressure reading for a Minut device. + */ + public float|null $value = null, + ) {} + } + + /** + * Latest sound reading for a Minut device. + */ + class Sound + { + public static function from_json(mixed $json): Sound|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest sound reading for a Minut device. + */ + public string|null $time = null, + /** + * Value of latest sound reading for a Minut device. + */ + public float|null $value = null, + ) {} + } + + /** + * Latest temperature reading for a Minut device. + */ + class Temperature + { + public static function from_json(mixed $json): Temperature|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest temperature reading for a Minut device. + */ + public string|null $time = null, + /** + * Value of latest temperature reading for a Minut device. + */ + public float|null $value = null, + ) {} + } +} + +namespace Seam\Resources\Device\Properties\NoiseawareMetadata { + enum DeviceModel: string + { + case INDOOR = "indoor"; + case OUTDOOR = "outdoor"; + } +} + +namespace Seam\Resources\Device\Properties\SeamBridgeMetadata { + enum UnlockMethod: string + { + case BRIDGE = "bridge"; + case DOORKING = "doorking"; + } +} + +namespace Seam\Resources\Device\Properties\TtlockMetadata { + /** + * Features for a TTLock device. + */ + class Features + { + public static function from_json(mixed $json): Features|null + { + if (!$json) { + return null; + } + return new self( + auto_lock_time_config: $json->auto_lock_time_config ?? null, + incomplete_keyboard_passcode: $json->incomplete_keyboard_passcode ?? + null, + lock_command: $json->lock_command ?? null, + passcode: $json->passcode ?? null, + passcode_management: $json->passcode_management ?? null, + unlock_via_gateway: $json->unlock_via_gateway ?? null, + wifi: $json->wifi ?? null, + ); + } + + public function __construct( + /** + * Indicates whether a TTLock device supports auto-lock time configuration. + */ + public bool|null $auto_lock_time_config = null, + /** + * Indicates whether a TTLock device supports an incomplete keyboard passcode. + */ + public bool|null $incomplete_keyboard_passcode = null, + /** + * Indicates whether a TTLock device supports the lock command. + */ + public bool|null $lock_command = null, + /** + * Indicates whether a TTLock device supports a passcode. + */ + public bool|null $passcode = null, + /** + * Indicates whether a TTLock device supports passcode management. + */ + public bool|null $passcode_management = null, + /** + * Indicates whether a TTLock device supports unlock via gateway. + */ + public bool|null $unlock_via_gateway = null, + /** + * Indicates whether a TTLock device supports Wi-Fi. + */ + public bool|null $wifi = null, + ) {} + } + + /** + * Wireless keypads for a TTLock device. + */ + class WirelessKeypads + { + public static function from_json(mixed $json): WirelessKeypads|null + { + if (!$json) { + return null; + } + return new self( + wireless_keypad_id: $json->wireless_keypad_id ?? null, + wireless_keypad_name: $json->wireless_keypad_name ?? null, + ); + } + + public function __construct( + /** + * ID for a wireless keypad for a TTLock device. + */ + public float|null $wireless_keypad_id = null, + /** + * Name for a wireless keypad for a TTLock device. + */ + public string|null $wireless_keypad_name = null, + ) {} + } +} + +namespace Seam\Resources\Device\Properties\CodeConstraints { + enum ConstraintType: string + { + case NO_ZEROS = "no_zeros"; + case CANNOT_START_WITH_12 = "cannot_start_with_12"; + case NO_TRIPLE_CONSECUTIVE_INTS = "no_triple_consecutive_ints"; + case CANNOT_SPECIFY_PIN_CODE = "cannot_specify_pin_code"; + case PIN_CODE_MATCHES_EXISTING_SET = "pin_code_matches_existing_set"; + case START_DATE_IN_FUTURE = "start_date_in_future"; + case NO_ASCENDING_OR_DESCENDING_SEQUENCE = "no_ascending_or_descending_sequence"; + case AT_LEAST_THREE_UNIQUE_DIGITS = "at_least_three_unique_digits"; + case CANNOT_CONTAIN_089 = "cannot_contain_089"; + case CANNOT_CONTAIN_0789 = "cannot_contain_0789"; + case UNIQUE_FIRST_FOUR_DIGITS = "unique_first_four_digits"; + case NO_ALL_SAME_DIGITS = "no_all_same_digits"; + case NAME_LENGTH = "name_length"; + case NAME_MUST_BE_UNIQUE = "name_must_be_unique"; + } +} + +namespace Seam\Resources\Device\Properties\OfflineTimeFrameOptions { + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + class TimePairs + { + public static function from_json(mixed $json): TimePairs|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_time: $json->end_time ?? null, + start_time: $json->start_time ?? null, + ); + } + + public function __construct( + /** + * Label for the start/end time pairing. + */ + public string|null $display_name, + /** + * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. + */ + public string|null $end_time, + /** + * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. + */ + public string|null $start_time, + ) {} + } +} + +namespace Seam\Resources\Device\Properties\OnlineTimeFrameOptions { + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + class TimePairs + { + public static function from_json(mixed $json): TimePairs|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_time: $json->end_time ?? null, + start_time: $json->start_time ?? null, + ); + } + + public function __construct( + /** + * Label for the start/end time pairing. + */ + public string|null $display_name, + /** + * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. + */ + public string|null $end_time, + /** + * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. + */ + public string|null $start_time, + ) {} + } +} + +namespace Seam\Resources\Device\Properties\ActiveThermostatSchedule { + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } +} + +namespace Seam\Resources\Device\Properties\AvailableClimatePresets { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } + + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref = null, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized = null, + /** + * Indicates whether the climate preset is owned by the user or the system. + * + * @var value-of<\Seam\Resources\Device\Properties\AvailableClimatePresets\EcobeeMetadata\Owner>|string|null + */ + public string|null $owner = null, + ) {} + } + + enum ClimatePresetMode: string + { + case HOME = "home"; + case AWAY = "away"; + case WAKE = "wake"; + case SLEEP = "sleep"; + case OCCUPIED = "occupied"; + case UNOCCUPIED = "unoccupied"; + } + + enum FanModeSetting: string + { + case AUTO = "auto"; + case ON = "on"; + case CIRCULATE = "circulate"; + } + + enum HvacModeSetting: string + { + case OFF = "off"; + case HEAT = "heat"; + case COOL = "cool"; + case HEAT_COOL = "heat_cool"; + case ECO = "eco"; + } +} + +namespace Seam\Resources\Device\Properties\AvailableClimatePresets\EcobeeMetadata { + enum Owner: string + { + case USER = "user"; + case SYSTEM = "system"; + } +} + +namespace Seam\Resources\Device\Properties\CurrentClimateSetting { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } + + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref = null, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized = null, + /** + * Indicates whether the climate preset is owned by the user or the system. + * + * @var value-of<\Seam\Resources\Device\Properties\CurrentClimateSetting\EcobeeMetadata\Owner>|string|null + */ + public string|null $owner = null, + ) {} + } + + enum ClimatePresetMode: string + { + case HOME = "home"; + case AWAY = "away"; + case WAKE = "wake"; + case SLEEP = "sleep"; + case OCCUPIED = "occupied"; + case UNOCCUPIED = "unoccupied"; + } + + enum FanModeSetting: string + { + case AUTO = "auto"; + case ON = "on"; + case CIRCULATE = "circulate"; + } + + enum HvacModeSetting: string + { + case OFF = "off"; + case HEAT = "heat"; + case COOL = "cool"; + case HEAT_COOL = "heat_cool"; + case ECO = "eco"; + } +} + +namespace Seam\Resources\Device\Properties\CurrentClimateSetting\EcobeeMetadata { + enum Owner: string + { + case USER = "user"; + case SYSTEM = "system"; + } +} + +namespace Seam\Resources\Device\Properties\DefaultClimateSetting { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } + + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref = null, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized = null, + /** + * Indicates whether the climate preset is owned by the user or the system. + * + * @var value-of<\Seam\Resources\Device\Properties\DefaultClimateSetting\EcobeeMetadata\Owner>|string|null + */ + public string|null $owner = null, + ) {} + } + + enum ClimatePresetMode: string + { + case HOME = "home"; + case AWAY = "away"; + case WAKE = "wake"; + case SLEEP = "sleep"; + case OCCUPIED = "occupied"; + case UNOCCUPIED = "unoccupied"; + } + + enum FanModeSetting: string + { + case AUTO = "auto"; + case ON = "on"; + case CIRCULATE = "circulate"; + } + + enum HvacModeSetting: string + { + case OFF = "off"; + case HEAT = "heat"; + case COOL = "cool"; + case HEAT_COOL = "heat_cool"; + case ECO = "eco"; + } +} + +namespace Seam\Resources\Device\Properties\DefaultClimateSetting\EcobeeMetadata { + enum Owner: string + { + case USER = "user"; + case SYSTEM = "system"; + } +} + +namespace Seam\Resources\Device\Properties\ThermostatDailyPrograms { + /** + * Array of thermostat daily program periods. + */ + class Periods + { + public static function from_json(mixed $json): Periods|null + { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + starts_at_time: $json->starts_at_time ?? null, + ); + } + + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. + */ + public string|null $climate_preset_key, + /** + * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at_time, + ) {} + } +} + +namespace Seam\Resources\Device\Warnings { + /** + * Indicates that the backup access code is unhealthy. + */ + final class PartialBackupAccessCodePool extends + \Seam\Resources\Device\Warnings + { + public static function from_json( + mixed $json, + ): PartialBackupAccessCodePool|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that there are too many backup codes. + */ + final class ManyActiveBackupCodes extends \Seam\Resources\Device\Warnings + { + public static function from_json( + mixed $json, + ): ManyActiveBackupCodes|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that a third-party integration has been detected. + */ + final class ThirdPartyIntegrationDetected extends + \Seam\Resources\Device\Warnings + { + public static function from_json( + mixed $json, + ): ThirdPartyIntegrationDetected|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the Remote Unlock feature is not enabled in the settings." + */ + final class TtlockLockGatewayUnlockingNotEnabled extends + \Seam\Resources\Device\Warnings + { + public static function from_json( + mixed $json, + ): TtlockLockGatewayUnlockingNotEnabled|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $name = null, - ) {} + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. + * Indicates that the gateway signal is weak. */ - class DefaultClimateSetting + final class TtlockWeakGatewaySignal extends \Seam\Resources\Device\Warnings { public static function from_json( mixed $json, - ): DefaultClimateSetting|null { + ): TtlockWeakGatewaySignal|null { if (!$json) { return null; } return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? - null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DefaultClimateSetting\EcobeeMetadata::from_json( - $json->ecobee_metadata, - ) - : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? - null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. - */ - public bool|null $can_delete = null, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. - */ - public bool|null $can_edit = null, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. - */ - public bool|null $can_use_with_thermostat_daily_programs = null, - /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $climate_preset_key = null, - /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. - */ - public string|null $climate_preset_mode = null, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius = null, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit = null, - /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $display_name = null, - /** - * Metadata specific to the Ecobee climate, if applicable. - */ - public DefaultClimateSetting\EcobeeMetadata|null $ecobee_metadata = null, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting = null, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius = null, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Date and time at which Seam created the warning. */ - public float|null $heating_set_point_fahrenheit = null, + string|null $created_at, /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $hvac_mode_setting = null, + string|null $message, /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. * - * @deprecated Use 'thermostat_schedule.is_override_allowed' - */ - public bool|null $manual_override_allowed = null, - /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $name = null, - ) {} + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + * Indicates that the device is in power saving mode and may have limited functionality. */ - class TemperatureThreshold + final class PowerSavingMode extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): TemperatureThreshold|null + public static function from_json(mixed $json): PowerSavingMode|null { if (!$json) { return null; } return new self( - lower_limit_celsius: $json->lower_limit_celsius ?? null, - lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, - upper_limit_celsius: $json->upper_limit_celsius ?? null, - upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Lower limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $lower_limit_celsius, - /** - * Lower limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + * Date and time at which Seam created the warning. */ - public float|null $lower_limit_fahrenheit, + string|null $created_at, /** - * Upper limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $upper_limit_celsius, + string|null $message, /** - * Upper limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $upper_limit_fahrenheit, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + * Indicates that the temperature threshold has been exceeded. */ - class ThermostatDailyPrograms + final class TemperatureThresholdExceeded extends + \Seam\Resources\Device\Warnings { public static function from_json( mixed $json, - ): ThermostatDailyPrograms|null { + ): TemperatureThresholdExceeded|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - name: $json->name ?? null, - periods: array_map( - fn($p) => ThermostatDailyPrograms\Periods::from_json($p), - $json->periods ?? [], - ), - thermostat_daily_program_id: $json->thermostat_daily_program_id ?? - null, - workspace_id: $json->workspace_id ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Date and time at which the thermostat daily program was created. - */ - public string|null $created_at, - /** - * ID of the thermostat device on which the thermostat daily program is configured. - */ - public string|null $device_id, - /** - * User-friendly name to identify the thermostat daily program. - */ - public string|null $name, - /** - * Array of thermostat daily program periods. + * Date and time at which Seam created the warning. */ - public array $periods, + string|null $created_at, /** - * ID of the thermostat daily program. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $thermostat_daily_program_id, + string|null $message, /** - * ID of the workspace that contains the thermostat daily program. - */ - public string|null $workspace_id, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + * Indicates that the device appears to be unresponsive. */ - class ThermostatWeeklyProgram + final class DeviceCommunicationDegraded extends + \Seam\Resources\Device\Warnings { public static function from_json( mixed $json, - ): ThermostatWeeklyProgram|null { + ): DeviceCommunicationDegraded|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, - friday_program_id: $json->friday_program_id ?? null, - monday_program_id: $json->monday_program_id ?? null, - saturday_program_id: $json->saturday_program_id ?? null, - sunday_program_id: $json->sunday_program_id ?? null, - thursday_program_id: $json->thursday_program_id ?? null, - tuesday_program_id: $json->tuesday_program_id ?? null, - wednesday_program_id: $json->wednesday_program_id ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Date and time at which the thermostat weekly program was created. - */ - public string|null $created_at, - /** - * ID of the thermostat daily program to run on Fridays. - */ - public string|null $friday_program_id, - /** - * ID of the thermostat daily program to run on Mondays. + * Date and time at which Seam created the warning. */ - public string|null $monday_program_id, + string|null $created_at, /** - * ID of the thermostat daily program to run on Saturdays. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $saturday_program_id, + string|null $message, /** - * ID of the thermostat daily program to run on Sundays. - */ - public string|null $sunday_program_id, + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that a scheduled maintenance window has been detected. + */ + final class ScheduledMaintenanceWindow extends + \Seam\Resources\Device\Warnings + { + public static function from_json( + mixed $json, + ): ScheduledMaintenanceWindow|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * ID of the thermostat daily program to run on Thursdays. + * Date and time at which Seam created the warning. */ - public string|null $thursday_program_id, + string|null $created_at, /** - * ID of the thermostat daily program to run on Tuesdays. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $tuesday_program_id, + string|null $message, /** - * ID of the thermostat daily program to run on Wednesdays. - */ - public string|null $wednesday_program_id, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\AccessoryKeypad { /** - * Keypad battery properties. + * Indicates that the device has a flaky connection. */ - class Battery + final class DeviceHasFlakyConnection extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Battery|null - { + public static function from_json( + mixed $json, + ): DeviceHasFlakyConnection|null { if (!$json) { return null; } - return new self(level: $json->level ?? null); + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - public function __construct(public float|null $level) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata { /** - * Endpoints associated with the phone. + * Indicates that the Salto KS lock is in Office Mode. Access Codes will not unlock doors. */ - class Endpoints + final class SaltoKsOfficeMode extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Endpoints|null + public static function from_json(mixed $json): SaltoKsOfficeMode|null { if (!$json) { return null; } return new self( - endpoint_id: $json->endpoint_id ?? null, - is_active: $json->is_active ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * ID of the associated endpoint. + * Date and time at which Seam created the warning. */ - public string|null $endpoint_id = null, + string|null $created_at, /** - * Indicated whether the endpoint is active. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public bool|null $is_active = null, - ) {} + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\DormakabaOracodeMetadata { /** - * Predefined time slots for a dormakaba Oracode device. + * Indicates that the Salto KS lock is in Privacy Mode. Access Codes will not unlock doors. */ - class PredefinedTimeSlots + final class SaltoKsPrivacyMode extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): PredefinedTimeSlots|null + public static function from_json(mixed $json): SaltoKsPrivacyMode|null { if (!$json) { return null; } return new self( - check_in_time: $json->check_in_time ?? null, - check_out_time: $json->check_out_time ?? null, - dormakaba_oracode_user_level_id: $json->dormakaba_oracode_user_level_id ?? - null, - dormakaba_oracode_user_level_prefix: $json->dormakaba_oracode_user_level_prefix ?? - null, - is_24_hour: $json->is_24_hour ?? null, - is_biweekly_mode: $json->is_biweekly_mode ?? null, - is_master: $json->is_master ?? null, - is_one_shot: $json->is_one_shot ?? null, - name: $json->name ?? null, - prefix: $json->prefix ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Check in time for a time slot for a dormakaba Oracode device. - */ - public string|null $check_in_time = null, - /** - * Checkout time for a time slot for a dormakaba Oracode device. - */ - public string|null $check_out_time = null, - /** - * ID of a user level for a dormakaba Oracode device. - */ - public string|null $dormakaba_oracode_user_level_id = null, - /** - * Prefix for a user level for a dormakaba Oracode device. - */ - public float|null $dormakaba_oracode_user_level_prefix = null, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a 24-hour time slot. - */ - public bool|null $is_24_hour = null, - /** - * Indicates whether a time slot for a dormakaba Oracode device is in biweekly mode. - */ - public bool|null $is_biweekly_mode = null, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a master time slot. - */ - public bool|null $is_master = null, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a one-shot time slot. + * Date and time at which Seam created the warning. */ - public bool|null $is_one_shot = null, + string|null $created_at, /** - * Name of a time slot for a dormakaba Oracode device. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $name = null, + string|null $message, /** - * Prefix for a time slot for a dormakaba Oracode device. - */ - public float|null $prefix = null, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\MinutMetadata { /** - * Latest sensor values for a Minut device. + * Indicates that the lock is in Privacy Mode. Access codes and remote unlock are blocked until Privacy Mode is disabled. */ - class LatestSensorValues + final class PrivacyMode extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): LatestSensorValues|null + public static function from_json(mixed $json): PrivacyMode|null { if (!$json) { return null; } return new self( - accelerometer_z: isset($json->accelerometer_z) - ? LatestSensorValues\AccelerometerZ::from_json( - $json->accelerometer_z, - ) - : null, - humidity: isset($json->humidity) - ? LatestSensorValues\Humidity::from_json($json->humidity) - : null, - pressure: isset($json->pressure) - ? LatestSensorValues\Pressure::from_json($json->pressure) - : null, - sound: isset($json->sound) - ? LatestSensorValues\Sound::from_json($json->sound) - : null, - temperature: isset($json->temperature) - ? LatestSensorValues\Temperature::from_json( - $json->temperature, - ) - : null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Latest accelerometer Z-axis reading for a Minut device. - */ - public LatestSensorValues\AccelerometerZ|null $accelerometer_z = null, - /** - * Latest humidity reading for a Minut device. - */ - public LatestSensorValues\Humidity|null $humidity = null, - /** - * Latest pressure reading for a Minut device. + * Date and time at which Seam created the warning. */ - public LatestSensorValues\Pressure|null $pressure = null, + string|null $created_at, /** - * Latest sound reading for a Minut device. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public LatestSensorValues\Sound|null $sound = null, + string|null $message, /** - * Latest temperature reading for a Minut device. - */ - public LatestSensorValues\Temperature|null $temperature = null, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues { /** - * Latest accelerometer Z-axis reading for a Minut device. + * 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. */ - class AccelerometerZ + final class SaltoKsSubscriptionLimitAlmostReached extends + \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): AccelerometerZ|null - { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitAlmostReached|null { if (!$json) { return null; } return new self( - time: $json->time ?? null, - value: $json->value ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Time of latest accelerometer Z-axis reading for a Minut device. + * Date and time at which Seam created the warning. */ - public string|null $time = null, + string|null $created_at, /** - * Value of latest accelerometer Z-axis reading for a Minut device. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $value = null, - ) {} + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * Latest humidity reading for a Minut device. + * 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. */ - class Humidity + final class SaltoKsLockAccessCodeSupportRemoved extends + \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Humidity|null - { + public static function from_json( + mixed $json, + ): SaltoKsLockAccessCodeSupportRemoved|null { if (!$json) { return null; } return new self( - time: $json->time ?? null, - value: $json->value ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Time of latest humidity reading for a Minut device. + * Date and time at which Seam created the warning. */ - public string|null $time = null, + string|null $created_at, /** - * Value of latest humidity reading for a Minut device. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $value = null, - ) {} + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * Latest pressure reading for a Minut device. + * 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. */ - class Pressure + final class UnknownIssueWithPhone extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Pressure|null - { + public static function from_json( + mixed $json, + ): UnknownIssueWithPhone|null { if (!$json) { return null; } return new self( - time: $json->time ?? null, - value: $json->value ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Time of latest pressure reading for a Minut device. + * Date and time at which Seam created the warning. */ - public string|null $time = null, + string|null $created_at, /** - * Value of latest pressure reading for a Minut device. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $value = null, - ) {} + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * Latest sound reading for a Minut device. + * Indicates that Seam detected that the Lockly device does not have a time zone configured. Time-bound codes may not work as expected. */ - class Sound + final class LocklyTimeZoneNotConfigured extends + \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Sound|null - { + public static function from_json( + mixed $json, + ): LocklyTimeZoneNotConfigured|null { if (!$json) { return null; } return new self( - time: $json->time ?? null, - value: $json->value ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Time of latest sound reading for a Minut device. + * Date and time at which Seam created the warning. */ - public string|null $time = null, + string|null $created_at, /** - * Value of latest sound reading for a Minut device. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $value = null, - ) {} + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * Latest temperature reading for a Minut device. + * Indicates that Seam does not know the time zone of the Ultraloq device. Set a time zone to enable time-bound access codes. */ - class Temperature + final class UltraloqTimeZoneUnknown extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Temperature|null - { + public static function from_json( + mixed $json, + ): UltraloqTimeZoneUnknown|null { if (!$json) { return null; } return new self( - time: $json->time ?? null, - value: $json->value ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Time of latest temperature reading for a Minut device. + * Date and time at which Seam created the warning. */ - public string|null $time = null, + string|null $created_at, /** - * Value of latest temperature reading for a Minut device. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $value = null, - ) {} + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\TtlockMetadata { /** - * Features for a TTLock device. + * Indicates that Seam does not know the device's time zone. Set a time zone to enable time-bound access codes. */ - class Features + final class TimeZoneUnknown extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Features|null + public static function from_json(mixed $json): TimeZoneUnknown|null { if (!$json) { return null; - } - return new self( - auto_lock_time_config: $json->auto_lock_time_config ?? null, - incomplete_keyboard_passcode: $json->incomplete_keyboard_passcode ?? - null, - lock_command: $json->lock_command ?? null, - passcode: $json->passcode ?? null, - passcode_management: $json->passcode_management ?? null, - unlock_via_gateway: $json->unlock_via_gateway ?? null, - wifi: $json->wifi ?? null, + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Indicates whether a TTLock device supports auto-lock time configuration. - */ - public bool|null $auto_lock_time_config = null, - /** - * Indicates whether a TTLock device supports an incomplete keyboard passcode. - */ - public bool|null $incomplete_keyboard_passcode = null, - /** - * Indicates whether a TTLock device supports the lock command. - */ - public bool|null $lock_command = null, - /** - * Indicates whether a TTLock device supports a passcode. - */ - public bool|null $passcode = null, - /** - * Indicates whether a TTLock device supports passcode management. + * Date and time at which Seam created the warning. */ - public bool|null $passcode_management = null, + string|null $created_at, /** - * Indicates whether a TTLock device supports unlock via gateway. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public bool|null $unlock_via_gateway = null, + string|null $message, /** - * Indicates whether a TTLock device supports Wi-Fi. - */ - public bool|null $wifi = null, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } /** - * Wireless keypads for a TTLock device. + * 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. */ - class WirelessKeypads + final class TimeZoneMismatch extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): WirelessKeypads|null + public static function from_json(mixed $json): TimeZoneMismatch|null { if (!$json) { return null; } return new self( - wireless_keypad_id: $json->wireless_keypad_id ?? null, - wireless_keypad_name: $json->wireless_keypad_name ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * ID for a wireless keypad for a TTLock device. + * Date and time at which Seam created the warning. */ - public float|null $wireless_keypad_id = null, + string|null $created_at, /** - * Name for a wireless keypad for a TTLock device. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $wireless_keypad_name = null, - ) {} + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\OfflineTimeFrameOptions { /** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + * Indicates that the 2N device does not have a time zone configured. Configure a time zone on the device to enable access codes. */ - class TimePairs + final class TwoNDeviceMissingTimezone extends + \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): TimePairs|null - { + public static function from_json( + mixed $json, + ): TwoNDeviceMissingTimezone|null { if (!$json) { return null; } return new self( - display_name: $json->display_name ?? null, - end_time: $json->end_time ?? null, - start_time: $json->start_time ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Label for the start/end time pairing. + * Date and time at which Seam created the warning. */ - public string|null $display_name, + string|null $created_at, /** - * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $end_time, + string|null $message, /** - * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. - */ - public string|null $start_time, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\OnlineTimeFrameOptions { /** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + * Indicates that a hub or relay must be connected to unlock additional capabilities such as remote unlock. */ - class TimePairs + final class HubRequiredForAdditionalCapabilities extends + \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): TimePairs|null - { + public static function from_json( + mixed $json, + ): HubRequiredForAdditionalCapabilities|null { if (!$json) { return null; } return new self( - display_name: $json->display_name ?? null, - end_time: $json->end_time ?? null, - start_time: $json->start_time ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Label for the start/end time pairing. + * Date and time at which Seam created the warning. */ - public string|null $display_name, + string|null $created_at, /** - * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $end_time, + string|null $message, /** - * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. - */ - public string|null $start_time, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\ActiveThermostatSchedule { /** - * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * Indicates a provider-specific issue that may affect device functionality. */ - class Errors + final class ProviderIssue extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Errors|null + public static function from_json(mixed $json): ProviderIssue|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Date and time at which Seam created the error. + * Date and time at which Seam created the warning. */ - public string|null $created_at, + string|null $created_at, /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $error_code, + string|null $message, /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\AvailableClimatePresets { /** - * Metadata specific to the Ecobee climate, if applicable. + * Indicates that the key is in a locker that does not support the access codes API. */ - class EcobeeMetadata + final class KeynestUnsupportedLocker extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): EcobeeMetadata|null - { + public static function from_json( + mixed $json, + ): KeynestUnsupportedLocker|null { if (!$json) { return null; } return new self( - climate_ref: $json->climate_ref ?? null, - is_optimized: $json->is_optimized ?? null, - owner: $json->owner ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Reference to the Ecobee climate, if applicable. + * Date and time at which Seam created the warning. */ - public string|null $climate_ref = null, + string|null $created_at, /** - * Indicates if the climate preset is optimized by Ecobee. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public bool|null $is_optimized = null, + string|null $message, /** - * Indicates whether the climate preset is owned by the user or the system. - */ - public string|null $owner = null, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\CurrentClimateSetting { /** - * Metadata specific to the Ecobee climate, if applicable. + * 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. */ - class EcobeeMetadata + final class AccessoryKeypadSetupRequired extends + \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): EcobeeMetadata|null - { + public static function from_json( + mixed $json, + ): AccessoryKeypadSetupRequired|null { if (!$json) { return null; } return new self( - climate_ref: $json->climate_ref ?? null, - is_optimized: $json->is_optimized ?? null, - owner: $json->owner ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Reference to the Ecobee climate, if applicable. + * Date and time at which Seam created the warning. */ - public string|null $climate_ref = null, + string|null $created_at, /** - * Indicates if the climate preset is optimized by Ecobee. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public bool|null $is_optimized = null, + string|null $message, /** - * Indicates whether the climate preset is owned by the user or the system. - */ - public string|null $owner = null, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\DefaultClimateSetting { /** - * Metadata specific to the Ecobee climate, if applicable. + * Indicates that the device may optimistically be reported as online because the provider does not reliably report its online status. */ - class EcobeeMetadata + final class UnreliableOnlineStatus extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): EcobeeMetadata|null - { + public static function from_json( + mixed $json, + ): UnreliableOnlineStatus|null { if (!$json) { return null; } return new self( - climate_ref: $json->climate_ref ?? null, - is_optimized: $json->is_optimized ?? null, - owner: $json->owner ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Reference to the Ecobee climate, if applicable. + * Date and time at which Seam created the warning. */ - public string|null $climate_ref = null, + string|null $created_at, /** - * Indicates if the climate preset is optimized by Ecobee. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public bool|null $is_optimized = null, + string|null $message, /** - * Indicates whether the climate preset is owned by the user or the system. - */ - public string|null $owner = null, - ) {} + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } } -} -namespace Seam\Resources\Device\Properties\ThermostatDailyPrograms { /** - * Array of thermostat daily program periods. + * Indicates that the device has reached its maximum number of active access codes. Delete existing codes before creating new ones. */ - class Periods + final class MaxAccessCodesReached extends \Seam\Resources\Device\Warnings { - public static function from_json(mixed $json): Periods|null - { + public static function from_json( + mixed $json, + ): MaxAccessCodesReached|null { if (!$json) { return null; } return new self( - climate_preset_key: $json->climate_preset_key ?? null, - starts_at_time: $json->starts_at_time ?? null, + active_access_code_count: $json->active_access_code_count ?? + null, + created_at: $json->created_at ?? null, + max_active_access_code_count: $json->max_active_access_code_count ?? + null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. + * Number of active access codes on the device when the warning was set. */ - public string|null $climate_preset_key, + public int|null $active_access_code_count, /** - * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + * Date and time at which Seam created the warning. */ - public string|null $starts_at_time, - ) {} + string|null $created_at, + /** + * Maximum number of active access codes supported by the device. + */ + public int|null $max_active_access_code_count, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\Device\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case PARTIAL_BACKUP_ACCESS_CODE_POOL = "partial_backup_access_code_pool"; + case MANY_ACTIVE_BACKUP_CODES = "many_active_backup_codes"; + case THIRD_PARTY_INTEGRATION_DETECTED = "third_party_integration_detected"; + case TTLOCK_LOCK_GATEWAY_UNLOCKING_NOT_ENABLED = "ttlock_lock_gateway_unlocking_not_enabled"; + case TTLOCK_WEAK_GATEWAY_SIGNAL = "ttlock_weak_gateway_signal"; + case POWER_SAVING_MODE = "power_saving_mode"; + case TEMPERATURE_THRESHOLD_EXCEEDED = "temperature_threshold_exceeded"; + case DEVICE_COMMUNICATION_DEGRADED = "device_communication_degraded"; + case SCHEDULED_MAINTENANCE_WINDOW = "scheduled_maintenance_window"; + case DEVICE_HAS_FLAKY_CONNECTION = "device_has_flaky_connection"; + case SALTO_KS_OFFICE_MODE = "salto_ks_office_mode"; + case SALTO_KS_PRIVACY_MODE = "salto_ks_privacy_mode"; + case PRIVACY_MODE = "privacy_mode"; + case SALTO_KS_SUBSCRIPTION_LIMIT_ALMOST_REACHED = "salto_ks_subscription_limit_almost_reached"; + case SALTO_KS_LOCK_ACCESS_CODE_SUPPORT_REMOVED = "salto_ks_lock_access_code_support_removed"; + case UNKNOWN_ISSUE_WITH_PHONE = "unknown_issue_with_phone"; + case LOCKLY_TIME_ZONE_NOT_CONFIGURED = "lockly_time_zone_not_configured"; + case ULTRALOQ_TIME_ZONE_UNKNOWN = "ultraloq_time_zone_unknown"; + case TIME_ZONE_UNKNOWN = "time_zone_unknown"; + case TIME_ZONE_MISMATCH = "time_zone_mismatch"; + case TWO_N_DEVICE_MISSING_TIMEZONE = "two_n_device_missing_timezone"; + case HUB_REQUIRED_FOR_ADDITIONAL_CAPABILITIES = "hub_required_for_additional_capabilities"; + case PROVIDER_ISSUE = "provider_issue"; + case KEYNEST_UNSUPPORTED_LOCKER = "keynest_unsupported_locker"; + case ACCESSORY_KEYPAD_SETUP_REQUIRED = "accessory_keypad_setup_required"; + case UNRELIABLE_ONLINE_STATUS = "unreliable_online_status"; + case MAX_ACCESS_CODES_REACHED = "max_access_codes_reached"; } } diff --git a/src/Resources/DeviceProvider.php b/src/Resources/DeviceProvider.php index 41b60ac9..17529829 100644 --- a/src/Resources/DeviceProvider.php +++ b/src/Resources/DeviceProvider.php @@ -9,6 +9,10 @@ public static function from_json(mixed $json): DeviceProvider|null return null; } return new self( + device_provider_name: $json->device_provider_name ?? null, + display_name: $json->display_name ?? null, + image_url: $json->image_url ?? null, + provider_categories: $json->provider_categories ?? null, can_configure_auto_lock: $json->can_configure_auto_lock ?? null, can_hvac_cool: $json->can_hvac_cool ?? null, can_hvac_heat: $json->can_hvac_heat ?? null, @@ -39,16 +43,14 @@ public static function from_json(mixed $json): DeviceProvider|null can_simulate_removal: $json->can_simulate_removal ?? null, can_turn_off_hvac: $json->can_turn_off_hvac ?? null, can_unlock_with_code: $json->can_unlock_with_code ?? null, - device_provider_name: $json->device_provider_name ?? null, - display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, - provider_categories: $json->provider_categories ?? null, ); } public function __construct( /** * Name of the device provider. + * + * @var value-of<\Seam\Resources\DeviceProvider\DeviceProviderName>|string|null */ public string|null $device_provider_name, /** @@ -61,6 +63,8 @@ public function __construct( public string|null $image_url, /** * List of provider categories to which the device provider belongs, such as `stable`, `consumer_smartlocks`, `thermostats`, and so on. + * + * @var list|null */ public array|null $provider_categories, /** @@ -146,3 +150,73 @@ public function __construct( ) {} } } + +namespace Seam\Resources\DeviceProvider { + enum DeviceProviderName: string + { + case HOTEK = "hotek"; + case DORMAKABA_COMMUNITY = "dormakaba_community"; + case LEGIC_CONNECT = "legic_connect"; + case AKUVOX = "akuvox"; + case AUGUST = "august"; + case AVIGILON_ALTA = "avigilon_alta"; + case BRIVO = "brivo"; + case BUTTERFLYMX = "butterflymx"; + case SCHLAGE = "schlage"; + case SMARTTHINGS = "smartthings"; + case YALE = "yale"; + case GENIE = "genie"; + case DOORKING = "doorking"; + case SALTO = "salto"; + case SALTO_KS = "salto_ks"; + case SALTO_KS_ACCEPT = "salto_ks_accept"; + case LOCKLY = "lockly"; + case TTLOCK = "ttlock"; + case LINEAR = "linear"; + case NOISEAWARE = "noiseaware"; + case NUKI = "nuki"; + case IGLOO = "igloo"; + case KWIKSET = "kwikset"; + case MINUT = "minut"; + case MY_2N = "my_2n"; + case CONTROLBYWEB = "controlbyweb"; + case NEST = "nest"; + case IGLOOHOME = "igloohome"; + case ECOBEE = "ecobee"; + case FOUR_SUITES = "four_suites"; + case DORMAKABA_ORACODE = "dormakaba_oracode"; + case PTI = "pti"; + case WYZE = "wyze"; + case SEAM_PASSPORT = "seam_passport"; + case VISIONLINE = "visionline"; + case ASSA_ABLOY_CREDENTIAL_SERVICE = "assa_abloy_credential_service"; + case TEDEE = "tedee"; + case HONEYWELL_RESIDEO = "honeywell_resideo"; + case FIRST_ALERT = "first_alert"; + case LATCH = "latch"; + case AKILES = "akiles"; + case ASSA_ABLOY_VOSTIO = "assa_abloy_vostio"; + case ASSA_ABLOY_VOSTIO_CREDENTIAL_SERVICE = "assa_abloy_vostio_credential_service"; + case TADO = "tado"; + case SALTO_SPACE = "salto_space"; + case SENSI = "sensi"; + case KEYNEST = "keynest"; + case KORELOCK = "korelock"; + case KEYINCODE = "keyincode"; + case DORMAKABA_AMBIANCE = "dormakaba_ambiance"; + case ULTRALOQ = "ultraloq"; + case YACAN = "yacan"; + case DUSAW = "dusaw"; + case SIFELY = "sifely"; + case THIRTY_THREE_LOCK = "thirty_three_lock"; + case RING = "ring"; + case ICAL = "ical"; + case LODGIFY = "lodgify"; + case HOSTAWAY = "hostaway"; + case GUESTY = "guesty"; + case ACUITY_SCHEDULING = "acuity_scheduling"; + case OMNITEC = "omnitec"; + case KISI = "kisi"; + case AQARA = "aqara"; + } +} diff --git a/src/Resources/Event.php b/src/Resources/Event.php index a7119251..24816fc4 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -1,6 +1,9 @@ Event\AccessCodeErrors::from_json($a), - $json->access_code_errors ?? [], + $discriminant = is_string($json->event_type ?? null) + ? \Seam\Resources\Event\EventType::tryFrom($json->event_type) + : null; + + return match ($discriminant) { + \Seam\Resources\Event\EventType::ACCESS_CODE_CREATED + => \Seam\Resources\Event\AccessCodeCreated::from_json( + $json, ), - access_code_id: $json->access_code_id ?? null, - access_code_is_managed: $json->access_code_is_managed ?? null, - access_code_warnings: array_map( - fn($a) => Event\AccessCodeWarnings::from_json($a), - $json->access_code_warnings ?? [], + \Seam\Resources\Event\EventType::ACCESS_CODE_CHANGED + => \Seam\Resources\Event\AccessCodeChanged::from_json( + $json, ), - access_grant_id: $json->access_grant_id ?? null, - access_grant_ids: $json->access_grant_ids ?? null, - access_grant_key: $json->access_grant_key ?? null, - access_grant_keys: $json->access_grant_keys ?? null, - access_method_id: $json->access_method_id ?? null, - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_encoder_id: $json->acs_encoder_id ?? null, - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_entrance_ids: $json->acs_entrance_ids ?? null, - acs_system_errors: array_map( - fn($a) => Event\AcsSystemErrors::from_json($a), - $json->acs_system_errors ?? [], + \Seam\Resources\Event\EventType::ACCESS_CODE_NAME_CHANGED + => \Seam\Resources\Event\AccessCodeNameChanged::from_json( + $json, ), - acs_system_id: $json->acs_system_id ?? null, - acs_system_warnings: array_map( - fn($a) => Event\AcsSystemWarnings::from_json($a), - $json->acs_system_warnings ?? [], + \Seam\Resources\Event\EventType::ACCESS_CODE_CODE_CHANGED + => \Seam\Resources\Event\AccessCodeCodeChanged::from_json( + $json, ), - acs_user_id: $json->acs_user_id ?? null, - action_attempt_id: $json->action_attempt_id ?? null, - action_type: $json->action_type ?? null, - activation_reason: $json->activation_reason ?? null, - backup_access_code_id: $json->backup_access_code_id ?? null, - battery_level: $json->battery_level ?? null, - battery_status: $json->battery_status ?? null, - change_reason: $json->change_reason ?? null, - changed_properties: array_map( - fn($c) => Event\ChangedProperties::from_json($c), - $json->changed_properties ?? [], + \Seam\Resources\Event\EventType::ACCESS_CODE_TIME_FRAME_CHANGED + => \Seam\Resources\Event\AccessCodeTimeFrameChanged::from_json( + $json, ), - client_session_id: $json->client_session_id ?? null, - climate_preset_key: $json->climate_preset_key ?? null, - code: $json->code ?? null, - connect_webview_id: $json->connect_webview_id ?? null, - connected_account_custom_metadata: $json->connected_account_custom_metadata ?? - null, - connected_account_errors: array_map( - fn($c) => Event\ConnectedAccountErrors::from_json($c), - $json->connected_account_errors ?? [], + \Seam\Resources\Event\EventType::ACCESS_CODE_MUTATIONS_REQUESTED + => \Seam\Resources\Event\AccessCodeMutationsRequested::from_json( + $json, ), - connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( - fn($c) => Event\ConnectedAccountWarnings::from_json($c), - $json->connected_account_warnings ?? [], + \Seam\Resources\Event\EventType::ACCESS_CODE_SCHEDULED_ON_DEVICE + => \Seam\Resources\Event\AccessCodeScheduledOnDevice::from_json( + $json, ), - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? - null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - created_at: $json->created_at ?? null, - customer_key: $json->customer_key ?? null, - description: $json->description ?? null, - desired_temperature_celsius: $json->desired_temperature_celsius ?? - null, - desired_temperature_fahrenheit: $json->desired_temperature_fahrenheit ?? - null, - device_custom_metadata: $json->device_custom_metadata ?? null, - device_errors: array_map( - fn($d) => Event\DeviceErrors::from_json($d), - $json->device_errors ?? [], + \Seam\Resources\Event\EventType::ACCESS_CODE_SET_ON_DEVICE + => \Seam\Resources\Event\AccessCodeSetOnDevice::from_json( + $json, ), - device_id: $json->device_id ?? null, - device_ids: $json->device_ids ?? null, - device_name: $json->device_name ?? null, - device_warnings: array_map( - fn($d) => Event\DeviceWarnings::from_json($d), - $json->device_warnings ?? [], + \Seam\Resources\Event\EventType::ACCESS_CODE_REMOVED_FROM_DEVICE + => \Seam\Resources\Event\AccessCodeRemovedFromDevice::from_json( + $json, ), - ends_at: $json->ends_at ?? null, - error_code: $json->error_code ?? null, - error_message: $json->error_message ?? null, - event_description: $json->event_description ?? null, + \Seam\Resources\Event\EventType::ACCESS_CODE_DELAY_IN_SETTING_ON_DEVICE + => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_FAILED_TO_SET_ON_DEVICE + => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_DELETED + => \Seam\Resources\Event\AccessCodeDeleted::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_DELAY_IN_REMOVING_FROM_DEVICE + => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_FAILED_TO_REMOVE_FROM_DEVICE + => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_MODIFIED_EXTERNAL_TO_SEAM + => \Seam\Resources\Event\AccessCodeModifiedExternalToSeam::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_DELETED_EXTERNAL_TO_SEAM + => \Seam\Resources\Event\AccessCodeDeletedExternalToSeam::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_BACKUP_ACCESS_CODE_PULLED + => \Seam\Resources\Event\AccessCodeBackupAccessCodePulled::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_UNMANAGED_CONVERTED_TO_MANAGED + => \Seam\Resources\Event\AccessCodeUnmanagedConvertedToManaged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_UNMANAGED_FAILED_TO_CONVERT_TO_MANAGED + => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_UNMANAGED_CREATED + => \Seam\Resources\Event\AccessCodeUnmanagedCreated::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_CODE_UNMANAGED_REMOVED + => \Seam\Resources\Event\AccessCodeUnmanagedRemoved::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_GRANT_CREATED + => \Seam\Resources\Event\AccessGrantCreated::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_GRANT_DELETED + => \Seam\Resources\Event\AccessGrantDeleted::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_GRANT_ACCESS_GRANTED_TO_ALL_DOORS + => \Seam\Resources\Event\AccessGrantAccessGrantedToAllDoors::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_GRANT_ACCESS_GRANTED_TO_DOOR + => \Seam\Resources\Event\AccessGrantAccessGrantedToDoor::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_GRANT_ACCESS_TO_DOOR_LOST + => \Seam\Resources\Event\AccessGrantAccessToDoorLost::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_GRANT_ACCESS_TIMES_CHANGED + => \Seam\Resources\Event\AccessGrantAccessTimesChanged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_GRANT_COULD_NOT_CREATE_REQUESTED_ACCESS_METHODS + => \Seam\Resources\Event\AccessGrantCouldNotCreateRequestedAccessMethods::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_METHOD_ISSUED + => \Seam\Resources\Event\AccessMethodIssued::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_METHOD_REVOKED + => \Seam\Resources\Event\AccessMethodRevoked::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_METHOD_CARD_ENCODING_REQUIRED + => \Seam\Resources\Event\AccessMethodCardEncodingRequired::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_METHOD_DELETED + => \Seam\Resources\Event\AccessMethodDeleted::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_METHOD_REISSUED + => \Seam\Resources\Event\AccessMethodReissued::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_METHOD_CREATED + => \Seam\Resources\Event\AccessMethodCreated::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_METHOD_DELAY_IN_ISSUING + => \Seam\Resources\Event\AccessMethodDelayInIssuing::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACCESS_METHOD_FAILED_TO_ISSUE + => \Seam\Resources\Event\AccessMethodFailedToIssue::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_SYSTEM_CONNECTED + => \Seam\Resources\Event\AcsSystemConnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_SYSTEM_ADDED + => \Seam\Resources\Event\AcsSystemAdded::from_json($json), + \Seam\Resources\Event\EventType::ACS_SYSTEM_DISCONNECTED + => \Seam\Resources\Event\AcsSystemDisconnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_CREDENTIAL_DELETED + => \Seam\Resources\Event\AcsCredentialDeleted::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_CREDENTIAL_ISSUED + => \Seam\Resources\Event\AcsCredentialIssued::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_CREDENTIAL_REISSUED + => \Seam\Resources\Event\AcsCredentialReissued::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_CREDENTIAL_INVALIDATED + => \Seam\Resources\Event\AcsCredentialInvalidated::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_USER_CREATED + => \Seam\Resources\Event\AcsUserCreated::from_json($json), + \Seam\Resources\Event\EventType::ACS_USER_DELETED + => \Seam\Resources\Event\AcsUserDeleted::from_json($json), + \Seam\Resources\Event\EventType::ACS_ENCODER_ADDED + => \Seam\Resources\Event\AcsEncoderAdded::from_json($json), + \Seam\Resources\Event\EventType::ACS_ENCODER_REMOVED + => \Seam\Resources\Event\AcsEncoderRemoved::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_ACCESS_GROUP_DELETED + => \Seam\Resources\Event\AcsAccessGroupDeleted::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACS_ENTRANCE_ADDED + => \Seam\Resources\Event\AcsEntranceAdded::from_json($json), + \Seam\Resources\Event\EventType::ACS_ENTRANCE_REMOVED + => \Seam\Resources\Event\AcsEntranceRemoved::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CLIENT_SESSION_DELETED + => \Seam\Resources\Event\ClientSessionDeleted::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECTED_ACCOUNT_CONNECTED + => \Seam\Resources\Event\ConnectedAccountConnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECTED_ACCOUNT_CREATED + => \Seam\Resources\Event\ConnectedAccountCreated::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECTED_ACCOUNT_SUCCESSFUL_LOGIN + => \Seam\Resources\Event\ConnectedAccountSuccessfulLogin::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECTED_ACCOUNT_DISCONNECTED + => \Seam\Resources\Event\ConnectedAccountDisconnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECTED_ACCOUNT_COMPLETED_FIRST_SYNC + => \Seam\Resources\Event\ConnectedAccountCompletedFirstSync::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECTED_ACCOUNT_DELETED + => \Seam\Resources\Event\ConnectedAccountDeleted::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECTED_ACCOUNT_COMPLETED_FIRST_SYNC_AFTER_RECONNECTION + => \Seam\Resources\Event\ConnectedAccountCompletedFirstSyncAfterReconnection::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECTED_ACCOUNT_REAUTHORIZATION_REQUESTED + => \Seam\Resources\Event\ConnectedAccountReauthorizationRequested::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACTION_ATTEMPT_LOCK_DOOR_SUCCEEDED + => \Seam\Resources\Event\ActionAttemptLockDoorSucceeded::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACTION_ATTEMPT_LOCK_DOOR_FAILED + => \Seam\Resources\Event\ActionAttemptLockDoorFailed::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACTION_ATTEMPT_UNLOCK_DOOR_SUCCEEDED + => \Seam\Resources\Event\ActionAttemptUnlockDoorSucceeded::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACTION_ATTEMPT_UNLOCK_DOOR_FAILED + => \Seam\Resources\Event\ActionAttemptUnlockDoorFailed::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACTION_ATTEMPT_SIMULATE_KEYPAD_CODE_ENTRY_SUCCEEDED + => \Seam\Resources\Event\ActionAttemptSimulateKeypadCodeEntrySucceeded::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACTION_ATTEMPT_SIMULATE_KEYPAD_CODE_ENTRY_FAILED + => \Seam\Resources\Event\ActionAttemptSimulateKeypadCodeEntryFailed::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACTION_ATTEMPT_SIMULATE_MANUAL_LOCK_VIA_KEYPAD_SUCCEEDED + => \Seam\Resources\Event\ActionAttemptSimulateManualLockViaKeypadSucceeded::from_json( + $json, + ), + \Seam\Resources\Event\EventType::ACTION_ATTEMPT_SIMULATE_MANUAL_LOCK_VIA_KEYPAD_FAILED + => \Seam\Resources\Event\ActionAttemptSimulateManualLockViaKeypadFailed::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECT_WEBVIEW_LOGIN_SUCCEEDED + => \Seam\Resources\Event\ConnectWebviewLoginSucceeded::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CONNECT_WEBVIEW_LOGIN_FAILED + => \Seam\Resources\Event\ConnectWebviewLoginFailed::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_CONNECTED + => \Seam\Resources\Event\DeviceConnected::from_json($json), + \Seam\Resources\Event\EventType::DEVICE_ADDED + => \Seam\Resources\Event\DeviceAdded::from_json($json), + \Seam\Resources\Event\EventType::DEVICE_CONVERTED_TO_UNMANAGED + => \Seam\Resources\Event\DeviceConvertedToUnmanaged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_UNMANAGED_CONVERTED_TO_MANAGED + => \Seam\Resources\Event\DeviceUnmanagedConvertedToManaged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_UNMANAGED_CONNECTED + => \Seam\Resources\Event\DeviceUnmanagedConnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_DISCONNECTED + => \Seam\Resources\Event\DeviceDisconnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_UNMANAGED_DISCONNECTED + => \Seam\Resources\Event\DeviceUnmanagedDisconnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_TAMPERED + => \Seam\Resources\Event\DeviceTampered::from_json($json), + \Seam\Resources\Event\EventType::DEVICE_LOW_BATTERY + => \Seam\Resources\Event\DeviceLowBattery::from_json($json), + \Seam\Resources\Event\EventType::DEVICE_BATTERY_STATUS_CHANGED + => \Seam\Resources\Event\DeviceBatteryStatusChanged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_REMOVED + => \Seam\Resources\Event\DeviceRemoved::from_json($json), + \Seam\Resources\Event\EventType::DEVICE_DELETED + => \Seam\Resources\Event\DeviceDeleted::from_json($json), + \Seam\Resources\Event\EventType::DEVICE_THIRD_PARTY_INTEGRATION_DETECTED + => \Seam\Resources\Event\DeviceThirdPartyIntegrationDetected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_THIRD_PARTY_INTEGRATION_NO_LONGER_DETECTED + => \Seam\Resources\Event\DeviceThirdPartyIntegrationNoLongerDetected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_SALTO_PRIVACY_MODE_ACTIVATED + => \Seam\Resources\Event\DeviceSaltoPrivacyModeActivated::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_SALTO_PRIVACY_MODE_DEACTIVATED + => \Seam\Resources\Event\DeviceSaltoPrivacyModeDeactivated::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_CONNECTION_BECAME_FLAKY + => \Seam\Resources\Event\DeviceConnectionBecameFlaky::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_CONNECTION_STABILIZED + => \Seam\Resources\Event\DeviceConnectionStabilized::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_ERROR_SUBSCRIPTION_REQUIRED + => \Seam\Resources\Event\DeviceErrorSubscriptionRequired::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_ERROR_SUBSCRIPTION_REQUIRED_RESOLVED + => \Seam\Resources\Event\DeviceErrorSubscriptionRequiredResolved::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_ACCESSORY_KEYPAD_CONNECTED + => \Seam\Resources\Event\DeviceAccessoryKeypadConnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_ACCESSORY_KEYPAD_DISCONNECTED + => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected::from_json( + $json, + ), + \Seam\Resources\Event\EventType::NOISE_SENSOR_NOISE_THRESHOLD_TRIGGERED + => \Seam\Resources\Event\NoiseSensorNoiseThresholdTriggered::from_json( + $json, + ), + \Seam\Resources\Event\EventType::LOCK_LOCKED + => \Seam\Resources\Event\LockLocked::from_json($json), + \Seam\Resources\Event\EventType::LOCK_UNLOCKED + => \Seam\Resources\Event\LockUnlocked::from_json($json), + \Seam\Resources\Event\EventType::LOCK_ACCESS_DENIED + => \Seam\Resources\Event\LockAccessDenied::from_json($json), + \Seam\Resources\Event\EventType::THERMOSTAT_CLIMATE_PRESET_ACTIVATED + => \Seam\Resources\Event\ThermostatClimatePresetActivated::from_json( + $json, + ), + \Seam\Resources\Event\EventType::THERMOSTAT_MANUALLY_ADJUSTED + => \Seam\Resources\Event\ThermostatManuallyAdjusted::from_json( + $json, + ), + \Seam\Resources\Event\EventType::THERMOSTAT_TEMPERATURE_THRESHOLD_EXCEEDED + => \Seam\Resources\Event\ThermostatTemperatureThresholdExceeded::from_json( + $json, + ), + \Seam\Resources\Event\EventType::THERMOSTAT_TEMPERATURE_THRESHOLD_NO_LONGER_EXCEEDED + => \Seam\Resources\Event\ThermostatTemperatureThresholdNoLongerExceeded::from_json( + $json, + ), + \Seam\Resources\Event\EventType::THERMOSTAT_TEMPERATURE_REACHED_SET_POINT + => \Seam\Resources\Event\ThermostatTemperatureReachedSetPoint::from_json( + $json, + ), + \Seam\Resources\Event\EventType::THERMOSTAT_TEMPERATURE_CHANGED + => \Seam\Resources\Event\ThermostatTemperatureChanged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::DEVICE_NAME_CHANGED + => \Seam\Resources\Event\DeviceNameChanged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::CAMERA_ACTIVATED + => \Seam\Resources\Event\CameraActivated::from_json($json), + \Seam\Resources\Event\EventType::DEVICE_DOORBELL_RANG + => \Seam\Resources\Event\DeviceDoorbellRang::from_json( + $json, + ), + \Seam\Resources\Event\EventType::PHONE_DEACTIVATED + => \Seam\Resources\Event\PhoneDeactivated::from_json($json), + \Seam\Resources\Event\EventType::SPACE_DEVICE_MEMBERSHIP_CHANGED + => \Seam\Resources\Event\SpaceDeviceMembershipChanged::from_json( + $json, + ), + \Seam\Resources\Event\EventType::SPACE_CREATED + => \Seam\Resources\Event\SpaceCreated::from_json($json), + \Seam\Resources\Event\EventType::SPACE_DELETED + => \Seam\Resources\Event\SpaceDeleted::from_json($json), + default => new self( + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which the event was created. + */ + public string|null $created_at, + /** + * ID of the event. + */ + public string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + public string|null $event_type, + /** + * Date and time at which the event occurred. + */ + public string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + public string|null $workspace_id, + /** + * 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. + */ + public string|null $event_description = null, + ) {} + } +} + +namespace Seam\Resources\Event { + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was created. + */ + final class AccessCodeCreated extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessCodeCreated|null + { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, - fan_mode_setting: $json->fan_mode_setting ?? null, - from: isset($json->from) - ? Event\From::from_json($json->from) - : null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? - null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - image_url: $json->image_url ?? null, - is_backup_code: $json->is_backup_code ?? null, - is_fallback_climate_preset: $json->is_fallback_climate_preset ?? - null, - is_via_bluetooth: $json->is_via_bluetooth ?? null, - is_via_nfc: $json->is_via_nfc ?? null, - lower_limit_celsius: $json->lower_limit_celsius ?? null, - lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, - method: $json->method ?? null, - minut_metadata: $json->minut_metadata ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - motion_sub_type: $json->motion_sub_type ?? null, - noise_level_decibels: $json->noise_level_decibels ?? null, - noise_level_nrs: $json->noise_level_nrs ?? null, - noise_threshold_id: $json->noise_threshold_id ?? null, - noise_threshold_name: $json->noise_threshold_name ?? null, - noiseaware_metadata: $json->noiseaware_metadata ?? null, occurred_at: $json->occurred_at ?? null, - reason: isset($json->reason) - ? Event\Reason::from_json($json->reason) - : null, - requested_mutations: array_map( - fn($r) => Event\RequestedMutations::from_json($r), - $json->requested_mutations ?? [], - ), - space_id: $json->space_id ?? null, - space_key: $json->space_key ?? null, - starts_at: $json->starts_at ?? null, - status: $json->status ?? null, - temperature_celsius: $json->temperature_celsius ?? null, - temperature_fahrenheit: $json->temperature_fahrenheit ?? null, - thermostat_schedule_id: $json->thermostat_schedule_id ?? null, - to: isset($json->to) ? Event\To::from_json($json->to) : null, - upper_limit_celsius: $json->upper_limit_celsius ?? null, - upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, - video_url: $json->video_url ?? null, workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was changed. + */ + final class AccessCodeChanged extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessCodeChanged|null + { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + change_reason: $json->change_reason ?? null, + changed_properties: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeChanged\ChangedProperties::from_json( + $c, + ), + $json->changed_properties ?? [], + ), + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). + */ + public string|null $change_reason = null, + /** + * List of properties that changed on the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeChanged\ChangedProperties>|null + */ + public array|null $changed_properties = null, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * The name of an [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was changed on the device. + */ + final class AccessCodeNameChanged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeNameChanged|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + description: $json->description ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + from: isset($json->from) + ? \Seam\Resources\Event\AccessCodeNameChanged\From::from_json( + $json->from, + ) + : null, + occurred_at: $json->occurred_at ?? null, + to: isset($json->to) + ? \Seam\Resources\Event\AccessCodeNameChanged\To::from_json( + $json->to, + ) + : null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Human-readable description of the change and its source. + */ + public string|null $description, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Previous access code name configuration. + */ + public \Seam\Resources\Event\AccessCodeNameChanged\From|null $from, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * New access code name configuration. + */ + public \Seam\Resources\Event\AccessCodeNameChanged\To|null $to, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * The pin code of an [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was changed on the device. + */ + final class AccessCodeCodeChanged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeCodeChanged|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + description: $json->description ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + from: isset($json->from) + ? \Seam\Resources\Event\AccessCodeCodeChanged\From::from_json( + $json->from, + ) + : null, + occurred_at: $json->occurred_at ?? null, + to: isset($json->to) + ? \Seam\Resources\Event\AccessCodeCodeChanged\To::from_json( + $json->to, + ) + : null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Human-readable description of the change and its source. + */ + public string|null $description, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Previous pin code configuration. + */ + public \Seam\Resources\Event\AccessCodeCodeChanged\From|null $from, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * New pin code configuration. + */ + public \Seam\Resources\Event\AccessCodeCodeChanged\To|null $to, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * The time frame of an [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was changed on the device. + */ + final class AccessCodeTimeFrameChanged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeTimeFrameChanged|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + description: $json->description ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + from: isset($json->from) + ? \Seam\Resources\Event\AccessCodeTimeFrameChanged\From::from_json( + $json->from, + ) + : null, + occurred_at: $json->occurred_at ?? null, + to: isset($json->to) + ? \Seam\Resources\Event\AccessCodeTimeFrameChanged\To::from_json( + $json->to, + ) + : null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Human-readable description of the change and its source. + */ + public string|null $description, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Previous time frame configuration. + */ + public \Seam\Resources\Event\AccessCodeTimeFrameChanged\From|null $from, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * New time frame configuration. + */ + public \Seam\Resources\Event\AccessCodeTimeFrameChanged\To|null $to, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * Mutations were requested on an [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). This event fires at request time, before the change is confirmed on the device. + */ + final class AccessCodeMutationsRequested extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeMutationsRequested|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + requested_mutations: array_map( + fn( + $r, + ) => \Seam\Resources\Event\AccessCodeMutationsRequested\RequestedMutations::from_json( + $r, + ), + $json->requested_mutations ?? [], + ), + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Array of mutations requested on the access code, each containing the mutation type and from/to values. + * + * @var list<\Seam\Resources\Event\AccessCodeMutationsRequested\RequestedMutations> + */ + public array $requested_mutations, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was [scheduled natively](https://docs.seam.co/low-level-apis/smart-locks/access-codes#native-scheduling) on a device. + */ + final class AccessCodeScheduledOnDevice extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeScheduledOnDevice|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * Code for the affected access code. + */ + public string|null $code, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was set on a device. + */ + final class AccessCodeSetOnDevice extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeSetOnDevice|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * Code for the affected access code. + */ + public string|null $code, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was removed from a device. + */ + final class AccessCodeRemovedFromDevice extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeRemovedFromDevice|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * There was an unusually long delay in setting an [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) on a device. + */ + final class AccessCodeDelayInSettingOnDevice extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeDelayInSettingOnDevice|null { + if (!$json) { + return null; + } + return new self( + access_code_errors: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\AccessCodeErrors::from_json( + $a, + ), + $json->access_code_errors ?? [], + ), + access_code_id: $json->access_code_id ?? null, + access_code_warnings: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\AccessCodeWarnings::from_json( + $a, + ), + $json->access_code_warnings ?? [], + ), + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\AccessCodeErrors> + */ + public array $access_code_errors, + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * Warnings associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\AccessCodeWarnings> + */ + public array $access_code_warnings, + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\DeviceWarnings> + */ + public array $device_warnings, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) failed to be set on a device. + */ + final class AccessCodeFailedToSetOnDevice extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeFailedToSetOnDevice|null { + if (!$json) { + return null; + } + return new self( + access_code_errors: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\AccessCodeErrors::from_json( + $a, + ), + $json->access_code_errors ?? [], + ), + access_code_id: $json->access_code_id ?? null, + access_code_warnings: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\AccessCodeWarnings::from_json( + $a, + ), + $json->access_code_warnings ?? [], + ), + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToSetOnDevice\AccessCodeErrors> + */ + public array $access_code_errors, + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * Warnings associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToSetOnDevice\AccessCodeWarnings> + */ + public array $access_code_warnings, + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToSetOnDevice\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToSetOnDevice\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToSetOnDevice\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToSetOnDevice\DeviceWarnings> + */ + public array $device_warnings, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was deleted. + */ + final class AccessCodeDeleted extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessCodeDeleted|null + { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * Code for the affected access code. + */ + public string|null $code, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * There was an unusually long delay in removing an [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) from a device. + */ + final class AccessCodeDelayInRemovingFromDevice extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeDelayInRemovingFromDevice|null { + if (!$json) { + return null; + } + return new self( + access_code_errors: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\AccessCodeErrors::from_json( + $a, + ), + $json->access_code_errors ?? [], + ), + access_code_id: $json->access_code_id ?? null, + access_code_warnings: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\AccessCodeWarnings::from_json( + $a, + ), + $json->access_code_warnings ?? [], + ), + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\AccessCodeErrors> + */ + public array $access_code_errors, + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * Warnings associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\AccessCodeWarnings> + */ + public array $access_code_warnings, + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\DeviceWarnings> + */ + public array $device_warnings, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) failed to be removed from a device. + */ + final class AccessCodeFailedToRemoveFromDevice extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeFailedToRemoveFromDevice|null { + if (!$json) { + return null; + } + return new self( + access_code_errors: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\AccessCodeErrors::from_json( + $a, + ), + $json->access_code_errors ?? [], + ), + access_code_id: $json->access_code_id ?? null, + access_code_warnings: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\AccessCodeWarnings::from_json( + $a, + ), + $json->access_code_warnings ?? [], + ), + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\AccessCodeErrors> + */ + public array $access_code_errors, + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * Warnings associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\AccessCodeWarnings> + */ + public array $access_code_warnings, + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\DeviceWarnings> + */ + public array $device_warnings, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was modified outside of Seam. + */ + final class AccessCodeModifiedExternalToSeam extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeModifiedExternalToSeam|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) was deleted outside of Seam. + */ + final class AccessCodeDeletedExternalToSeam extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeDeletedExternalToSeam|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [backup access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) was pulled from the backup access code pool and set on a device. + */ + final class AccessCodeBackupAccessCodePulled extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeBackupAccessCodePulled|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + backup_access_code_id: $json->backup_access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the backup access code that was pulled from the pool. + */ + public string|null $backup_access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [unmanaged access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) was converted successfully to a managed access code. + */ + final class AccessCodeUnmanagedConvertedToManaged extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeUnmanagedConvertedToManaged|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [unmanaged access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) failed to be converted to a managed access code. + */ + final class AccessCodeUnmanagedFailedToConvertToManaged extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeUnmanagedFailedToConvertToManaged|null { + if (!$json) { + return null; + } + return new self( + access_code_errors: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\AccessCodeErrors::from_json( + $a, + ), + $json->access_code_errors ?? [], + ), + access_code_id: $json->access_code_id ?? null, + access_code_warnings: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\AccessCodeWarnings::from_json( + $a, + ), + $json->access_code_warnings ?? [], + ), + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\AccessCodeErrors> + */ + public array $access_code_errors, + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * Warnings associated with the access code. + * + * @var list<\Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\AccessCodeWarnings> + */ + public array $access_code_warnings, + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\DeviceWarnings> + */ + public array $device_warnings, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [unmanaged access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) was created on a device. + */ + final class AccessCodeUnmanagedCreated extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeUnmanagedCreated|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [unmanaged access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) was removed from a device. + */ + final class AccessCodeUnmanagedRemoved extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessCodeUnmanagedRemoved|null { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access code. + */ + public string|null $access_code_id, + /** + * ID of the connected account associated with the affected access code. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the device associated with the affected access code. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An Access Grant was created. + */ + final class AccessGrantCreated extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessGrantCreated|null + { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An Access Grant was deleted. + */ + final class AccessGrantDeleted extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessGrantDeleted|null + { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * All access requested for an Access Grant was successfully granted. + */ + final class AccessGrantAccessGrantedToAllDoors extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessGrantAccessGrantedToAllDoors|null { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * Access requested as part of an Access Grant to a particular door was successfully granted. + */ + final class AccessGrantAccessGrantedToDoor extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessGrantAccessGrantedToDoor|null { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + acs_entrance_id: $json->acs_entrance_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * ID of the affected [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $acs_entrance_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * Access to a particular door that was requested as part of an Access Grant was lost. + */ + final class AccessGrantAccessToDoorLost extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessGrantAccessToDoorLost|null { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + acs_entrance_id: $json->acs_entrance_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * ID of the affected [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $acs_entrance_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An Access Grant's start or end time was changed. + */ + final class AccessGrantAccessTimesChanged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessGrantAccessTimesChanged|null { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_key: $json->access_grant_key ?? null, + ends_at: $json->ends_at ?? null, + event_description: $json->event_description ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Key of the affected Access Grant (if present). + */ + public string|null $access_grant_key = null, + /** + * The new end time for the access grant. + */ + public string|null $ends_at = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * The new start time for the access grant. + */ + public string|null $starts_at = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * One or more requested access methods could not be created for an Access Grant. + */ + final class AccessGrantCouldNotCreateRequestedAccessMethods extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessGrantCouldNotCreateRequestedAccessMethods|null { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + created_at: $json->created_at ?? null, + error_message: $json->error_message ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Description of why the access methods could not be created. + */ + public string|null $error_message, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + /** + * 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. + * + * @var list|null + */ + public array|null $missing_device_ids = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An access method was issued. + */ + final class AccessMethodIssued extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessMethodIssued|null + { + if (!$json) { + return null; + } + return new self( + access_grant_ids: $json->access_grant_ids ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + code: $json->code ?? null, + event_description: $json->event_description ?? null, + is_backup_code: $json->is_backup_code ?? null, + ); + } + + public function __construct( + /** + * IDs of the access grants associated with this access method. + * + * @var list|null + */ + public array|null $access_grant_ids, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Keys of the access grants associated with this access method (if present). + * + * @var list|null + */ + public array|null $access_grant_keys = null, + /** + * The actual PIN code for code access methods (only present when mode is 'code'). + */ + public string|null $code = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). + */ + public bool|null $is_backup_code = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An access method was revoked. + */ + final class AccessMethodRevoked extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessMethodRevoked|null + { + if (!$json) { + return null; + } + return new self( + access_grant_ids: $json->access_grant_ids ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * IDs of the access grants associated with this access method. + * + * @var list|null + */ + public array|null $access_grant_ids, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Keys of the access grants associated with this access method (if present). + * + * @var list|null + */ + public array|null $access_grant_keys = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An access method representing a physical card requires encoding. + */ + final class AccessMethodCardEncodingRequired extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessMethodCardEncodingRequired|null { + if (!$json) { + return null; + } + return new self( + access_grant_ids: $json->access_grant_ids ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * IDs of the access grants associated with this access method. + * + * @var list|null + */ + public array|null $access_grant_ids, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Keys of the access grants associated with this access method (if present). + * + * @var list|null + */ + public array|null $access_grant_keys = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An access method was deleted. + */ + final class AccessMethodDeleted extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessMethodDeleted|null + { + if (!$json) { + return null; + } + return new self( + access_grant_ids: $json->access_grant_ids ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * IDs of the access grants associated with this access method. + * + * @var list|null + */ + public array|null $access_grant_ids, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Keys of the access grants associated with this access method (if present). + * + * @var list|null + */ + public array|null $access_grant_keys = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An access method was reissued. + */ + final class AccessMethodReissued extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessMethodReissued|null + { + if (!$json) { + return null; + } + return new self( + access_grant_ids: $json->access_grant_ids ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + code: $json->code ?? null, + event_description: $json->event_description ?? null, + is_backup_code: $json->is_backup_code ?? null, + ); + } + + public function __construct( + /** + * IDs of the access grants associated with this access method. + * + * @var list|null + */ + public array|null $access_grant_ids, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Keys of the access grants associated with this access method (if present). + * + * @var list|null + */ + public array|null $access_grant_keys = null, + /** + * The actual PIN code for code access methods (only present when mode is 'code'). + */ + public string|null $code = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). + */ + public bool|null $is_backup_code = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An access method was created. + */ + final class AccessMethodCreated extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AccessMethodCreated|null + { + if (!$json) { + return null; + } + return new self( + access_grant_ids: $json->access_grant_ids ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * IDs of the access grants associated with this access method. + * + * @var list|null + */ + public array|null $access_grant_ids, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Keys of the access grants associated with this access method (if present). + * + * @var list|null + */ + public array|null $access_grant_keys = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * 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. + */ + final class AccessMethodDelayInIssuing extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessMethodDelayInIssuing|null { + if (!$json) { + return null; + } + return new self( + access_grant_ids: $json->access_grant_ids ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * IDs of the access grants associated with this access method. + * + * @var list|null + */ + public array|null $access_grant_ids, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Keys of the access grants associated with this access method (if present). + * + * @var list|null + */ + public array|null $access_grant_keys = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * 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. + */ + final class AccessMethodFailedToIssue extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AccessMethodFailedToIssue|null { + if (!$json) { + return null; + } + return new self( + access_grant_ids: $json->access_grant_ids ?? null, + access_method_id: $json->access_method_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * IDs of the access grants associated with this access method. + * + * @var list|null + */ + public array|null $access_grant_ids, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Keys of the access grants associated with this access method (if present). + * + * @var list|null + */ + public array|null $access_grant_keys = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system](https://docs.seam.co/low-level-apis/access-systems) was connected. + */ + final class AcsSystemConnected extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsSystemConnected|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system](https://docs.seam.co/low-level-apis/access-systems) was added. + */ + final class AcsSystemAdded extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsSystemAdded|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system](https://docs.seam.co/low-level-apis/access-systems) was disconnected. + */ + final class AcsSystemDisconnected extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AcsSystemDisconnected|null { + if (!$json) { + return null; + } + return new self( + acs_system_errors: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AcsSystemDisconnected\AcsSystemErrors::from_json( + $a, + ), + $json->acs_system_errors ?? [], + ), + acs_system_id: $json->acs_system_id ?? null, + acs_system_warnings: array_map( + fn( + $a, + ) => \Seam\Resources\Event\AcsSystemDisconnected\AcsSystemWarnings::from_json( + $a, + ), + $json->acs_system_warnings ?? [], + ), + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AcsSystemDisconnected\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\AcsSystemDisconnected\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the access control system. + * + * @var list<\Seam\Resources\Event\AcsSystemDisconnected\AcsSystemErrors> + */ + public array $acs_system_errors, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Warnings associated with the access control system. + * + * @var list<\Seam\Resources\Event\AcsSystemDisconnected\AcsSystemWarnings> + */ + public array $acs_system_warnings, + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\AcsSystemDisconnected\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\AcsSystemDisconnected\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was deleted. + */ + final class AcsCredentialDeleted extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsCredentialDeleted|null + { + if (!$json) { + return null; + } + return new self( + acs_credential_id: $json->acs_credential_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected credential. + */ + public string|null $acs_credential_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was issued. + */ + final class AcsCredentialIssued extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsCredentialIssued|null + { + if (!$json) { + return null; + } + return new self( + acs_credential_id: $json->acs_credential_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected credential. + */ + public string|null $acs_credential_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was reissued. + */ + final class AcsCredentialReissued extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AcsCredentialReissued|null { + if (!$json) { + return null; + } + return new self( + acs_credential_id: $json->acs_credential_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected credential. + */ + public string|null $acs_credential_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was invalidated. That is, the credential cannot be used anymore. + */ + final class AcsCredentialInvalidated extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AcsCredentialInvalidated|null { + if (!$json) { + return null; + } + return new self( + acs_credential_id: $json->acs_credential_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected credential. + */ + public string|null $acs_credential_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was created. + */ + final class AcsUserCreated extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsUserCreated|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * ID of the affected access system user. + */ + public string|null $acs_user_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was deleted. + */ + final class AcsUserDeleted extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsUserDeleted|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * ID of the affected access system user. + */ + public string|null $acs_user_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners) was added. + */ + final class AcsEncoderAdded extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsEncoderAdded|null + { + if (!$json) { + return null; + } + return new self( + acs_encoder_id: $json->acs_encoder_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected encoder. + */ + public string|null $acs_encoder_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners) was removed. + */ + final class AcsEncoderRemoved extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsEncoderRemoved|null + { + if (!$json) { + return null; + } + return new self( + acs_encoder_id: $json->acs_encoder_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected encoder. + */ + public string|null $acs_encoder_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An ACS access group was deleted. + */ + final class AcsAccessGroupDeleted extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): AcsAccessGroupDeleted|null { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected access group. + */ + public string|null $acs_access_group_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) was added. + */ + final class AcsEntranceAdded extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsEntranceAdded|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected entrance. + */ + public string|null $acs_entrance_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [access system entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) was removed. + */ + final class AcsEntranceRemoved extends \Seam\Resources\Event + { + public static function from_json(mixed $json): AcsEntranceRemoved|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected entrance. + */ + public string|null $acs_entrance_id, + /** + * ID of the access system. + */ + public string|null $acs_system_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account. + */ + public string|null $connected_account_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A client session was deleted. + */ + final class ClientSessionDeleted extends \Seam\Resources\Event + { + public static function from_json(mixed $json): ClientSessionDeleted|null + { + if (!$json) { + return null; + } + return new self( + client_session_id: $json->client_session_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected client session. + */ + public string|null $client_session_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A connected account was connected for the first time or was reconnected after being disconnected. + */ + final class ConnectedAccountConnected extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectedAccountConnected|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connect_webview_id: $json->connect_webview_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected connected account. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the Connect Webview associated with the event. + */ + public string|null $connect_webview_id = null, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with this connected account, if any. + */ + public string|null $customer_key = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A connected account was created. + */ + final class ConnectedAccountCreated extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectedAccountCreated|null { + if (!$json) { + return null; + } + return new self( + connect_webview_id: $json->connect_webview_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the Connect Webview associated with the event. + */ + public string|null $connect_webview_id, + /** + * ID of the affected connected account. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A connected account had a successful login using a Connect Webview. + */ + final class ConnectedAccountSuccessfulLogin extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectedAccountSuccessfulLogin|null { + if (!$json) { + return null; + } + return new self( + connect_webview_id: $json->connect_webview_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the Connect Webview associated with the event. + */ + public string|null $connect_webview_id, + /** + * ID of the affected connected account. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A connected account was disconnected. + */ + final class ConnectedAccountDisconnected extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectedAccountDisconnected|null { + if (!$json) { + return null; + } + return new self( + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\ConnectedAccountDisconnected\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\ConnectedAccountDisconnected\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\ConnectedAccountDisconnected\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the affected connected account. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\ConnectedAccountDisconnected\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A connected account completed the first sync with Seam, and the corresponding devices or systems are now available. + */ + final class ConnectedAccountCompletedFirstSync extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectedAccountCompletedFirstSync|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected connected account. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A connected account was deleted. + */ + final class ConnectedAccountDeleted extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectedAccountDeleted|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected connected account. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with this connected account, if any. + */ + public string|null $customer_key = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A connected account completed the first sync after reconnection with Seam, and the corresponding devices or systems are now available. + */ + final class ConnectedAccountCompletedFirstSyncAfterReconnection extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectedAccountCompletedFirstSyncAfterReconnection|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected connected account. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * 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. + */ + final class ConnectedAccountReauthorizationRequested extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectedAccountReauthorizationRequested|null { + if (!$json) { + return null; + } + return new self( + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\ConnectedAccountReauthorizationRequested\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\ConnectedAccountReauthorizationRequested\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\ConnectedAccountReauthorizationRequested\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the affected connected account. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\ConnectedAccountReauthorizationRequested\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A lock door action attempt succeeded. + */ + final class ActionAttemptLockDoorSucceeded extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ActionAttemptLockDoorSucceeded|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + status: $json->status ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected action attempt. + */ + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account associated with the action attempt, if applicable. + */ + public string|null $connected_account_id = null, + /** + * ID of the device associated with the action attempt, if applicable. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A lock door action attempt failed. + */ + final class ActionAttemptLockDoorFailed extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ActionAttemptLockDoorFailed|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + status: $json->status ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected action attempt. + */ + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account associated with the action attempt, if applicable. + */ + public string|null $connected_account_id = null, + /** + * ID of the device associated with the action attempt, if applicable. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An unlock door action attempt succeeded. + */ + final class ActionAttemptUnlockDoorSucceeded extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ActionAttemptUnlockDoorSucceeded|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + status: $json->status ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected action attempt. + */ + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account associated with the action attempt, if applicable. + */ + public string|null $connected_account_id = null, + /** + * ID of the device associated with the action attempt, if applicable. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An unlock door action attempt failed. + */ + final class ActionAttemptUnlockDoorFailed extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ActionAttemptUnlockDoorFailed|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + status: $json->status ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected action attempt. + */ + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account associated with the action attempt, if applicable. + */ + public string|null $connected_account_id = null, + /** + * ID of the device associated with the action attempt, if applicable. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A simulate keypad code entry action attempt succeeded. + */ + final class ActionAttemptSimulateKeypadCodeEntrySucceeded extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ActionAttemptSimulateKeypadCodeEntrySucceeded|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + status: $json->status ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected action attempt. + */ + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account associated with the action attempt, if applicable. + */ + public string|null $connected_account_id = null, + /** + * ID of the device associated with the action attempt, if applicable. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A simulate keypad code entry action attempt failed. + */ + final class ActionAttemptSimulateKeypadCodeEntryFailed extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ActionAttemptSimulateKeypadCodeEntryFailed|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + status: $json->status ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected action attempt. + */ + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account associated with the action attempt, if applicable. + */ + public string|null $connected_account_id = null, + /** + * ID of the device associated with the action attempt, if applicable. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A simulate manual lock via keypad action attempt succeeded. + */ + final class ActionAttemptSimulateManualLockViaKeypadSucceeded extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ActionAttemptSimulateManualLockViaKeypadSucceeded|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + status: $json->status ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected action attempt. + */ + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account associated with the action attempt, if applicable. + */ + public string|null $connected_account_id = null, + /** + * ID of the device associated with the action attempt, if applicable. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A simulate manual lock via keypad action attempt failed. + */ + final class ActionAttemptSimulateManualLockViaKeypadFailed extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ActionAttemptSimulateManualLockViaKeypadFailed|null { + if (!$json) { + return null; + } + return new self( + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + status: $json->status ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected action attempt. + */ + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the connected account associated with the action attempt, if applicable. + */ + public string|null $connected_account_id = null, + /** + * ID of the device associated with the action attempt, if applicable. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A Connect Webview login succeeded. + */ + final class ConnectWebviewLoginSucceeded extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectWebviewLoginSucceeded|null { + if (!$json) { + return null; + } + return new self( + connect_webview_id: $json->connect_webview_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Connect Webview. + */ + public string|null $connect_webview_id, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account; present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with this connect webview, if any. + */ + public string|null $customer_key = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A Connect Webview login failed. + */ + final class ConnectWebviewLoginFailed extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ConnectWebviewLoginFailed|null { + if (!$json) { + return null; + } + return new self( + connect_webview_id: $json->connect_webview_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the affected Connect Webview. + */ + public string|null $connect_webview_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * 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. + */ + final class DeviceConnected extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceConnected|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A device was added to Seam or was re-added to Seam after having been removed. + */ + final class DeviceAdded extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceAdded|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A managed device was successfully converted to an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). + */ + final class DeviceConvertedToUnmanaged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceConvertedToUnmanaged|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices) was successfully converted to a managed device. + */ + final class DeviceUnmanagedConvertedToManaged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceUnmanagedConvertedToManaged|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * The status of an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices) changed from offline to online. That is, the `device.properties.online` property changed from `false` to `true`. + */ + final class DeviceUnmanagedConnected extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceUnmanagedConnected|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * The status of a device changed from online to offline. That is, the `device.properties.online` property changed from `true` to `false`. + */ + final class DeviceDisconnected extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceDisconnected|null + { + if (!$json) { + return null; + } + return new self( + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceDisconnected\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceDisconnected\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceDisconnected\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceDisconnected\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + error_code: $json->error_code ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceDisconnected\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceDisconnected\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceDisconnected\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceDisconnected\DeviceWarnings> + */ + public array $device_warnings, + /** + * Error code associated with the disconnection event, if any. + * + * @var value-of<\Seam\Resources\Event\DeviceDisconnected\ErrorCode>|string|null + */ + public string|null $error_code, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * The status of an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices) changed from online to offline. That is, the `device.properties.online` property changed from `true` to `false`. + */ + final class DeviceUnmanagedDisconnected extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceUnmanagedDisconnected|null { + if (!$json) { + return null; + } + return new self( + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + error_code: $json->error_code ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceUnmanagedDisconnected\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceUnmanagedDisconnected\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceUnmanagedDisconnected\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceUnmanagedDisconnected\DeviceWarnings> + */ + public array $device_warnings, + /** + * Error code associated with the disconnection event, if any. + * + * @var value-of<\Seam\Resources\Event\DeviceUnmanagedDisconnected\ErrorCode>|string|null + */ + public string|null $error_code, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A device detected that it was tampered with, for example, opened or moved. + */ + final class DeviceTampered extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceTampered|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A device battery level dropped below the low threshold. + */ + final class DeviceLowBattery extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceLowBattery|null + { + if (!$json) { + return null; + } + return new self( + battery_level: $json->battery_level ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. + */ + public float|null $battery_level, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A device battery status changed since the last `battery_status_changed` event. + */ + final class DeviceBatteryStatusChanged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceBatteryStatusChanged|null { + if (!$json) { + return null; + } + return new self( + battery_level: $json->battery_level ?? null, + battery_status: $json->battery_status ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. + */ + public float|null $battery_level, + /** + * Battery status of the affected device, calculated from the numeric `battery_level` value. + * + * @var value-of<\Seam\Resources\Event\DeviceBatteryStatusChanged\BatteryStatus>|string|null + */ + public string|null $battery_status, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A device was removed externally from the connected account. + */ + final class DeviceRemoved extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceRemoved|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A device was deleted. + */ + final class DeviceDeleted extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceDeleted|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + device_name: $json->device_name ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + public string|null $device_name = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * Seam detected that a device is using a third-party integration that will interfere with Seam device management. + */ + final class DeviceThirdPartyIntegrationDetected extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceThirdPartyIntegrationDetected|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * Seam detected that a device is no longer using a third-party integration that was interfering with Seam device management. + */ + final class DeviceThirdPartyIntegrationNoLongerDetected extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceThirdPartyIntegrationNoLongerDetected|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [Salto device](https://docs.seam.co/device-and-system-integration-guides/salto-locks) activated privacy mode. + */ + final class DeviceSaltoPrivacyModeActivated extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceSaltoPrivacyModeActivated|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [Salto device](https://docs.seam.co/device-and-system-integration-guides/salto-locks) deactivated privacy mode. + */ + final class DeviceSaltoPrivacyModeDeactivated extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceSaltoPrivacyModeDeactivated|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * Seam detected a flaky device connection. + */ + final class DeviceConnectionBecameFlaky extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceConnectionBecameFlaky|null { + if (!$json) { + return null; + } + return new self( + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceConnectionBecameFlaky\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceConnectionBecameFlaky\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceConnectionBecameFlaky\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceConnectionBecameFlaky\DeviceWarnings> + */ + public array $device_warnings, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * Seam detected that a previously-flaky device connection stabilized. + */ + final class DeviceConnectionStabilized extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceConnectionStabilized|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A third-party subscription is required to use all device features. + */ + final class DeviceErrorSubscriptionRequired extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceErrorSubscriptionRequired|null { + if (!$json) { + return null; + } + return new self( + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceErrorSubscriptionRequired\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceErrorSubscriptionRequired\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceErrorSubscriptionRequired\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceErrorSubscriptionRequired\DeviceWarnings> + */ + public array $device_warnings, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A third-party subscription is active or no longer required to use all device features. + */ + final class DeviceErrorSubscriptionRequiredResolved extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceErrorSubscriptionRequiredResolved|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An accessory keypad was connected to a device. + */ + final class DeviceAccessoryKeypadConnected extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceAccessoryKeypadConnected|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * An accessory keypad was disconnected from a device. + */ + final class DeviceAccessoryKeypadDisconnected extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): DeviceAccessoryKeypadDisconnected|null { + if (!$json) { + return null; + } + return new self( + connected_account_errors: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\ConnectedAccountErrors::from_json( + $c, + ), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_warnings: array_map( + fn( + $c, + ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\ConnectedAccountWarnings::from_json( + $c, + ), + $json->connected_account_warnings ?? [], + ), + created_at: $json->created_at ?? null, + device_errors: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\DeviceErrors::from_json( + $d, + ), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_warnings: array_map( + fn( + $d, + ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\DeviceWarnings::from_json( + $d, + ), + $json->device_warnings ?? [], + ), + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Errors associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\ConnectedAccountErrors> + */ + public array $connected_account_errors, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Warnings associated with the connected account. + * + * @var list<\Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\ConnectedAccountWarnings> + */ + public array $connected_account_warnings, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * Errors associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\DeviceErrors> + */ + public array $device_errors, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * Warnings associated with the device. + * + * @var list<\Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\DeviceWarnings> + */ + public array $device_warnings, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * Extended periods of noise or noise exceeding a [threshold](https://docs.seam.co/capability-guides/noise-sensors#what-is-a-threshold) were detected. + */ + final class NoiseSensorNoiseThresholdTriggered extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): NoiseSensorNoiseThresholdTriggered|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + minut_metadata: $json->minut_metadata ?? null, + noise_level_decibels: $json->noise_level_decibels ?? null, + noise_level_nrs: $json->noise_level_nrs ?? null, + noise_threshold_id: $json->noise_threshold_id ?? null, + noise_threshold_name: $json->noise_threshold_name ?? null, + noiseaware_metadata: $json->noiseaware_metadata ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * Metadata from Minut. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $minut_metadata = null, + /** + * Detected noise level in decibels. + */ + public float|null $noise_level_decibels = null, + /** + * Detected noise level in Noiseaware Noise Risk Score (NRS). + */ + public float|null $noise_level_nrs = null, + /** + * ID of the noise threshold that was triggered. + */ + public string|null $noise_threshold_id = null, + /** + * Name of the noise threshold that was triggered. + */ + public string|null $noise_threshold_name = null, + /** + * Metadata from Noiseaware. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $noiseaware_metadata = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [lock](https://docs.seam.co/low-level-apis/smart-locks) was locked. + */ + final class LockLocked extends \Seam\Resources\Event + { + public static function from_json(mixed $json): LockLocked|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + method: $json->method ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_code_id: $json->access_code_id ?? null, + access_code_is_managed: $json->access_code_is_managed ?? null, + action_attempt_id: $json->action_attempt_id ?? null, + code: $json->code ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + is_via_bluetooth: $json->is_via_bluetooth ?? null, + is_via_nfc: $json->is_via_nfc ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * 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. + * + * @var value-of<\Seam\Resources\Event\LockLocked\Method>|string|null + */ + public string|null $method, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the access code that was used to lock the device. + */ + public string|null $access_code_id = null, + /** + * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + */ + public bool|null $access_code_is_managed = null, + /** + * 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). + */ + public string|null $action_attempt_id = null, + /** + * 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. + */ + public string|null $code = null, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * 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. + */ + public bool|null $is_via_bluetooth = null, + /** + * 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. + */ + public bool|null $is_via_nfc = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [lock](https://docs.seam.co/low-level-apis/smart-locks) was unlocked. + */ + final class LockUnlocked extends \Seam\Resources\Event + { + public static function from_json(mixed $json): LockUnlocked|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + method: $json->method ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_code_id: $json->access_code_id ?? null, + access_code_is_managed: $json->access_code_is_managed ?? null, + action_attempt_id: $json->action_attempt_id ?? null, + code: $json->code ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + is_via_bluetooth: $json->is_via_bluetooth ?? null, + is_via_nfc: $json->is_via_nfc ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Method by which the lock was unlocked. `keycode`: an [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes) 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. + * + * @var value-of<\Seam\Resources\Event\LockUnlocked\Method>|string|null + */ + public string|null $method, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the access code that was used to unlock the affected device. + */ + public string|null $access_code_id = null, + /** + * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + */ + public bool|null $access_code_is_managed = null, + /** + * 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). + */ + public string|null $action_attempt_id = null, + /** + * 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. + */ + public string|null $code = null, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * ID of the affected device. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * 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. + */ + public bool|null $is_via_bluetooth = null, + /** + * 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. + */ + public bool|null $is_via_nfc = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * The [lock](https://docs.seam.co/low-level-apis/smart-locks) denied access to a user after one or more consecutive invalid attempts to unlock the device. + */ + final class LockAccessDenied extends \Seam\Resources\Event + { + public static function from_json(mixed $json): LockAccessDenied|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + access_code_id: $json->access_code_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + device_id: $json->device_id ?? null, + event_description: $json->event_description ?? null, + reason: isset($json->reason) + ? \Seam\Resources\Event\LockAccessDenied\Reason::from_json( + $json->reason, + ) + : null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * ID of the access code that was used in the unlock attempts. + */ + public string|null $access_code_id = null, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * ID of the affected device. + */ + public string|null $device_id = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + */ + public \Seam\Resources\Event\LockAccessDenied\Reason|null $reason = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A thermostat [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) was activated. + */ + final class ThermostatClimatePresetActivated extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ThermostatClimatePresetActivated|null { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + is_fallback_climate_preset: $json->is_fallback_climate_preset ?? + null, + occurred_at: $json->occurred_at ?? null, + thermostat_schedule_id: $json->thermostat_schedule_id ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Key of the climate preset that was activated. + */ + public string|null $climate_preset_key, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. + */ + public bool|null $is_fallback_climate_preset, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the thermostat schedule that prompted the affected climate preset to be activated. + */ + public string|null $thermostat_schedule_id, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [thermostat](https://docs.seam.co/capability-guides/thermostats) was adjusted manually. + */ + final class ThermostatManuallyAdjusted extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ThermostatManuallyAdjusted|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + method: $json->method ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * 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. + * + * @var value-of<\Seam\Resources\Event\ThermostatManuallyAdjusted\Method>|string|null + */ + public string|null $method, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius = null, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + * + * @var value-of<\Seam\Resources\Event\ThermostatManuallyAdjusted\FanModeSetting>|string|null + */ + public string|null $fan_mode_setting = null, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius = null, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit = null, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + * + * @var value-of<\Seam\Resources\Event\ThermostatManuallyAdjusted\HvacModeSetting>|string|null + */ + public string|null $hvac_mode_setting = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [thermostat's](https://docs.seam.co/capability-guides/thermostats) temperature reading exceeded the set [threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds). + */ + final class ThermostatTemperatureThresholdExceeded extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ThermostatTemperatureThresholdExceeded|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + lower_limit_celsius: $json->lower_limit_celsius ?? null, + lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, + occurred_at: $json->occurred_at ?? null, + temperature_celsius: $json->temperature_celsius ?? null, + temperature_fahrenheit: $json->temperature_fahrenheit ?? null, + upper_limit_celsius: $json->upper_limit_celsius ?? null, + upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Lower temperature limit, in °C, defined by the set threshold. + */ + public float|null $lower_limit_celsius, + /** + * Lower temperature limit, in °F, defined by the set threshold. + */ + public float|null $lower_limit_fahrenheit, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Temperature, in °C, reported by the affected thermostat. + */ + public float|null $temperature_celsius, + /** + * Temperature, in °F, reported by the affected thermostat. + */ + public float|null $temperature_fahrenheit, + /** + * Upper temperature limit, in °C, defined by the set threshold. + */ + public float|null $upper_limit_celsius, + /** + * Upper temperature limit, in °F, defined by the set threshold. + */ + public float|null $upper_limit_fahrenheit, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [thermostat's](https://docs.seam.co/capability-guides/thermostats) temperature reading no longer exceeds the set [threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds). + */ + final class ThermostatTemperatureThresholdNoLongerExceeded extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ThermostatTemperatureThresholdNoLongerExceeded|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + lower_limit_celsius: $json->lower_limit_celsius ?? null, + lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, + occurred_at: $json->occurred_at ?? null, + temperature_celsius: $json->temperature_celsius ?? null, + temperature_fahrenheit: $json->temperature_fahrenheit ?? null, + upper_limit_celsius: $json->upper_limit_celsius ?? null, + upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Lower temperature limit, in °C, defined by the set threshold. + */ + public float|null $lower_limit_celsius, + /** + * Lower temperature limit, in °F, defined by the set threshold. + */ + public float|null $lower_limit_fahrenheit, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Temperature, in °C, reported by the affected thermostat. + */ + public float|null $temperature_celsius, + /** + * Temperature, in °F, reported by the affected thermostat. + */ + public float|null $temperature_fahrenheit, + /** + * Upper temperature limit, in °C, defined by the set threshold. + */ + public float|null $upper_limit_celsius, + /** + * Upper temperature limit, in °F, defined by the set threshold. + */ + public float|null $upper_limit_fahrenheit, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [thermostat's](https://docs.seam.co/capability-guides/thermostats) temperature reading is within 1 °C of the configured cooling or heating [set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + final class ThermostatTemperatureReachedSetPoint extends + \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ThermostatTemperatureReachedSetPoint|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + temperature_celsius: $json->temperature_celsius ?? null, + temperature_fahrenheit: $json->temperature_fahrenheit ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + desired_temperature_celsius: $json->desired_temperature_celsius ?? + null, + desired_temperature_fahrenheit: $json->desired_temperature_fahrenheit ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Temperature, in °C, reported by the affected thermostat. + */ + public float|null $temperature_celsius, + /** + * Temperature, in °F, reported by the affected thermostat. + */ + public float|null $temperature_fahrenheit, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. + */ + public float|null $desired_temperature_celsius = null, + /** + * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. + */ + public float|null $desired_temperature_fahrenheit = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A [thermostat's](https://docs.seam.co/capability-guides/thermostats) reported temperature changed by at least 1 °C. + */ + final class ThermostatTemperatureChanged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): ThermostatTemperatureChanged|null { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + temperature_celsius: $json->temperature_celsius ?? null, + temperature_fahrenheit: $json->temperature_fahrenheit ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * Temperature, in °C, reported by the affected thermostat. + */ + public float|null $temperature_celsius, + /** + * Temperature, in °F, reported by the affected thermostat. + */ + public float|null $temperature_fahrenheit, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * The name of a device was changed. + */ + final class DeviceNameChanged extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceNameChanged|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * The new name of the affected device. + */ + public string|null $device_name, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A camera was activated, for example, by motion detection. + */ + final class CameraActivated extends \Seam\Resources\Event + { + public static function from_json(mixed $json): CameraActivated|null + { + if (!$json) { + return null; + } + return new self( + activation_reason: $json->activation_reason ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + image_url: $json->image_url ?? null, + motion_sub_type: $json->motion_sub_type ?? null, + video_url: $json->video_url ?? null, + ); + } + + public function __construct( + /** + * The reason the camera was activated. + * + * @var value-of<\Seam\Resources\Event\CameraActivated\ActivationReason>|string|null + */ + public string|null $activation_reason, + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * URL to a thumbnail image captured at the time of activation. + */ + public string|null $image_url = null, + /** + * Sub-type of motion detected, if available. + * + * @var value-of<\Seam\Resources\Event\CameraActivated\MotionSubType>|string|null + */ + public string|null $motion_sub_type = null, + /** + * URL to a short video clip captured at the time of activation. + */ + public string|null $video_url = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A doorbell button was pressed on a device. + */ + final class DeviceDoorbellRang extends \Seam\Resources\Event + { + public static function from_json(mixed $json): DeviceDoorbellRang|null + { + if (!$json) { + return null; + } + return new self( + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + customer_key: $json->customer_key ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + image_url: $json->image_url ?? null, + video_url: $json->video_url ?? null, + ); + } + + public function __construct( + /** + * ID of the connected account associated with the event. + */ + public string|null $connected_account_id, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the connected account, present when connected_account_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $connected_account_custom_metadata = null, + /** + * The customer key associated with the device, if any. + */ + public string|null $customer_key = null, + /** + * Custom metadata of the device, present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + /** + * URL to a thumbnail image captured at the time the doorbell was pressed. + */ + public string|null $image_url = null, + /** + * URL to a short video clip captured at the time the doorbell was pressed. + */ + public string|null $video_url = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A phone device was deactivated. + */ + final class PhoneDeactivated extends \Seam\Resources\Event + { + public static function from_json(mixed $json): PhoneDeactivated|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + workspace_id: $json->workspace_id ?? null, + device_custom_metadata: $json->device_custom_metadata ?? null, + event_description: $json->event_description ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * ID of the affected phone device. + */ + public string|null $device_id, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * Custom metadata of the device; present when device_id is provided. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $device_custom_metadata = null, + /** + * 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. + */ + string|null $event_description = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A device was added or removed from a space. + */ + final class SpaceDeviceMembershipChanged extends \Seam\Resources\Event + { + public static function from_json( + mixed $json, + ): SpaceDeviceMembershipChanged|null { + if (!$json) { + return null; + } + return new self( + acs_entrance_ids: $json->acs_entrance_ids ?? null, + created_at: $json->created_at ?? null, + device_ids: $json->device_ids ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + space_id: $json->space_id ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + space_key: $json->space_key ?? null, + ); + } + + public function __construct( + /** + * IDs of all ACS entrances currently attached to the space. + * + * @var list|null + */ + public array|null $acs_entrance_ids, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * IDs of all devices currently attached to the space. + * + * @var list|null + */ + public array|null $device_ids, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the affected space. + */ + public string|null $space_id, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + /** + * Unique key for the space within the workspace. + */ + public string|null $space_key = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A space was created. + */ + final class SpaceCreated extends \Seam\Resources\Event + { + public static function from_json(mixed $json): SpaceCreated|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_ids: $json->acs_entrance_ids ?? null, + created_at: $json->created_at ?? null, + device_ids: $json->device_ids ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + space_id: $json->space_id ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + space_key: $json->space_key ?? null, + ); + } + + public function __construct( + /** + * IDs of all ACS entrances attached to the space when it was created. + * + * @var list|null + */ + public array|null $acs_entrance_ids, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * IDs of all devices attached to the space when it was created. + * + * @var list|null + */ + public array|null $device_ids, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the affected space. + */ + public string|null $space_id, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + /** + * Unique key for the space within the workspace. + */ + public string|null $space_key = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + /** + * A space was deleted. + */ + final class SpaceDeleted extends \Seam\Resources\Event + { + public static function from_json(mixed $json): SpaceDeleted|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_ids: $json->acs_entrance_ids ?? null, + created_at: $json->created_at ?? null, + device_ids: $json->device_ids ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + occurred_at: $json->occurred_at ?? null, + space_id: $json->space_id ?? null, + workspace_id: $json->workspace_id ?? null, + event_description: $json->event_description ?? null, + space_key: $json->space_key ?? null, + ); + } + + public function __construct( + /** + * IDs of all ACS entrances currently attached to the space when it was deleted. + * + * @var list|null + */ + public array|null $acs_entrance_ids, + /** + * Date and time at which the event was created. + */ + string|null $created_at, + /** + * IDs of all devices attached to the space when it was deleted. + * + * @var list|null + */ + public array|null $device_ids, + /** + * ID of the event. + */ + string|null $event_id, + /** + * @var value-of<\Seam\Resources\Event\EventType>|string|null + */ + string|null $event_type, + /** + * Date and time at which the event occurred. + */ + string|null $occurred_at, + /** + * ID of the affected space. + */ + public string|null $space_id, + /** + * ID of the workspace associated with the event. + */ + string|null $workspace_id, + /** + * 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. + */ + string|null $event_description = null, + /** + * Unique key for the space within the workspace. + */ + public string|null $space_key = null, + ) { + parent::__construct( + created_at: $created_at, + event_description: $event_description, + event_id: $event_id, + event_type: $event_type, + occurred_at: $occurred_at, + workspace_id: $workspace_id, + ); + } + } + + enum EventType: string + { + case ACCESS_CODE_CREATED = "access_code.created"; + case ACCESS_CODE_CHANGED = "access_code.changed"; + case ACCESS_CODE_NAME_CHANGED = "access_code.name_changed"; + case ACCESS_CODE_CODE_CHANGED = "access_code.code_changed"; + case ACCESS_CODE_TIME_FRAME_CHANGED = "access_code.time_frame_changed"; + case ACCESS_CODE_MUTATIONS_REQUESTED = "access_code.mutations_requested"; + case ACCESS_CODE_SCHEDULED_ON_DEVICE = "access_code.scheduled_on_device"; + case ACCESS_CODE_SET_ON_DEVICE = "access_code.set_on_device"; + case ACCESS_CODE_REMOVED_FROM_DEVICE = "access_code.removed_from_device"; + case ACCESS_CODE_DELAY_IN_SETTING_ON_DEVICE = "access_code.delay_in_setting_on_device"; + case ACCESS_CODE_FAILED_TO_SET_ON_DEVICE = "access_code.failed_to_set_on_device"; + case ACCESS_CODE_DELETED = "access_code.deleted"; + case ACCESS_CODE_DELAY_IN_REMOVING_FROM_DEVICE = "access_code.delay_in_removing_from_device"; + case ACCESS_CODE_FAILED_TO_REMOVE_FROM_DEVICE = "access_code.failed_to_remove_from_device"; + case ACCESS_CODE_MODIFIED_EXTERNAL_TO_SEAM = "access_code.modified_external_to_seam"; + case ACCESS_CODE_DELETED_EXTERNAL_TO_SEAM = "access_code.deleted_external_to_seam"; + case ACCESS_CODE_BACKUP_ACCESS_CODE_PULLED = "access_code.backup_access_code_pulled"; + case ACCESS_CODE_UNMANAGED_CONVERTED_TO_MANAGED = "access_code.unmanaged.converted_to_managed"; + case ACCESS_CODE_UNMANAGED_FAILED_TO_CONVERT_TO_MANAGED = "access_code.unmanaged.failed_to_convert_to_managed"; + case ACCESS_CODE_UNMANAGED_CREATED = "access_code.unmanaged.created"; + case ACCESS_CODE_UNMANAGED_REMOVED = "access_code.unmanaged.removed"; + case ACCESS_GRANT_CREATED = "access_grant.created"; + case ACCESS_GRANT_DELETED = "access_grant.deleted"; + case ACCESS_GRANT_ACCESS_GRANTED_TO_ALL_DOORS = "access_grant.access_granted_to_all_doors"; + case ACCESS_GRANT_ACCESS_GRANTED_TO_DOOR = "access_grant.access_granted_to_door"; + case ACCESS_GRANT_ACCESS_TO_DOOR_LOST = "access_grant.access_to_door_lost"; + case ACCESS_GRANT_ACCESS_TIMES_CHANGED = "access_grant.access_times_changed"; + case ACCESS_GRANT_COULD_NOT_CREATE_REQUESTED_ACCESS_METHODS = "access_grant.could_not_create_requested_access_methods"; + case ACCESS_METHOD_ISSUED = "access_method.issued"; + case ACCESS_METHOD_REVOKED = "access_method.revoked"; + case ACCESS_METHOD_CARD_ENCODING_REQUIRED = "access_method.card_encoding_required"; + case ACCESS_METHOD_DELETED = "access_method.deleted"; + case ACCESS_METHOD_REISSUED = "access_method.reissued"; + case ACCESS_METHOD_CREATED = "access_method.created"; + case ACCESS_METHOD_DELAY_IN_ISSUING = "access_method.delay_in_issuing"; + case ACCESS_METHOD_FAILED_TO_ISSUE = "access_method.failed_to_issue"; + case ACS_SYSTEM_CONNECTED = "acs_system.connected"; + case ACS_SYSTEM_ADDED = "acs_system.added"; + case ACS_SYSTEM_DISCONNECTED = "acs_system.disconnected"; + case ACS_CREDENTIAL_DELETED = "acs_credential.deleted"; + case ACS_CREDENTIAL_ISSUED = "acs_credential.issued"; + case ACS_CREDENTIAL_REISSUED = "acs_credential.reissued"; + case ACS_CREDENTIAL_INVALIDATED = "acs_credential.invalidated"; + case ACS_USER_CREATED = "acs_user.created"; + case ACS_USER_DELETED = "acs_user.deleted"; + case ACS_ENCODER_ADDED = "acs_encoder.added"; + case ACS_ENCODER_REMOVED = "acs_encoder.removed"; + case ACS_ACCESS_GROUP_DELETED = "acs_access_group.deleted"; + case ACS_ENTRANCE_ADDED = "acs_entrance.added"; + case ACS_ENTRANCE_REMOVED = "acs_entrance.removed"; + case CLIENT_SESSION_DELETED = "client_session.deleted"; + case CONNECTED_ACCOUNT_CONNECTED = "connected_account.connected"; + case CONNECTED_ACCOUNT_CREATED = "connected_account.created"; + case CONNECTED_ACCOUNT_SUCCESSFUL_LOGIN = "connected_account.successful_login"; + case CONNECTED_ACCOUNT_DISCONNECTED = "connected_account.disconnected"; + case CONNECTED_ACCOUNT_COMPLETED_FIRST_SYNC = "connected_account.completed_first_sync"; + case CONNECTED_ACCOUNT_DELETED = "connected_account.deleted"; + case CONNECTED_ACCOUNT_COMPLETED_FIRST_SYNC_AFTER_RECONNECTION = "connected_account.completed_first_sync_after_reconnection"; + case CONNECTED_ACCOUNT_REAUTHORIZATION_REQUESTED = "connected_account.reauthorization_requested"; + case ACTION_ATTEMPT_LOCK_DOOR_SUCCEEDED = "action_attempt.lock_door.succeeded"; + case ACTION_ATTEMPT_LOCK_DOOR_FAILED = "action_attempt.lock_door.failed"; + case ACTION_ATTEMPT_UNLOCK_DOOR_SUCCEEDED = "action_attempt.unlock_door.succeeded"; + case ACTION_ATTEMPT_UNLOCK_DOOR_FAILED = "action_attempt.unlock_door.failed"; + case ACTION_ATTEMPT_SIMULATE_KEYPAD_CODE_ENTRY_SUCCEEDED = "action_attempt.simulate_keypad_code_entry.succeeded"; + case ACTION_ATTEMPT_SIMULATE_KEYPAD_CODE_ENTRY_FAILED = "action_attempt.simulate_keypad_code_entry.failed"; + case ACTION_ATTEMPT_SIMULATE_MANUAL_LOCK_VIA_KEYPAD_SUCCEEDED = "action_attempt.simulate_manual_lock_via_keypad.succeeded"; + case ACTION_ATTEMPT_SIMULATE_MANUAL_LOCK_VIA_KEYPAD_FAILED = "action_attempt.simulate_manual_lock_via_keypad.failed"; + case CONNECT_WEBVIEW_LOGIN_SUCCEEDED = "connect_webview.login_succeeded"; + case CONNECT_WEBVIEW_LOGIN_FAILED = "connect_webview.login_failed"; + case DEVICE_CONNECTED = "device.connected"; + case DEVICE_ADDED = "device.added"; + case DEVICE_CONVERTED_TO_UNMANAGED = "device.converted_to_unmanaged"; + case DEVICE_UNMANAGED_CONVERTED_TO_MANAGED = "device.unmanaged.converted_to_managed"; + case DEVICE_UNMANAGED_CONNECTED = "device.unmanaged.connected"; + case DEVICE_DISCONNECTED = "device.disconnected"; + case DEVICE_UNMANAGED_DISCONNECTED = "device.unmanaged.disconnected"; + case DEVICE_TAMPERED = "device.tampered"; + case DEVICE_LOW_BATTERY = "device.low_battery"; + case DEVICE_BATTERY_STATUS_CHANGED = "device.battery_status_changed"; + case DEVICE_REMOVED = "device.removed"; + case DEVICE_DELETED = "device.deleted"; + case DEVICE_THIRD_PARTY_INTEGRATION_DETECTED = "device.third_party_integration_detected"; + case DEVICE_THIRD_PARTY_INTEGRATION_NO_LONGER_DETECTED = "device.third_party_integration_no_longer_detected"; + case DEVICE_SALTO_PRIVACY_MODE_ACTIVATED = "device.salto.privacy_mode_activated"; + case DEVICE_SALTO_PRIVACY_MODE_DEACTIVATED = "device.salto.privacy_mode_deactivated"; + case DEVICE_CONNECTION_BECAME_FLAKY = "device.connection_became_flaky"; + case DEVICE_CONNECTION_STABILIZED = "device.connection_stabilized"; + case DEVICE_ERROR_SUBSCRIPTION_REQUIRED = "device.error.subscription_required"; + case DEVICE_ERROR_SUBSCRIPTION_REQUIRED_RESOLVED = "device.error.subscription_required.resolved"; + case DEVICE_ACCESSORY_KEYPAD_CONNECTED = "device.accessory_keypad_connected"; + case DEVICE_ACCESSORY_KEYPAD_DISCONNECTED = "device.accessory_keypad_disconnected"; + case NOISE_SENSOR_NOISE_THRESHOLD_TRIGGERED = "noise_sensor.noise_threshold_triggered"; + case LOCK_LOCKED = "lock.locked"; + case LOCK_UNLOCKED = "lock.unlocked"; + case LOCK_ACCESS_DENIED = "lock.access_denied"; + case THERMOSTAT_CLIMATE_PRESET_ACTIVATED = "thermostat.climate_preset_activated"; + case THERMOSTAT_MANUALLY_ADJUSTED = "thermostat.manually_adjusted"; + case THERMOSTAT_TEMPERATURE_THRESHOLD_EXCEEDED = "thermostat.temperature_threshold_exceeded"; + case THERMOSTAT_TEMPERATURE_THRESHOLD_NO_LONGER_EXCEEDED = "thermostat.temperature_threshold_no_longer_exceeded"; + case THERMOSTAT_TEMPERATURE_REACHED_SET_POINT = "thermostat.temperature_reached_set_point"; + case THERMOSTAT_TEMPERATURE_CHANGED = "thermostat.temperature_changed"; + case DEVICE_NAME_CHANGED = "device.name_changed"; + case CAMERA_ACTIVATED = "camera.activated"; + case DEVICE_DOORBELL_RANG = "device.doorbell_rang"; + case PHONE_DEACTIVATED = "phone.deactivated"; + case SPACE_DEVICE_MEMBERSHIP_CHANGED = "space.device_membership_changed"; + case SPACE_CREATED = "space.created"; + case SPACE_DELETED = "space.deleted"; + } +} + +namespace Seam\Resources\Event\AccessCodeChanged { + /** + * List of properties that changed on the access code. + */ + class ChangedProperties + { + public static function from_json(mixed $json): ChangedProperties|null + { + if (!$json) { + return null; + } + return new self( + from: $json->from ?? null, + property: $json->property ?? null, + to: $json->to ?? null, + ); + } + + public function __construct( + /** + * Previous value of the property, or null if not set. + */ + public string|null $from, + /** + * Name of the property that changed (e.g. `code`). + */ + public string|null $property, + /** + * New value of the property, or null if cleared. + */ + public string|null $to, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeNameChanged { + /** + * Previous access code name configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(name: $json->name ?? null); + } + + public function __construct( + /** + * Previous name of the access code. + */ + public string|null $name, + ) {} + } + + /** + * New access code name configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(name: $json->name ?? null); + } + + public function __construct( + /** + * New name of the access code. + */ + public string|null $name, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeCodeChanged { + /** + * Previous pin code configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(code: $json->code ?? null); + } + + public function __construct( + /** + * Previous pin code. + */ + public string|null $code, + ) {} + } + + /** + * New pin code configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(code: $json->code ?? null); + } + + public function __construct( + /** + * New pin code. + */ + public string|null $code, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeTimeFrameChanged { + /** + * Previous time frame configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * Previous end time. + */ + public string|null $ends_at, + /** + * Previous start time. + */ + public string|null $starts_at, + ) {} + } + + /** + * New time frame configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * New end time. + */ + public string|null $ends_at, + /** + * New start time. + */ + public string|null $starts_at, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeMutationsRequested { + /** + * Array of mutations requested on the access code, each containing the mutation type and from/to values. + */ + class RequestedMutations + { + public static function from_json(mixed $json): RequestedMutations|null + { + if (!$json) { + return null; + } + return new self( + mutation_code: $json->mutation_code ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); + } + + public function __construct( + /** + * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. + * + * @var value-of<\Seam\Resources\Event\AccessCodeMutationsRequested\RequestedMutations\MutationCode>|string|null + */ + public string|null $mutation_code, + /** + * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $from = null, + /** + * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + * + * @var array|\stdClass|null + */ + public array|\stdClass|null $to = null, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeMutationsRequested\RequestedMutations { + enum MutationCode: string + { + case UPDATING_NAME = "updating_name"; + case UPDATING_CODE = "updating_code"; + case UPDATING_TIME_FRAME = "updating_time_frame"; + case DELETING = "deleting"; + case CREATING = "creating"; + case DEFERRING_CREATION = "deferring_creation"; + } +} + +namespace Seam\Resources\Event\AccessCodeDelayInSettingOnDevice { + /** + * Errors associated with the access code. + */ + class AccessCodeErrors + { + public static function from_json(mixed $json): AccessCodeErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings + { + public static function from_json(mixed $json): AccessCodeWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the device. + */ + class DeviceErrors + { + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the device. + */ + class DeviceWarnings + { + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeFailedToSetOnDevice { + /** + * Errors associated with the access code. + */ + class AccessCodeErrors + { + public static function from_json(mixed $json): AccessCodeErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings + { + public static function from_json(mixed $json): AccessCodeWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the device. + */ + class DeviceErrors + { + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the device. + */ + class DeviceWarnings + { + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice { + /** + * Errors associated with the access code. + */ + class AccessCodeErrors + { + public static function from_json(mixed $json): AccessCodeErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings + { + public static function from_json(mixed $json): AccessCodeWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the device. + */ + class DeviceErrors + { + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the device. + */ + class DeviceWarnings + { + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice { + /** + * Errors associated with the access code. + */ + class AccessCodeErrors + { + public static function from_json(mixed $json): AccessCodeErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings + { + public static function from_json(mixed $json): AccessCodeWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the device. + */ + class DeviceErrors + { + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Warnings associated with the device. + */ + class DeviceWarnings + { + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Errors associated with the access code. - */ - public array $access_code_errors, - public string|null $access_code_id, - /** - * Warnings associated with the access code. - */ - public array $access_code_warnings, - /** - * ID of the affected Access Grant. - */ - public string|null $access_grant_id, - /** - * IDs of the access grants associated with this access method. + * Date and time at which Seam created the warning. */ - public array|null $access_grant_ids, + public string|null $created_at, /** - * ID of the affected access method. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $access_method_id, + public string|null $message, /** - * ID of the affected access group. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $acs_access_group_id, + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged { + /** + * Errors associated with the access code. + */ + class AccessCodeErrors + { + public static function from_json(mixed $json): AccessCodeErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * ID of the affected credential. + * Date and time at which Seam created the error. */ - public string|null $acs_credential_id, + public string|null $created_at, /** - * ID of the affected encoder. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $acs_encoder_id, - public string|null $acs_entrance_id, - public array|null $acs_entrance_ids, + public string|null $error_code, /** - * Errors associated with the access control system. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public array $acs_system_errors, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings + { + public static function from_json(mixed $json): AccessCodeWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * ID of the access system. + * Date and time at which Seam created the warning. */ - public string|null $acs_system_id, + public string|null $created_at, /** - * Warnings associated with the access control system. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public array $acs_system_warnings, + public string|null $message, /** - * ID of the affected access system user. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $acs_user_id, - public string|null $action_attempt_id, + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Type of the action. + * Date and time at which Seam created the error. */ - public string|null $action_type, + public string|null $created_at, /** - * The reason the camera was activated. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $activation_reason, + public string|null $error_code, /** - * ID of the backup access code that was pulled from the pool. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $backup_access_code_id, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. + * Date and time at which Seam created the warning. */ - public float|null $battery_level, + public string|null $created_at, /** - * Battery status of the affected device, calculated from the numeric `battery_level` value. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $battery_status, + public string|null $message, /** - * ID of the affected client session. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $client_session_id, + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the device. + */ + class DeviceErrors + { + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Key of the climate preset that was activated. + * Date and time at which Seam created the error. */ - public string|null $climate_preset_key, - public string|null $code, + public string|null $created_at, /** - * Errors associated with the connected account. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public array $connected_account_errors, - public string|null $connected_account_id, + public string|null $error_code, /** - * Warnings associated with the connected account. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public array $connected_account_warnings, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the device. + */ + class DeviceWarnings + { + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Date and time at which the event was created. + * Date and time at which Seam created the warning. */ public string|null $created_at, /** - * Human-readable description of the change and its source. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $description, + public string|null $message, /** - * Errors associated with the device. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public array $device_errors, - public string|null $device_id, - public array|null $device_ids, + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\AcsSystemDisconnected { + /** + * Errors associated with the access control system. + */ + class AcsSystemErrors + { + public static function from_json(mixed $json): AcsSystemErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Warnings associated with the device. + * Date and time at which Seam created the error. */ - public array $device_warnings, + public string|null $created_at, /** - * Error code associated with the disconnection event, if any. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ public string|null $error_code, /** - * Description of why the access methods could not be created. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $error_message, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the access control system. + */ + class AcsSystemWarnings + { + public static function from_json(mixed $json): AcsSystemWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * ID of the event. + * Date and time at which Seam created the warning. */ - public string|null $event_id, + public string|null $created_at, /** - * Type of the event. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $event_type, - public Event\From|null $from, + public string|null $message, /** - * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public bool|null $is_fallback_climate_preset, + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Lower temperature limit, in °C, defined by the set threshold. + * Date and time at which Seam created the error. */ - public float|null $lower_limit_celsius, + public string|null $created_at, /** - * Lower temperature limit, in °F, defined by the set threshold. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public float|null $lower_limit_fahrenheit, - public string|null $method, + public string|null $error_code, /** - * Date and time at which the event occurred. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $occurred_at, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. + * Date and time at which Seam created the warning. */ - public array $requested_mutations, + public string|null $created_at, /** - * ID of the affected space. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $space_id, + public string|null $message, /** - * Status of the action. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $status, + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\ConnectedAccountDisconnected { + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Temperature, in °C, reported by the affected thermostat. + * Date and time at which Seam created the error. */ - public float|null $temperature_celsius, + public string|null $created_at, /** - * Temperature, in °F, reported by the affected thermostat. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public float|null $temperature_fahrenheit, + public string|null $error_code, /** - * ID of the thermostat schedule that prompted the affected climate preset to be activated. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $thermostat_schedule_id, - public Event\To|null $to, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Upper temperature limit, in °C, defined by the set threshold. + * Date and time at which Seam created the warning. */ - public float|null $upper_limit_celsius, + public string|null $created_at, /** - * Upper temperature limit, in °F, defined by the set threshold. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $upper_limit_fahrenheit, + public string|null $message, /** - * ID of the workspace associated with the event. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $workspace_id, + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\ConnectedAccountReauthorizationRequested { + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + * Date and time at which Seam created the error. */ - public bool|null $access_code_is_managed = null, + public string|null $created_at, /** - * Key of the affected Access Grant (if present). + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $access_grant_key = null, + public string|null $error_code, /** - * Keys of the access grants associated with this access method (if present). + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public array|null $access_grant_keys = null, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). + * Date and time at which Seam created the warning. */ - public string|null $change_reason = null, + public string|null $created_at, /** - * List of properties that changed on the access code. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public array|null $changed_properties = null, - public string|null $connect_webview_id = null, + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\DeviceDisconnected { + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * @var array|\stdClass|null + * Date and time at which Seam created the error. */ - public array|\stdClass|null $connected_account_custom_metadata = null, + public string|null $created_at, /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public float|null $cooling_set_point_celsius = null, + public string|null $error_code, /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public float|null $cooling_set_point_fahrenheit = null, - public string|null $customer_key = null, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. + * Date and time at which Seam created the warning. */ - public float|null $desired_temperature_celsius = null, + public string|null $created_at, /** - * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $desired_temperature_fahrenheit = null, + public string|null $message, /** - * @var array|\stdClass|null + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public array|\stdClass|null $device_custom_metadata = null, - public string|null $device_name = null, + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the device. + */ + class DeviceErrors + { + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * The new end time for the access grant. + * Date and time at which Seam created the error. */ - public string|null $ends_at = null, + public string|null $created_at, /** - * 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. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $event_description = null, + public string|null $error_code, /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $fan_mode_setting = null, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the device. + */ + class DeviceWarnings + { + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Date and time at which Seam created the warning. */ - public float|null $heating_set_point_celsius = null, + public string|null $created_at, /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $heating_set_point_fahrenheit = null, + public string|null $message, /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $hvac_mode_setting = null, - public string|null $image_url = null, + public string|null $warning_code, + ) {} + } + + enum ErrorCode: string + { + case ACCOUNT_DISCONNECTED = "account_disconnected"; + case HUB_DISCONNECTED = "hub_disconnected"; + case DEVICE_DISCONNECTED = "device_disconnected"; + } +} + +namespace Seam\Resources\Event\DeviceUnmanagedDisconnected { + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). + * Date and time at which Seam created the error. */ - public bool|null $is_backup_code = null, - public bool|null $is_via_bluetooth = null, - public bool|null $is_via_nfc = null, + public string|null $created_at, /** - * Metadata from Minut. - * - * @var array|\stdClass|null + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public array|\stdClass|null $minut_metadata = null, + public string|null $error_code, /** - * 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. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public array|null $missing_device_ids = null, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings + { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Sub-type of motion detected, if available. + * Date and time at which Seam created the warning. */ - public string|null $motion_sub_type = null, + public string|null $created_at, /** - * Detected noise level in decibels. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public float|null $noise_level_decibels = null, + public string|null $message, /** - * Detected noise level in Noiseaware Noise Risk Score (NRS). + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public float|null $noise_level_nrs = null, + public string|null $warning_code, + ) {} + } + + /** + * Errors associated with the device. + */ + class DeviceErrors + { + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( /** - * ID of the noise threshold that was triggered. + * Date and time at which Seam created the error. */ - public string|null $noise_threshold_id = null, + public string|null $created_at, /** - * Name of the noise threshold that was triggered. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $noise_threshold_name = null, + public string|null $error_code, /** - * Metadata from Noiseaware. - * - * @var array|\stdClass|null + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public array|\stdClass|null $noiseaware_metadata = null, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the device. + */ + class DeviceWarnings + { + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( /** - * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + * Date and time at which Seam created the warning. */ - public Event\Reason|null $reason = null, + public string|null $created_at, /** - * Unique key for the space within the workspace. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $space_key = null, + public string|null $message, /** - * The new start time for the access grant. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $starts_at = null, - public string|null $video_url = null, + public string|null $warning_code, ) {} } + + enum ErrorCode: string + { + case ACCOUNT_DISCONNECTED = "account_disconnected"; + case HUB_DISCONNECTED = "hub_disconnected"; + case DEVICE_DISCONNECTED = "device_disconnected"; + } } -namespace Seam\Resources\Event { +namespace Seam\Resources\Event\DeviceBatteryStatusChanged { + enum BatteryStatus: string + { + case CRITICAL = "critical"; + case LOW = "low"; + case GOOD = "good"; + case FULL = "full"; + } +} + +namespace Seam\Resources\Event\DeviceConnectionBecameFlaky { /** - * Errors associated with the access code. + * Errors associated with the connected account. */ - class AccessCodeErrors + class ConnectedAccountErrors { - public static function from_json(mixed $json): AccessCodeErrors|null - { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { if (!$json) { return null; } @@ -489,12 +12510,13 @@ public function __construct( } /** - * Warnings associated with the access code. + * Warnings associated with the connected account. */ - class AccessCodeWarnings + class ConnectedAccountWarnings { - public static function from_json(mixed $json): AccessCodeWarnings|null - { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { if (!$json) { return null; } @@ -522,11 +12544,11 @@ public function __construct( } /** - * Errors associated with the access control system. + * Errors associated with the device. */ - class AcsSystemErrors + class DeviceErrors { - public static function from_json(mixed $json): AcsSystemErrors|null + public static function from_json(mixed $json): DeviceErrors|null { if (!$json) { return null; @@ -555,11 +12577,11 @@ public function __construct( } /** - * Warnings associated with the access control system. + * Warnings associated with the device. */ - class AcsSystemWarnings + class DeviceWarnings { - public static function from_json(mixed $json): AcsSystemWarnings|null + public static function from_json(mixed $json): DeviceWarnings|null { if (!$json) { return null; @@ -586,40 +12608,9 @@ public function __construct( public string|null $warning_code, ) {} } +} - /** - * List of properties that changed on the access code. - */ - class ChangedProperties - { - public static function from_json(mixed $json): ChangedProperties|null - { - if (!$json) { - return null; - } - return new self( - from: $json->from ?? null, - property: $json->property ?? null, - to: $json->to ?? null, - ); - } - - public function __construct( - /** - * Previous value of the property, or null if not set. - */ - public string|null $from, - /** - * Name of the property that changed (e.g. `code`). - */ - public string|null $property, - /** - * New value of the property, or null if cleared. - */ - public string|null $to, - ) {} - } - +namespace Seam\Resources\Event\DeviceErrorSubscriptionRequired { /** * Errors associated with the connected account. */ @@ -753,139 +12744,247 @@ public function __construct( public string|null $warning_code, ) {} } +} - class From +namespace Seam\Resources\Event\DeviceAccessoryKeypadDisconnected { + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors { - public static function from_json(mixed $json): From|null - { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { if (!$json) { return null; } return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Previous pin code. - */ - public string|null $code, - /** - * Previous end time. + * Date and time at which Seam created the error. */ - public string|null $ends_at, + public string|null $created_at, /** - * Previous name of the access code. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $name, + public string|null $error_code, /** - * Previous start time. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $starts_at, + public string|null $message, ) {} } /** - * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + * Warnings associated with the connected account. */ - class Reason + class ConnectedAccountWarnings { - public static function from_json(mixed $json): Reason|null - { + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { if (!$json) { return null; } return new self( + created_at: $json->created_at ?? null, message: $json->message ?? null, - reason_code: $json->reason_code ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Human-readable explanation of why access was denied. + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, /** - * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $reason_code, + public string|null $warning_code, ) {} } /** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. + * Errors associated with the device. */ - class RequestedMutations + class DeviceErrors { - public static function from_json(mixed $json): RequestedMutations|null + public static function from_json(mixed $json): DeviceErrors|null { if (!$json) { return null; } return new self( - from: $json->from ?? null, - mutation_code: $json->mutation_code ?? null, - to: $json->to ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. + * Date and time at which Seam created the error. */ - public string|null $mutation_code, + public string|null $created_at, /** - * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. - * - * @var array|\stdClass|null + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public array|\stdClass|null $from = null, + public string|null $error_code, /** - * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. - * - * @var array|\stdClass|null + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public array|\stdClass|null $to = null, + public string|null $message, ) {} } - class To + /** + * Warnings associated with the device. + */ + class DeviceWarnings { - public static function from_json(mixed $json): To|null + public static function from_json(mixed $json): DeviceWarnings|null { if (!$json) { return null; } return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * New pin code. + * Date and time at which Seam created the warning. */ - public string|null $code, + public string|null $created_at, /** - * New end time. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $ends_at, + public string|null $message, /** - * New name of the access code. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $name, + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\Event\LockLocked { + enum Method: string + { + case KEYCODE = "keycode"; + case MANUAL = "manual"; + case AUTOMATIC = "automatic"; + case UNKNOWN = "unknown"; + case REMOTE = "remote"; + case CARD = "card"; + } +} + +namespace Seam\Resources\Event\LockUnlocked { + enum Method: string + { + case KEYCODE = "keycode"; + case MANUAL = "manual"; + case AUTOMATIC = "automatic"; + case UNKNOWN = "unknown"; + case REMOTE = "remote"; + case CARD = "card"; + } +} + +namespace Seam\Resources\Event\LockAccessDenied { + /** + * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + */ + class Reason + { + public static function from_json(mixed $json): Reason|null + { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + reason_code: $json->reason_code ?? null, + ); + } + + public function __construct( /** - * New start time. + * Human-readable explanation of why access was denied. */ - public string|null $starts_at, + public string|null $message, + /** + * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. + * + * @var value-of<\Seam\Resources\Event\LockAccessDenied\Reason\ReasonCode>|string|null + */ + public string|null $reason_code, ) {} } } + +namespace Seam\Resources\Event\LockAccessDenied\Reason { + enum ReasonCode: string + { + case UNKNOWN_CODE = "unknown_code"; + case EXPIRED_CODE = "expired_code"; + case BLOCKLISTED_CODE = "blocklisted_code"; + case TOO_MANY_ATTEMPTS = "too_many_attempts"; + case BLOCKED_BY_PRIVACY_MODE = "blocked_by_privacy_mode"; + case CREDENTIAL_ERROR = "credential_error"; + } +} + +namespace Seam\Resources\Event\ThermostatManuallyAdjusted { + enum FanModeSetting: string + { + case AUTO = "auto"; + case ON = "on"; + case CIRCULATE = "circulate"; + } + + enum HvacModeSetting: string + { + case OFF = "off"; + case HEAT = "heat"; + case COOL = "cool"; + case HEAT_COOL = "heat_cool"; + case ECO = "eco"; + } + + enum Method: string + { + case SEAM = "seam"; + case EXTERNAL = "external"; + } +} + +namespace Seam\Resources\Event\CameraActivated { + enum ActivationReason: string + { + case MOTION_DETECTED = "motion_detected"; + } + + enum MotionSubType: string + { + case HUMAN = "human"; + case VEHICLE = "vehicle"; + case PACKAGE = "package"; + case OTHER = "other"; + } +} diff --git a/src/Resources/InstantKey.php b/src/Resources/InstantKey.php index f24095a0..b55c18a0 100644 --- a/src/Resources/InstantKey.php +++ b/src/Resources/InstantKey.php @@ -16,16 +16,18 @@ public static function from_json(mixed $json): InstantKey|null return new self( client_session_id: $json->client_session_id ?? null, created_at: $json->created_at ?? null, - customization: isset($json->customization) - ? InstantKey\Customization::from_json($json->customization) - : null, - customization_profile_id: $json->customization_profile_id ?? - null, expires_at: $json->expires_at ?? null, instant_key_id: $json->instant_key_id ?? null, instant_key_url: $json->instant_key_url ?? null, user_identity_id: $json->user_identity_id ?? null, workspace_id: $json->workspace_id ?? null, + customization: isset($json->customization) + ? \Seam\Resources\InstantKey\Customization::from_json( + $json->customization, + ) + : null, + customization_profile_id: $json->customization_profile_id ?? + null, ); } @@ -61,7 +63,7 @@ public function __construct( /** * Customization applied to the Instant Key UI. */ - public InstantKey\Customization|null $customization = null, + public \Seam\Resources\InstantKey\Customization|null $customization = null, /** * ID of the customization profile associated with the Instant Key. */ diff --git a/src/Resources/NoiseThreshold.php b/src/Resources/NoiseThreshold.php index 24d7e7e0..94f75866 100644 --- a/src/Resources/NoiseThreshold.php +++ b/src/Resources/NoiseThreshold.php @@ -18,8 +18,8 @@ public static function from_json(mixed $json): NoiseThreshold|null noise_threshold_decibels: $json->noise_threshold_decibels ?? null, noise_threshold_id: $json->noise_threshold_id ?? null, - noise_threshold_nrs: $json->noise_threshold_nrs ?? null, starts_daily_at: $json->starts_daily_at ?? null, + noise_threshold_nrs: $json->noise_threshold_nrs ?? null, ); } diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index c56b8b5e..1573cb51 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -18,18 +18,20 @@ public static function from_json(mixed $json): Phone|null device_type: $json->device_type ?? null, display_name: $json->display_name ?? null, errors: array_map( - fn($e) => Phone\Errors::from_json($e), + fn($e) => \Seam\Resources\Phone\Errors::from_json($e), $json->errors ?? [], ), - nickname: $json->nickname ?? null, properties: isset($json->properties) - ? Phone\Properties::from_json($json->properties) + ? \Seam\Resources\Phone\Properties::from_json( + $json->properties, + ) : null, warnings: array_map( - fn($w) => Phone\Warnings::from_json($w), + fn($w) => \Seam\Resources\Phone\Warnings::from_json($w), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, + nickname: $json->nickname ?? null, ); } @@ -50,6 +52,8 @@ public function __construct( public string|null $device_id, /** * Type of the phone device, such as `ios_phone` or `android_phone`. + * + * @var value-of<\Seam\Resources\Phone\DeviceType>|string|null */ public string|null $device_type, /** @@ -58,14 +62,18 @@ public function __construct( public string|null $display_name, /** * Errors associated with the phone. + * + * @var list<\Seam\Resources\Phone\Errors> */ public array $errors, /** * Properties of the phone. */ - public Phone\Properties|null $properties, + public \Seam\Resources\Phone\Properties|null $properties, /** * Warnings associated with the phone. + * + * @var list<\Seam\Resources\Phone\Warnings> */ public array $warnings, /** @@ -128,14 +136,14 @@ public static function from_json(mixed $json): Properties|null assa_abloy_credential_service_metadata: isset( $json->assa_abloy_credential_service_metadata, ) - ? Properties\AssaAbloyCredentialServiceMetadata::from_json( + ? \Seam\Resources\Phone\Properties\AssaAbloyCredentialServiceMetadata::from_json( $json->assa_abloy_credential_service_metadata, ) : null, salto_space_credential_service_metadata: isset( $json->salto_space_credential_service_metadata, ) - ? Properties\SaltoSpaceCredentialServiceMetadata::from_json( + ? \Seam\Resources\Phone\Properties\SaltoSpaceCredentialServiceMetadata::from_json( $json->salto_space_credential_service_metadata, ) : null, @@ -146,11 +154,11 @@ public function __construct( /** * ASSA ABLOY Credential Service metadata for the phone. */ - public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata = null, + public \Seam\Resources\Phone\Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata = null, /** * Salto Space credential service metadata for the phone. */ - public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata = null, + public \Seam\Resources\Phone\Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata = null, ) {} } @@ -186,6 +194,12 @@ public function __construct( public string|null $warning_code, ) {} } + + enum DeviceType: string + { + case IOS_PHONE = "ios_phone"; + case ANDROID_PHONE = "android_phone"; + } } namespace Seam\Resources\Phone\Properties { @@ -204,7 +218,7 @@ public static function from_json( endpoints: array_map( fn( $e, - ) => AssaAbloyCredentialServiceMetadata\Endpoints::from_json( + ) => \Seam\Resources\Phone\Properties\AssaAbloyCredentialServiceMetadata\Endpoints::from_json( $e, ), $json->endpoints ?? [], @@ -216,6 +230,8 @@ public static function from_json( public function __construct( /** * Endpoints associated with the phone. + * + * @var list<\Seam\Resources\Phone\Properties\AssaAbloyCredentialServiceMetadata\Endpoints>|null */ public array|null $endpoints = null, /** diff --git a/src/Resources/Space.php b/src/Resources/Space.php index f5d9a754..b93e0512 100644 --- a/src/Resources/Space.php +++ b/src/Resources/Space.php @@ -14,19 +14,23 @@ public static function from_json(mixed $json): Space|null return new self( acs_entrance_count: $json->acs_entrance_count ?? null, created_at: $json->created_at ?? null, + device_count: $json->device_count ?? null, + display_name: $json->display_name ?? null, + name: $json->name ?? null, + space_id: $json->space_id ?? null, + workspace_id: $json->workspace_id ?? null, customer_data: isset($json->customer_data) - ? Space\CustomerData::from_json($json->customer_data) + ? \Seam\Resources\Space\CustomerData::from_json( + $json->customer_data, + ) : null, customer_key: $json->customer_key ?? null, - device_count: $json->device_count ?? null, - display_name: $json->display_name ?? null, geolocation: isset($json->geolocation) - ? Space\Geolocation::from_json($json->geolocation) + ? \Seam\Resources\Space\Geolocation::from_json( + $json->geolocation, + ) : null, - name: $json->name ?? null, - space_id: $json->space_id ?? null, space_key: $json->space_key ?? null, - workspace_id: $json->workspace_id ?? null, ); } @@ -62,7 +66,7 @@ public function __construct( /** * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). */ - public Space\CustomerData|null $customer_data = null, + public \Seam\Resources\Space\CustomerData|null $customer_data = null, /** * Customer key associated with the space. */ @@ -70,7 +74,7 @@ public function __construct( /** * Geographic coordinates (latitude and longitude) of the space. */ - public Space\Geolocation|null $geolocation = null, + public \Seam\Resources\Space\Geolocation|null $geolocation = null, /** * Unique key for the space within the workspace. */ diff --git a/src/Resources/ThermostatDailyProgram.php b/src/Resources/ThermostatDailyProgram.php index 1733a4dc..e19bb791 100644 --- a/src/Resources/ThermostatDailyProgram.php +++ b/src/Resources/ThermostatDailyProgram.php @@ -17,7 +17,11 @@ public static function from_json( device_id: $json->device_id ?? null, name: $json->name ?? null, periods: array_map( - fn($p) => ThermostatDailyProgram\Periods::from_json($p), + fn( + $p, + ) => \Seam\Resources\ThermostatDailyProgram\Periods::from_json( + $p, + ), $json->periods ?? [], ), thermostat_daily_program_id: $json->thermostat_daily_program_id ?? @@ -41,6 +45,8 @@ public function __construct( public string|null $name, /** * Array of thermostat daily program periods. + * + * @var list<\Seam\Resources\ThermostatDailyProgram\Periods> */ public array $periods, /** diff --git a/src/Resources/ThermostatSchedule.php b/src/Resources/ThermostatSchedule.php index 0ebf3370..225123db 100644 --- a/src/Resources/ThermostatSchedule.php +++ b/src/Resources/ThermostatSchedule.php @@ -17,16 +17,20 @@ public static function from_json(mixed $json): ThermostatSchedule|null device_id: $json->device_id ?? null, ends_at: $json->ends_at ?? null, errors: array_map( - fn($e) => ThermostatSchedule\Errors::from_json($e), + fn( + $e, + ) => \Seam\Resources\ThermostatSchedule\Errors::from_json( + $e, + ), $json->errors ?? [], ), - is_override_allowed: $json->is_override_allowed ?? null, - max_override_period_minutes: $json->max_override_period_minutes ?? - null, name: $json->name ?? null, starts_at: $json->starts_at ?? null, thermostat_schedule_id: $json->thermostat_schedule_id ?? null, workspace_id: $json->workspace_id ?? null, + is_override_allowed: $json->is_override_allowed ?? null, + max_override_period_minutes: $json->max_override_period_minutes ?? + null, ); } @@ -49,6 +53,8 @@ public function __construct( public string|null $ends_at, /** * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * + * @var list<\Seam\Resources\ThermostatSchedule\Errors> */ public array $errors, /** diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 61863afe..35991da2 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -23,34 +23,42 @@ public static function from_json(mixed $json): UnmanagedAccessCode|null } return new self( access_code_id: $json->access_code_id ?? null, - cannot_be_managed: $json->cannot_be_managed ?? null, - cannot_delete_unmanaged_access_code: $json->cannot_delete_unmanaged_access_code ?? - null, code: $json->code ?? null, created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, - dormakaba_oracode_metadata: isset( - $json->dormakaba_oracode_metadata, - ) - ? UnmanagedAccessCode\DormakabaOracodeMetadata::from_json( - $json->dormakaba_oracode_metadata, - ) - : null, - ends_at: $json->ends_at ?? null, errors: array_map( - fn($e) => UnmanagedAccessCode\Errors::from_json($e), + fn( + $e, + ) => \Seam\Resources\UnmanagedAccessCode\Errors::from_json( + $e, + ), $json->errors ?? [], ), is_managed: $json->is_managed ?? null, name: $json->name ?? null, - starts_at: $json->starts_at ?? null, status: $json->status ?? null, type: $json->type ?? null, warnings: array_map( - fn($w) => UnmanagedAccessCode\Warnings::from_json($w), + fn( + $w, + ) => \Seam\Resources\UnmanagedAccessCode\Warnings::from_json( + $w, + ), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, + cannot_be_managed: $json->cannot_be_managed ?? null, + cannot_delete_unmanaged_access_code: $json->cannot_delete_unmanaged_access_code ?? + null, + dormakaba_oracode_metadata: isset( + $json->dormakaba_oracode_metadata, + ) + ? \Seam\Resources\UnmanagedAccessCode\DormakabaOracodeMetadata::from_json( + $json->dormakaba_oracode_metadata, + ) + : null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, ); } @@ -73,6 +81,8 @@ public function __construct( public string|null $device_id, /** * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * + * @var list<\Seam\Resources\UnmanagedAccessCode\Errors> */ public array $errors, /** @@ -85,14 +95,20 @@ public function __construct( public string|null $name, /** * Current status of the access code within the operational lifecycle. `set` indicates that the code is active and operational. `unset` indicates that the code exists on the provider but is not usable on the device. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Status>|string|null */ public string|null $status, /** * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Type>|string|null */ public string|null $type, /** * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * + * @var list<\Seam\Resources\UnmanagedAccessCode\Warnings> */ public array $warnings, /** @@ -110,7 +126,7 @@ public function __construct( /** * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. */ - public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, + public \Seam\Resources\UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Date and time after which the time-bound access code becomes inactive. */ @@ -184,7 +200,7 @@ public function __construct( } /** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -193,181 +209,1805 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_access_code_error: $json->is_access_code_error ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - managed_access_code_id: $json->managed_access_code_id ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => Errors\ModifiedFields::from_json($m), - $json->modified_fields ?? [], + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::PROVIDER_ISSUE + => \Seam\Resources\UnmanagedAccessCode\Errors\ProviderIssue::from_json( + $json, ), - unmanaged_access_code_id: $json->unmanaged_access_code_id ?? - null, - ); + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::FAILED_TO_SET_ON_DEVICE + => \Seam\Resources\UnmanagedAccessCode\Errors\FailedToSetOnDevice::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::FAILED_TO_REMOVE_FROM_DEVICE + => \Seam\Resources\UnmanagedAccessCode\Errors\FailedToRemoveFromDevice::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::DUPLICATE_CODE_ON_DEVICE + => \Seam\Resources\UnmanagedAccessCode\Errors\DuplicateCodeOnDevice::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::NO_SPACE_FOR_ACCESS_CODE_ON_DEVICE + => \Seam\Resources\UnmanagedAccessCode\Errors\NoSpaceForAccessCodeOnDevice::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::CONFLICTING_EXTERNAL_MODIFICATION + => \Seam\Resources\UnmanagedAccessCode\Errors\ConflictingExternalModification::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::ACCESS_CODE_INACTIVE + => \Seam\Resources\UnmanagedAccessCode\Errors\AccessCodeInactive::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::ACCOUNT_DISCONNECTED + => \Seam\Resources\UnmanagedAccessCode\Errors\AccountDisconnected::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED + => \Seam\Resources\UnmanagedAccessCode\Errors\SaltoKsSubscriptionLimitExceeded::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::INSUFFICIENT_PERMISSIONS + => \Seam\Resources\UnmanagedAccessCode\Errors\InsufficientPermissions::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::DORMAKABA_SITES_DISCONNECTED + => \Seam\Resources\UnmanagedAccessCode\Errors\DormakabaSitesDisconnected::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::DEVICE_OFFLINE + => \Seam\Resources\UnmanagedAccessCode\Errors\DeviceOffline::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::DEVICE_REMOVED + => \Seam\Resources\UnmanagedAccessCode\Errors\DeviceRemoved::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::HUB_DISCONNECTED + => \Seam\Resources\UnmanagedAccessCode\Errors\HubDisconnected::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::DEVICE_DISCONNECTED + => \Seam\Resources\UnmanagedAccessCode\Errors\DeviceDisconnected::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::EMPTY_BACKUP_ACCESS_CODE_POOL + => \Seam\Resources\UnmanagedAccessCode\Errors\EmptyBackupAccessCodePool::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::AUGUST_LOCK_NOT_AUTHORIZED + => \Seam\Resources\UnmanagedAccessCode\Errors\AugustLockNotAuthorized::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::MISSING_DEVICE_CREDENTIALS + => \Seam\Resources\UnmanagedAccessCode\Errors\MissingDeviceCredentials::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::AUXILIARY_HEAT_RUNNING + => \Seam\Resources\UnmanagedAccessCode\Errors\AuxiliaryHeatRunning::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::SUBSCRIPTION_REQUIRED + => \Seam\Resources\UnmanagedAccessCode\Errors\SubscriptionRequired::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::BRIDGE_DISCONNECTED + => \Seam\Resources\UnmanagedAccessCode\Errors\BridgeDisconnected::from_json( + $json, + ), + default => new self( + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null */ public string|null $error_code, /** - * Indicates that this is an access code error. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public true|null $is_access_code_error, - public bool|null $is_connected_account_error, - public bool|null $is_device_error, + public string|null $message, + ) {} + } + + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::CODE_ROTATES_PERIODICALLY + => \Seam\Resources\UnmanagedAccessCode\Warnings\CodeRotatesPeriodically::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::TIME_FRAME_ADJUSTED_FOR_UNKNOWN_TIME_ZONE + => \Seam\Resources\UnmanagedAccessCode\Warnings\TimeFrameAdjustedForUnknownTimeZone::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::EXTERNAL_MODIFICATION_IN_EFFECT + => \Seam\Resources\UnmanagedAccessCode\Warnings\ExternalModificationInEffect::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::DELAY_IN_SETTING_ON_DEVICE + => \Seam\Resources\UnmanagedAccessCode\Warnings\DelayInSettingOnDevice::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::DELAY_IN_REMOVING_FROM_DEVICE + => \Seam\Resources\UnmanagedAccessCode\Warnings\DelayInRemovingFromDevice::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::THIRD_PARTY_INTEGRATION_DETECTED + => \Seam\Resources\UnmanagedAccessCode\Warnings\ThirdPartyIntegrationDetected::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::IGLOO_ALGOPIN_MUST_BE_USED_WITHIN_24_HOURS + => \Seam\Resources\UnmanagedAccessCode\Warnings\IglooAlgopinMustBeUsedWithin_24Hours::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::MANAGEMENT_TRANSFERRED + => \Seam\Resources\UnmanagedAccessCode\Warnings\ManagementTransferred::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::USING_BACKUP_ACCESS_CODE + => \Seam\Resources\UnmanagedAccessCode\Warnings\UsingBackupAccessCode::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\UnmanagedAccessCode\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode::UNKNOWN_ISSUE_WITH_ACCESS_CODE + => \Seam\Resources\UnmanagedAccessCode\Warnings\UnknownIssueWithAccessCode::from_json( + $json, + ), + default => new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ), + }; + } + + public function __construct( /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, /** - * 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. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null */ - public string|null $change_type = null, + public string|null $warning_code, /** - * Date and time at which Seam created the error. + * Date and time at which Seam created the warning. */ public string|null $created_at = null, + ) {} + } + + enum Status: string + { + case SET = "set"; + case VALUE_UNSET = "unset"; + } + + enum Type: string + { + case TIME_BOUND = "time_bound"; + case ONGOING = "ongoing"; + } +} + +namespace Seam\Resources\UnmanagedAccessCode\Errors { + /** + * Indicates a provider-specific issue that prevents the access code from being set or managed. Check the error message for details. + */ + final class ProviderIssue extends \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): ProviderIssue|null + { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null */ - public bool|null $is_bridge_error = null, + string|null $error_code, /** - * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + * Indicates that this is an access code error. */ - public string|null $managed_access_code_id = null, + public true|null $is_access_code_error, /** - * List of fields that were changed externally, with their previous and new values. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public array|null $modified_fields = null, + string|null $message, /** - * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + * Date and time at which Seam created the error. */ - public string|null $unmanaged_access_code_id = null, - ) {} + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } } /** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * Failed to set code on device. */ - class Warnings + final class FailedToSetOnDevice extends + \Seam\Resources\UnmanagedAccessCode\Errors { - public static function from_json(mixed $json): Warnings|null + public static function from_json(mixed $json): FailedToSetOnDevice|null { if (!$json) { return null; } return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, message: $json->message ?? null, - modified_fields: array_map( - fn($m) => Warnings\ModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, ); } public function __construct( /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null */ - public string|null $message, + string|null $error_code, /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * Indicates that this is an access code error. */ - public string|null $warning_code, + public true|null $is_access_code_error, /** - * 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. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $change_type = null, + string|null $message, /** - * Date and time at which Seam created the warning. + * Date and time at which Seam created the error. */ public string|null $created_at = null, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array|null $modified_fields = null, - ) {} + ) { + parent::__construct(error_code: $error_code, message: $message); + } } -} -namespace Seam\Resources\UnmanagedAccessCode\Errors { /** - * List of fields that were changed externally, with their previous and new values. + * Failed to remove code from device. */ - class ModifiedFields + final class FailedToRemoveFromDevice extends + \Seam\Resources\UnmanagedAccessCode\Errors { - public static function from_json(mixed $json): ModifiedFields|null - { + public static function from_json( + mixed $json, + ): FailedToRemoveFromDevice|null { if (!$json) { return null; } return new self( - field: $json->field ?? null, - from: $json->from ?? null, - to: $json->to ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, ); } public function __construct( /** - * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null */ - public string|null $field, + string|null $error_code, /** - * The previous value of the field. + * Indicates that this is an access code error. */ - public string|null $from, + public true|null $is_access_code_error, /** - * The new value of the field. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $to, - ) {} + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } } -} -namespace Seam\Resources\UnmanagedAccessCode\Warnings { /** - * List of fields that were changed externally, with their previous and new values. + * Duplicate access code detected on device. */ - class ModifiedFields + final class DuplicateCodeOnDevice extends + \Seam\Resources\UnmanagedAccessCode\Errors { - public static function from_json(mixed $json): ModifiedFields|null - { + public static function from_json( + mixed $json, + ): DuplicateCodeOnDevice|null { if (!$json) { return null; } return new self( - field: $json->field ?? null, - from: $json->from ?? null, - to: $json->to ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + managed_access_code_id: $json->managed_access_code_id ?? null, + unmanaged_access_code_id: $json->unmanaged_access_code_id ?? + null, ); } public function __construct( /** - * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null */ - public string|null $field, + string|null $error_code, /** - * The previous value of the field. + * Indicates that this is an access code error. */ - public string|null $from, + public true|null $is_access_code_error, /** - * The new value of the field. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $to, - ) {} + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + /** + * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $managed_access_code_id = null, + /** + * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $unmanaged_access_code_id = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * No space for access code on device. + */ + final class NoSpaceForAccessCodeOnDevice extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): NoSpaceForAccessCodeOnDevice|null { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * 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. + */ + final class ConflictingExternalModification extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): ConflictingExternalModification|null { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + modified_fields: array_map( + fn( + $m, + ) => \Seam\Resources\UnmanagedAccessCode\Errors\ConflictingExternalModification\ModifiedFields::from_json( + $m, + ), + $json->modified_fields ?? [], + ), + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * 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. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ConflictingExternalModification\ChangeType>|string|null + */ + public string|null $change_type = null, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + /** + * List of fields that were changed externally, with their previous and new values. + * + * @var list<\Seam\Resources\UnmanagedAccessCode\Errors\ConflictingExternalModification\ModifiedFields>|null + */ + public array|null $modified_fields = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the access code is disabled or inactive on the device. The code exists but will not grant access until re-enabled. + */ + final class AccessCodeInactive extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): AccessCodeInactive|null + { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the account is disconnected. + */ + final class AccountDisconnected extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): AccountDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the Salto site user limit has been reached. + */ + final class SaltoKsSubscriptionLimitExceeded extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitExceeded|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * 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. + */ + final class InsufficientPermissions extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): InsufficientPermissions|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. + */ + final class DormakabaSitesDisconnected extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): DormakabaSitesDisconnected|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the device is offline. + */ + final class DeviceOffline extends \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): DeviceOffline|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the device has been removed. + */ + final class DeviceRemoved extends \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): DeviceRemoved|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the hub is disconnected. + */ + final class HubDisconnected extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): HubDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the device is disconnected. + */ + final class DeviceDisconnected extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): DeviceDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. + */ + final class EmptyBackupAccessCodePool extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): EmptyBackupAccessCodePool|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the user is not authorized to use the August lock. + */ + final class AugustLockNotAuthorized extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): AugustLockNotAuthorized|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that device credentials are missing. + */ + final class MissingDeviceCredentials extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): MissingDeviceCredentials|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the auxiliary heat is running. + */ + final class AuxiliaryHeatRunning extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): AuxiliaryHeatRunning|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that a subscription is required to connect. + */ + final class SubscriptionRequired extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): SubscriptionRequired|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + /** + * Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/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](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system-errors-seam_bridge_disconnected). + */ + final class BridgeDisconnected extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json(mixed $json): BridgeDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error = null, + /** + * Indicates whether the error is related specifically to the connected account. + */ + public bool|null $is_connected_account_error = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + + enum ErrorCode: string + { + case PROVIDER_ISSUE = "provider_issue"; + case FAILED_TO_SET_ON_DEVICE = "failed_to_set_on_device"; + case FAILED_TO_REMOVE_FROM_DEVICE = "failed_to_remove_from_device"; + case DUPLICATE_CODE_ON_DEVICE = "duplicate_code_on_device"; + case NO_SPACE_FOR_ACCESS_CODE_ON_DEVICE = "no_space_for_access_code_on_device"; + case CONFLICTING_EXTERNAL_MODIFICATION = "conflicting_external_modification"; + case ACCESS_CODE_INACTIVE = "access_code_inactive"; + case ACCOUNT_DISCONNECTED = "account_disconnected"; + case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; + case INSUFFICIENT_PERMISSIONS = "insufficient_permissions"; + case DORMAKABA_SITES_DISCONNECTED = "dormakaba_sites_disconnected"; + case DEVICE_OFFLINE = "device_offline"; + case DEVICE_REMOVED = "device_removed"; + case HUB_DISCONNECTED = "hub_disconnected"; + case DEVICE_DISCONNECTED = "device_disconnected"; + case EMPTY_BACKUP_ACCESS_CODE_POOL = "empty_backup_access_code_pool"; + case AUGUST_LOCK_NOT_AUTHORIZED = "august_lock_not_authorized"; + case MISSING_DEVICE_CREDENTIALS = "missing_device_credentials"; + case AUXILIARY_HEAT_RUNNING = "auxiliary_heat_running"; + case SUBSCRIPTION_REQUIRED = "subscription_required"; + case BRIDGE_DISCONNECTED = "bridge_disconnected"; + } +} + +namespace Seam\Resources\UnmanagedAccessCode\Errors\ConflictingExternalModification { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); + } + + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } + + enum ChangeType: string + { + case MODIFIED = "modified"; + case REMOVED = "removed"; + } +} + +namespace Seam\Resources\UnmanagedAccessCode\Warnings { + /** + * The access code's PIN rotates periodically when the code is renewed. Retrieve the latest code before each use. + */ + final class CodeRotatesPeriodically extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): CodeRotatesPeriodically|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class TimeFrameAdjustedForUnknownTimeZone extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): TimeFrameAdjustedForUnknownTimeZone|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class ExternalModificationInEffect extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): ExternalModificationInEffect|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + modified_fields: array_map( + fn( + $m, + ) => \Seam\Resources\UnmanagedAccessCode\Warnings\ExternalModificationInEffect\ModifiedFields::from_json( + $m, + ), + $json->modified_fields ?? [], + ), + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * 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. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\ExternalModificationInEffect\ChangeType>|string|null + */ + public string|null $change_type = null, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + /** + * List of fields that were changed externally, with their previous and new values. + * + * @var list<\Seam\Resources\UnmanagedAccessCode\Warnings\ExternalModificationInEffect\ModifiedFields>|null + */ + public array|null $modified_fields = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Delay in setting code on device. + */ + final class DelayInSettingOnDevice extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): DelayInSettingOnDevice|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Delay in removing code from device. + */ + final class DelayInRemovingFromDevice extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): DelayInRemovingFromDevice|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Third-party integration detected that may cause access codes to fail. + */ + final class ThirdPartyIntegrationDetected extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): ThirdPartyIntegrationDetected|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Algopins must be used within 24 hours. + */ + final class IglooAlgopinMustBeUsedWithin_24Hours extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): IglooAlgopinMustBeUsedWithin_24Hours|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Management was transferred to another workspace. + */ + final class ManagementTransferred extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): ManagementTransferred|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * A backup access code has been pulled and is being used in place of this access code. + */ + final class UsingBackupAccessCode extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): UsingBackupAccessCode|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Access code is being deleted. + */ + final class BeingDeleted extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * An unknown issue occurred with the access code. + */ + final class UnknownIssueWithAccessCode extends + \Seam\Resources\UnmanagedAccessCode\Warnings + { + public static function from_json( + mixed $json, + ): UnknownIssueWithAccessCode|null { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case CODE_ROTATES_PERIODICALLY = "code_rotates_periodically"; + case TIME_FRAME_ADJUSTED_FOR_UNKNOWN_TIME_ZONE = "time_frame_adjusted_for_unknown_time_zone"; + case EXTERNAL_MODIFICATION_IN_EFFECT = "external_modification_in_effect"; + case DELAY_IN_SETTING_ON_DEVICE = "delay_in_setting_on_device"; + case DELAY_IN_REMOVING_FROM_DEVICE = "delay_in_removing_from_device"; + case THIRD_PARTY_INTEGRATION_DETECTED = "third_party_integration_detected"; + case IGLOO_ALGOPIN_MUST_BE_USED_WITHIN_24_HOURS = "igloo_algopin_must_be_used_within_24_hours"; + case MANAGEMENT_TRANSFERRED = "management_transferred"; + case USING_BACKUP_ACCESS_CODE = "using_backup_access_code"; + case BEING_DELETED = "being_deleted"; + case UNKNOWN_ISSUE_WITH_ACCESS_CODE = "unknown_issue_with_access_code"; + } +} + +namespace Seam\Resources\UnmanagedAccessCode\Warnings\ExternalModificationInEffect { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); + } + + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } + + enum ChangeType: string + { + case MODIFIED = "modified"; + case REMOVED = "removed"; } } diff --git a/src/Resources/UnmanagedAccessGrant.php b/src/Resources/UnmanagedAccessGrant.php index 03726d3b..dd1d7703 100644 --- a/src/Resources/UnmanagedAccessGrant.php +++ b/src/Resources/UnmanagedAccessGrant.php @@ -18,13 +18,19 @@ public static function from_json(mixed $json): UnmanagedAccessGrant|null display_name: $json->display_name ?? null, ends_at: $json->ends_at ?? null, errors: array_map( - fn($e) => UnmanagedAccessGrant\Errors::from_json($e), + fn( + $e, + ) => \Seam\Resources\UnmanagedAccessGrant\Errors::from_json( + $e, + ), $json->errors ?? [], ), location_ids: $json->location_ids ?? null, name: $json->name ?? null, pending_mutations: array_map( - fn($p) => UnmanagedAccessGrant\PendingMutations::from_json( + fn( + $p, + ) => \Seam\Resources\UnmanagedAccessGrant\PendingMutations::from_json( $p, ), $json->pending_mutations ?? [], @@ -32,20 +38,24 @@ public static function from_json(mixed $json): UnmanagedAccessGrant|null requested_access_methods: array_map( fn( $r, - ) => UnmanagedAccessGrant\RequestedAccessMethods::from_json( + ) => \Seam\Resources\UnmanagedAccessGrant\RequestedAccessMethods::from_json( $r, ), $json->requested_access_methods ?? [], ), - reservation_key: $json->reservation_key ?? null, space_ids: $json->space_ids ?? null, starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, warnings: array_map( - fn($w) => UnmanagedAccessGrant\Warnings::from_json($w), + fn( + $w, + ) => \Seam\Resources\UnmanagedAccessGrant\Warnings::from_json( + $w, + ), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, + reservation_key: $json->reservation_key ?? null, + user_identity_id: $json->user_identity_id ?? null, ); } @@ -56,6 +66,8 @@ public function __construct( public string|null $access_grant_id, /** * IDs of the access methods created for the Access Grant. + * + * @var list|null */ public array|null $access_method_ids, /** @@ -72,9 +84,12 @@ public function __construct( public string|null $ends_at, /** * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + * + * @var list<\Seam\Resources\UnmanagedAccessGrant\Errors> */ public array $errors, /** + * @var list|null * @deprecated Use `space_ids`. */ public array|null $location_ids, @@ -84,14 +99,20 @@ public function __construct( public string|null $name, /** * List of pending mutations for the access grant. This shows updates that are in progress. + * + * @var list<\Seam\Resources\UnmanagedAccessGrant\PendingMutations> */ public array $pending_mutations, /** * Access methods that the user requested for the Access Grant. + * + * @var list<\Seam\Resources\UnmanagedAccessGrant\RequestedAccessMethods> */ public array $requested_access_methods, /** * IDs of the spaces to which the Access Grant gives access. + * + * @var list|null */ public array|null $space_ids, /** @@ -100,6 +121,8 @@ public function __construct( public string|null $starts_at, /** * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + * + * @var list<\Seam\Resources\UnmanagedAccessGrant\Warnings> */ public array $warnings, /** @@ -120,7 +143,7 @@ public function __construct( namespace Seam\Resources\UnmanagedAccessGrant { /** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -129,12 +152,24 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\UnmanagedAccessGrant\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedAccessGrant\Errors\ErrorCode::CANNOT_CREATE_REQUESTED_ACCESS_METHODS + => \Seam\Resources\UnmanagedAccessGrant\Errors\CannotCreateRequestedAccessMethods::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ), + }; } public function __construct( @@ -144,6 +179,8 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Errors\ErrorCode>|string|null */ public string|null $error_code, /** @@ -152,13 +189,15 @@ public function __construct( public string|null $message, /** * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. + * + * @var list|null */ public array|null $missing_device_ids = null, ) {} } /** - * List of pending mutations for the access grant. This shows updates that are in progress. + * List of pending mutations for the access grant. This shows updates that are in progress. Known mutation_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class PendingMutations { @@ -167,36 +206,44 @@ public static function from_json(mixed $json): PendingMutations|null if (!$json) { return null; } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? PendingMutations\From::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? PendingMutations\To::from_json($json->to) - : null, - ); + $discriminant = is_string($json->mutation_code ?? null) + ? \Seam\Resources\UnmanagedAccessGrant\PendingMutations\MutationCode::tryFrom( + $json->mutation_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedAccessGrant\PendingMutations\MutationCode::UPDATING_SPACES + => \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingSpaces::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessGrant\PendingMutations\MutationCode::UPDATING_ACCESS_TIMES + => \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingAccessTimes::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ), + }; } public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, /** * Date and time at which the mutation was created. */ public string|null $created_at, - public PendingMutations\From|null $from, /** * Detailed description of the mutation. */ public string|null $message, + /** + * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\PendingMutations\MutationCode>|string|null + */ public string|null $mutation_code, - public PendingMutations\To|null $to, ) {} } @@ -212,20 +259,22 @@ public static function from_json( return null; } return new self( - code: $json->code ?? null, created_access_method_ids: $json->created_access_method_ids ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, + mode: $json->mode ?? null, + code: $json->code ?? null, instant_key_max_use_count: $json->instant_key_max_use_count ?? null, - mode: $json->mode ?? null, ); } public function __construct( /** * IDs of the access methods created for the requested access method. + * + * @var list|null */ public array|null $created_access_method_ids, /** @@ -238,6 +287,8 @@ public function __construct( public string|null $display_name, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\RequestedAccessMethods\Mode>|string|null */ public string|null $mode, /** @@ -252,11 +303,203 @@ public function __construct( } /** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\UnmanagedAccessGrant\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode::UNDERPROVISIONED_ACCESS + => \Seam\Resources\UnmanagedAccessGrant\Warnings\UnderprovisionedAccess::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode::OVERPROVISIONED_ACCESS + => \Seam\Resources\UnmanagedAccessGrant\Warnings\OverprovisionedAccess::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode::UPDATING_ACCESS_TIMES + => \Seam\Resources\UnmanagedAccessGrant\Warnings\UpdatingAccessTimes::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode::REQUESTED_CODE_UNAVAILABLE + => \Seam\Resources\UnmanagedAccessGrant\Warnings\RequestedCodeUnavailable::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode::DEVICE_DOES_NOT_SUPPORT_ACCESS_CODES + => \Seam\Resources\UnmanagedAccessGrant\Warnings\DeviceDoesNotSupportAccessCodes::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode::DEVICE_TIME_CONSTRAINTS_VIOLATED + => \Seam\Resources\UnmanagedAccessGrant\Warnings\DeviceTimeConstraintsViolated::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\UnmanagedAccessGrant\Errors { + /** + * Indicates that Seam could not create one or more of the requested access methods for the access grant. + */ + final class CannotCreateRequestedAccessMethods extends + \Seam\Resources\UnmanagedAccessGrant\Errors + { + public static function from_json( + mixed $json, + ): CannotCreateRequestedAccessMethods|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. + * + * @var list|null + */ + array|null $missing_device_ids = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + missing_device_ids: $missing_device_ids, + ); + } + } + + enum ErrorCode: string + { + case CANNOT_CREATE_REQUESTED_ACCESS_METHODS = "cannot_create_requested_access_methods"; + } +} + +namespace Seam\Resources\UnmanagedAccessGrant\PendingMutations { + /** + * Seam is in the process of updating the devices/spaces associated with this access grant. + */ + final class UpdatingSpaces extends + \Seam\Resources\UnmanagedAccessGrant\PendingMutations + { + public static function from_json(mixed $json): UpdatingSpaces|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingSpaces\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingSpaces\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Previous location configuration. + */ + public \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingSpaces\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New location configuration. + */ + public \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingSpaces\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of updating the access times for this access grant. + */ + final class UpdatingAccessTimes extends + \Seam\Resources\UnmanagedAccessGrant\PendingMutations + { + public static function from_json(mixed $json): UpdatingAccessTimes|null { if (!$json) { return null; @@ -264,58 +507,125 @@ public static function from_json(mixed $json): Warnings|null return new self( access_method_ids: $json->access_method_ids ?? null, created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - failed_devices: array_map( - fn($f) => Warnings\FailedDevices::from_json($f), - $json->failed_devices ?? [], - ), + from: isset($json->from) + ? \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingAccessTimes\From::from_json( + $json->from, + ) + : null, message: $json->message ?? null, - new_code: $json->new_code ?? null, - original_code: $json->original_code ?? null, - reason: $json->reason ?? null, - warning_code: $json->warning_code ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingAccessTimes\To::from_json( + $json->to, + ) + : null, ); } public function __construct( /** * IDs of the access methods being updated. + * + * @var list|null */ public array|null $access_method_ids, /** - * Date and time at which Seam created the warning. + * Date and time at which the mutation was created. */ - public string|null $created_at, - public string|null $device_id, + string|null $created_at, /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Previous access time configuration. */ - public string|null $message, + public \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingAccessTimes\From|null $from, /** - * The new PIN code that was assigned instead. + * Detailed description of the mutation. */ - public string|null $new_code, + string|null $message, /** - * The originally requested PIN code that was unavailable. + * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\PendingMutations\MutationCode>|string|null */ - public string|null $original_code, + string|null $mutation_code, /** - * Specific reason why the grant's times are not programmable on the device. + * New access time configuration. */ - public string|null $reason, + public \Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingAccessTimes\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + enum MutationCode: string + { + case UPDATING_SPACES = "updating_spaces"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + } +} + +namespace Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingSpaces { + /** + * Previous location configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * Previous device IDs where access codes existed. + * + * @var list|null */ - public string|null $warning_code, + public array|null $device_ids, + ) {} + } + + /** + * New location configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + common_code_key: $json->common_code_key ?? null, + ); + } + + public function __construct( /** - * 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). + * New device IDs where access codes should be created. + * + * @var list|null */ - public array|null $failed_devices = null, + public array|null $device_ids, + /** + * Common code key to ensure PIN code reuse across devices. + */ + public string|null $common_code_key = null, ) {} } } -namespace Seam\Resources\UnmanagedAccessGrant\PendingMutations { +namespace Seam\Resources\UnmanagedAccessGrant\PendingMutations\UpdatingAccessTimes { + /** + * Previous access time configuration. + */ class From { public static function from_json(mixed $json): From|null @@ -324,17 +634,12 @@ public static function from_json(mixed $json): From|null return null; } return new self( - device_ids: $json->device_ids ?? null, ends_at: $json->ends_at ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( - /** - * Previous device IDs where access codes existed. - */ - public array|null $device_ids, /** * Previous end time for access. */ @@ -346,6 +651,9 @@ public function __construct( ) {} } + /** + * New access time configuration. + */ class To { public static function from_json(mixed $json): To|null @@ -354,18 +662,12 @@ public static function from_json(mixed $json): To|null return null; } return new self( - common_code_key: $json->common_code_key ?? null, - device_ids: $json->device_ids ?? null, ends_at: $json->ends_at ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( - /** - * New device IDs where access codes should be created. - */ - public array|null $device_ids, /** * New end time for access. */ @@ -374,15 +676,386 @@ public function __construct( * New start time for access. */ public string|null $starts_at, - /** - * Common code key to ensure PIN code reuse across devices. - */ - public string|null $common_code_key = null, ) {} } } +namespace Seam\Resources\UnmanagedAccessGrant\RequestedAccessMethods { + enum Mode: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } +} + namespace Seam\Resources\UnmanagedAccessGrant\Warnings { + /** + * Indicates that the [access grant](https://docs.seam.co/use-cases/granting-access) is being deleted. + */ + final class BeingDeleted extends + \Seam\Resources\UnmanagedAccessGrant\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the access grant should have access to more locations than it currently does. Access methods are being created for the missing locations. + */ + final class UnderprovisionedAccess extends + \Seam\Resources\UnmanagedAccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): UnderprovisionedAccess|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the access grant has access to locations it should not have. Access methods are being removed from the extra locations. + */ + final class OverprovisionedAccess extends + \Seam\Resources\UnmanagedAccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): OverprovisionedAccess|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + failed_devices: array_map( + fn( + $f, + ) => \Seam\Resources\UnmanagedAccessGrant\Warnings\OverprovisionedAccess\FailedDevices::from_json( + $f, + ), + $json->failed_devices ?? [], + ), + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * 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). + * + * @var list<\Seam\Resources\UnmanagedAccessGrant\Warnings\OverprovisionedAccess\FailedDevices>|null + */ + public array|null $failed_devices = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the access times for this [access grant](https://docs.seam.co/use-cases/granting-access) are being updated. + */ + final class UpdatingAccessTimes extends + \Seam\Resources\UnmanagedAccessGrant\Warnings + { + public static function from_json(mixed $json): UpdatingAccessTimes|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * IDs of the access methods being updated. + * + * @var list|null + */ + public array|null $access_method_ids, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the requested PIN code was already in use on a device, so a different code was assigned. + */ + final class RequestedCodeUnavailable extends + \Seam\Resources\UnmanagedAccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): RequestedCodeUnavailable|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * ID of the device where the requested code was unavailable. + */ + public string|null $device_id, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * The new PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that was unavailable. + */ + public string|null $original_code, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that a device in the access grant does not support access codes and was excluded from code materialization. + */ + final class DeviceDoesNotSupportAccessCodes extends + \Seam\Resources\UnmanagedAccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): DeviceDoesNotSupportAccessCodes|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * ID of the device that does not support access codes. + */ + public string|null $device_id, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class DeviceTimeConstraintsViolated extends + \Seam\Resources\UnmanagedAccessGrant\Warnings + { + public static function from_json( + mixed $json, + ): DeviceTimeConstraintsViolated|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + message: $json->message ?? null, + reason: $json->reason ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * ID of the device whose time constraints the access grant violates. + */ + public string|null $device_id, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Specific reason why the grant's times are not programmable on the device. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\DeviceTimeConstraintsViolated\Reason>|string|null + */ + public string|null $reason, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessGrant\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case BEING_DELETED = "being_deleted"; + case UNDERPROVISIONED_ACCESS = "underprovisioned_access"; + case OVERPROVISIONED_ACCESS = "overprovisioned_access"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + case REQUESTED_CODE_UNAVAILABLE = "requested_code_unavailable"; + case DEVICE_DOES_NOT_SUPPORT_ACCESS_CODES = "device_does_not_support_access_codes"; + case DEVICE_TIME_CONSTRAINTS_VIOLATED = "device_time_constraints_violated"; + } +} + +namespace Seam\Resources\UnmanagedAccessGrant\Warnings\OverprovisionedAccess { /** * 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). */ @@ -416,3 +1089,12 @@ public function __construct( ) {} } } + +namespace Seam\Resources\UnmanagedAccessGrant\Warnings\DeviceTimeConstraintsViolated { + enum Reason: string + { + case DURATION_EXCEEDS_MAX = "duration_exceeds_max"; + case TIMES_DO_NOT_MATCH_SLOTS = "times_do_not_match_slots"; + case ONGOING_NOT_SUPPORTED = "ongoing_not_supported"; + } +} diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index b59d7248..f24a7e20 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -14,31 +14,41 @@ public static function from_json( } return new self( access_method_id: $json->access_method_id ?? null, - code: $json->code ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, errors: array_map( - fn($e) => UnmanagedAccessMethod\Errors::from_json($e), + fn( + $e, + ) => \Seam\Resources\UnmanagedAccessMethod\Errors::from_json( + $e, + ), $json->errors ?? [], ), - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, is_issued: $json->is_issued ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, issued_at: $json->issued_at ?? null, mode: $json->mode ?? null, pending_mutations: array_map( - fn($p) => UnmanagedAccessMethod\PendingMutations::from_json( + fn( + $p, + ) => \Seam\Resources\UnmanagedAccessMethod\PendingMutations::from_json( $p, ), $json->pending_mutations ?? [], ), warnings: array_map( - fn($w) => UnmanagedAccessMethod\Warnings::from_json($w), + fn( + $w, + ) => \Seam\Resources\UnmanagedAccessMethod\Warnings::from_json( + $w, + ), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, + code: $json->code ?? null, + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, ); } @@ -57,6 +67,8 @@ public function __construct( public string|null $display_name, /** * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * + * @var list<\Seam\Resources\UnmanagedAccessMethod\Errors> */ public array $errors, /** @@ -69,14 +81,20 @@ public function __construct( public string|null $issued_at, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\Mode>|string|null */ public string|null $mode, /** * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + * + * @var list<\Seam\Resources\UnmanagedAccessMethod\PendingMutations> */ public array $pending_mutations, /** * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * + * @var list<\Seam\Resources\UnmanagedAccessMethod\Warnings> */ public array $warnings, /** @@ -109,7 +127,7 @@ public function __construct( namespace Seam\Resources\UnmanagedAccessMethod { /** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -118,11 +136,23 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\UnmanagedAccessMethod\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedAccessMethod\Errors\ErrorCode::FAILED_TO_ISSUE + => \Seam\Resources\UnmanagedAccessMethod\Errors\FailedToIssue::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -132,6 +162,8 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\Errors\ErrorCode>|string|null */ public string|null $error_code, /** @@ -142,11 +174,185 @@ public function __construct( } /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. Known mutation_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class PendingMutations { public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->mutation_code ?? null) + ? \Seam\Resources\UnmanagedAccessMethod\PendingMutations\MutationCode::tryFrom( + $json->mutation_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedAccessMethod\PendingMutations\MutationCode::PROVISIONING_ACCESS + => \Seam\Resources\UnmanagedAccessMethod\PendingMutations\ProvisioningAccess::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessMethod\PendingMutations\MutationCode::REVOKING_ACCESS + => \Seam\Resources\UnmanagedAccessMethod\PendingMutations\RevokingAccess::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessMethod\PendingMutations\MutationCode::UPDATING_ACCESS_TIMES + => \Seam\Resources\UnmanagedAccessMethod\PendingMutations\UpdatingAccessTimes::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + /** + * Detailed description of the mutation. + */ + public string|null $message, + /** + * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\PendingMutations\MutationCode>|string|null + */ + public string|null $mutation_code, + ) {} + } + + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\UnmanagedAccessMethod\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode::UPDATING_ACCESS_TIMES + => \Seam\Resources\UnmanagedAccessMethod\Warnings\UpdatingAccessTimes::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode::PULLED_BACKUP_ACCESS_CODE + => \Seam\Resources\UnmanagedAccessMethod\Warnings\PulledBackupAccessCode::from_json( + $json, + ), + \Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode::DELAY_IN_ISSUING + => \Seam\Resources\UnmanagedAccessMethod\Warnings\DelayInIssuing::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } + + enum Mode: string + { + case CODE = "code"; + case CARD = "card"; + case MOBILE_KEY = "mobile_key"; + case CLOUD_KEY = "cloud_key"; + } +} + +namespace Seam\Resources\UnmanagedAccessMethod\Errors { + /** + * Indicates that Seam was unable to issue this [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant) 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. + */ + final class FailedToIssue extends + \Seam\Resources\UnmanagedAccessMethod\Errors + { + public static function from_json(mixed $json): FailedToIssue|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case FAILED_TO_ISSUE = "failed_to_issue"; + } +} + +namespace Seam\Resources\UnmanagedAccessMethod\PendingMutations { + /** + * Seam is in the process of provisioning access for this access method on new devices. + */ + final class ProvisioningAccess extends + \Seam\Resources\UnmanagedAccessMethod\PendingMutations + { + public static function from_json(mixed $json): ProvisioningAccess|null { if (!$json) { return null; @@ -154,12 +360,16 @@ public static function from_json(mixed $json): PendingMutations|null return new self( created_at: $json->created_at ?? null, from: isset($json->from) - ? PendingMutations\From::from_json($json->from) + ? \Seam\Resources\UnmanagedAccessMethod\PendingMutations\ProvisioningAccess\From::from_json( + $json->from, + ) : null, message: $json->message ?? null, mutation_code: $json->mutation_code ?? null, to: isset($json->to) - ? PendingMutations\To::from_json($json->to) + ? \Seam\Resources\UnmanagedAccessMethod\PendingMutations\ProvisioningAccess\To::from_json( + $json->to, + ) : null, ); } @@ -168,58 +378,262 @@ public function __construct( /** * Date and time at which the mutation was created. */ - public string|null $created_at, - public PendingMutations\From|null $from, + string|null $created_at, + /** + * Previous device configuration. + */ + public \Seam\Resources\UnmanagedAccessMethod\PendingMutations\ProvisioningAccess\From|null $from, /** * Detailed description of the mutation. */ - public string|null $message, - public string|null $mutation_code, - public PendingMutations\To|null $to, - ) {} + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New device configuration. + */ + public \Seam\Resources\UnmanagedAccessMethod\PendingMutations\ProvisioningAccess\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } } /** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + * Seam is in the process of revoking access for this access method from devices. */ - class Warnings + final class RevokingAccess extends + \Seam\Resources\UnmanagedAccessMethod\PendingMutations { - public static function from_json(mixed $json): Warnings|null + public static function from_json(mixed $json): RevokingAccess|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\UnmanagedAccessMethod\PendingMutations\RevokingAccess\From::from_json( + $json->from, + ) + : null, message: $json->message ?? null, - original_access_method_id: $json->original_access_method_id ?? - null, - warning_code: $json->warning_code ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\UnmanagedAccessMethod\PendingMutations\RevokingAccess\To::from_json( + $json->to, + ) + : null, ); } public function __construct( /** - * Date and time at which Seam created the warning. + * Date and time at which the mutation was created. */ - public string|null $created_at, + string|null $created_at, /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Previous device configuration. */ - public string|null $message, + public \Seam\Resources\UnmanagedAccessMethod\PendingMutations\RevokingAccess\From|null $from, /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * Detailed description of the mutation. */ - public string|null $warning_code, + string|null $message, /** - * ID of the original access method from which this backup access method was split, if applicable. + * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\PendingMutations\MutationCode>|string|null */ - public string|null $original_access_method_id = null, + string|null $mutation_code, + /** + * New device configuration. + */ + public \Seam\Resources\UnmanagedAccessMethod\PendingMutations\RevokingAccess\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + /** + * Seam is in the process of updating the access times for this access method. + */ + final class UpdatingAccessTimes extends + \Seam\Resources\UnmanagedAccessMethod\PendingMutations + { + public static function from_json(mixed $json): UpdatingAccessTimes|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? \Seam\Resources\UnmanagedAccessMethod\PendingMutations\UpdatingAccessTimes\From::from_json( + $json->from, + ) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? \Seam\Resources\UnmanagedAccessMethod\PendingMutations\UpdatingAccessTimes\To::from_json( + $json->to, + ) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + string|null $created_at, + /** + * Previous access time configuration. + */ + public \Seam\Resources\UnmanagedAccessMethod\PendingMutations\UpdatingAccessTimes\From|null $from, + /** + * Detailed description of the mutation. + */ + string|null $message, + /** + * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\PendingMutations\MutationCode>|string|null + */ + string|null $mutation_code, + /** + * New access time configuration. + */ + public \Seam\Resources\UnmanagedAccessMethod\PendingMutations\UpdatingAccessTimes\To|null $to, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + mutation_code: $mutation_code, + ); + } + } + + enum MutationCode: string + { + case PROVISIONING_ACCESS = "provisioning_access"; + case REVOKING_ACCESS = "revoking_access"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + } +} + +namespace Seam\Resources\UnmanagedAccessMethod\PendingMutations\ProvisioningAccess { + /** + * Previous device configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( + /** + * Previous device IDs where access was provisioned. + * + * @var list|null + */ + public array|null $device_ids, + ) {} + } + + /** + * New device configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( + /** + * New device IDs where access is being provisioned. + * + * @var list|null + */ + public array|null $device_ids, ) {} } } -namespace Seam\Resources\UnmanagedAccessMethod\PendingMutations { +namespace Seam\Resources\UnmanagedAccessMethod\PendingMutations\RevokingAccess { + /** + * Previous device configuration. + */ + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( + /** + * Previous device IDs where access existed. + * + * @var list|null + */ + public array|null $device_ids, + ) {} + } + + /** + * New device configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self(device_ids: $json->device_ids ?? null); + } + + public function __construct( + /** + * New device IDs where access should remain. + * + * @var list|null + */ + public array|null $device_ids, + ) {} + } +} + +namespace Seam\Resources\UnmanagedAccessMethod\PendingMutations\UpdatingAccessTimes { + /** + * Previous access time configuration. + */ class From { public static function from_json(mixed $json): From|null @@ -228,14 +642,12 @@ public static function from_json(mixed $json): From|null return null; } return new self( - device_ids: $json->device_ids ?? null, ends_at: $json->ends_at ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( - public array|null $device_ids, /** * Previous end time for access. */ @@ -247,6 +659,9 @@ public function __construct( ) {} } + /** + * New access time configuration. + */ class To { public static function from_json(mixed $json): To|null @@ -255,14 +670,12 @@ public static function from_json(mixed $json): To|null return null; } return new self( - device_ids: $json->device_ids ?? null, ends_at: $json->ends_at ?? null, starts_at: $json->starts_at ?? null, ); } public function __construct( - public array|null $device_ids, /** * New end time for access. */ @@ -274,3 +687,188 @@ public function __construct( ) {} } } + +namespace Seam\Resources\UnmanagedAccessMethod\Warnings { + /** + * Indicates that the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant) is being deleted. + */ + final class BeingDeleted extends + \Seam\Resources\UnmanagedAccessMethod\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the access times for this [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant) are being updated. + */ + final class UpdatingAccessTimes extends + \Seam\Resources\UnmanagedAccessMethod\Warnings + { + public static function from_json(mixed $json): UpdatingAccessTimes|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class PulledBackupAccessCode extends + \Seam\Resources\UnmanagedAccessMethod\Warnings + { + public static function from_json( + mixed $json, + ): PulledBackupAccessCode|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id = null, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that Seam has not yet issued this [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant), 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. + */ + final class DelayInIssuing extends + \Seam\Resources\UnmanagedAccessMethod\Warnings + { + public static function from_json(mixed $json): DelayInIssuing|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessMethod\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case BEING_DELETED = "being_deleted"; + case UPDATING_ACCESS_TIMES = "updating_access_times"; + case PULLED_BACKUP_ACCESS_CODE = "pulled_backup_access_code"; + case DELAY_IN_ISSUING = "delay_in_issuing"; + } +} diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 55727698..615542e4 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -12,6 +12,33 @@ public static function from_json(mixed $json): UnmanagedDevice|null return null; } return new self( + capabilities_supported: $json->capabilities_supported ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + device_id: $json->device_id ?? null, + device_type: $json->device_type ?? null, + errors: array_map( + fn($e) => \Seam\Resources\UnmanagedDevice\Errors::from_json( + $e, + ), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + properties: isset($json->properties) + ? \Seam\Resources\UnmanagedDevice\Properties::from_json( + $json->properties, + ) + : null, + warnings: array_map( + fn( + $w, + ) => \Seam\Resources\UnmanagedDevice\Warnings::from_json( + $w, + ), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, can_configure_auto_lock: $json->can_configure_auto_lock ?? null, can_hvac_cool: $json->can_hvac_cool ?? null, can_hvac_heat: $json->can_hvac_heat ?? null, @@ -42,34 +69,19 @@ public static function from_json(mixed $json): UnmanagedDevice|null can_simulate_removal: $json->can_simulate_removal ?? null, can_turn_off_hvac: $json->can_turn_off_hvac ?? null, can_unlock_with_code: $json->can_unlock_with_code ?? null, - capabilities_supported: $json->capabilities_supported ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - device_id: $json->device_id ?? null, - device_type: $json->device_type ?? null, - errors: array_map( - fn($e) => UnmanagedDevice\Errors::from_json($e), - $json->errors ?? [], - ), - is_managed: $json->is_managed ?? null, location: isset($json->location) - ? UnmanagedDevice\Location::from_json($json->location) - : null, - properties: isset($json->properties) - ? UnmanagedDevice\Properties::from_json($json->properties) + ? \Seam\Resources\UnmanagedDevice\Location::from_json( + $json->location, + ) : null, - warnings: array_map( - fn($w) => UnmanagedDevice\Warnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, ); } public function __construct( /** * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). + * + * @var list|null */ public array|null $capabilities_supported, /** @@ -92,10 +104,14 @@ public function __construct( public string|null $device_id, /** * Type of the device. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\DeviceType>|string|null */ public string|null $device_type, /** * 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. + * + * @var list<\Seam\Resources\UnmanagedDevice\Errors> */ public array $errors, /** @@ -105,9 +121,11 @@ public function __construct( /** * properties of the device. */ - public UnmanagedDevice\Properties|null $properties, + public \Seam\Resources\UnmanagedDevice\Properties|null $properties, /** * 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. + * + * @var list<\Seam\Resources\UnmanagedDevice\Warnings> */ public array $warnings, /** @@ -197,14 +215,14 @@ public function __construct( /** * Location information for the device. */ - public UnmanagedDevice\Location|null $location = null, + public \Seam\Resources\UnmanagedDevice\Location|null $location = null, ) {} } } namespace Seam\Resources\UnmanagedDevice { /** - * 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. + * 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. Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -213,15 +231,75 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::ACCOUNT_DISCONNECTED + => \Seam\Resources\UnmanagedDevice\Errors\AccountDisconnected::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED + => \Seam\Resources\UnmanagedDevice\Errors\SaltoKsSubscriptionLimitExceeded::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::INSUFFICIENT_PERMISSIONS + => \Seam\Resources\UnmanagedDevice\Errors\InsufficientPermissions::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::DORMAKABA_SITES_DISCONNECTED + => \Seam\Resources\UnmanagedDevice\Errors\DormakabaSitesDisconnected::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::DEVICE_OFFLINE + => \Seam\Resources\UnmanagedDevice\Errors\DeviceOffline::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::DEVICE_REMOVED + => \Seam\Resources\UnmanagedDevice\Errors\DeviceRemoved::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::HUB_DISCONNECTED + => \Seam\Resources\UnmanagedDevice\Errors\HubDisconnected::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::DEVICE_DISCONNECTED + => \Seam\Resources\UnmanagedDevice\Errors\DeviceDisconnected::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::EMPTY_BACKUP_ACCESS_CODE_POOL + => \Seam\Resources\UnmanagedDevice\Errors\EmptyBackupAccessCodePool::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::AUGUST_LOCK_NOT_AUTHORIZED + => \Seam\Resources\UnmanagedDevice\Errors\AugustLockNotAuthorized::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::MISSING_DEVICE_CREDENTIALS + => \Seam\Resources\UnmanagedDevice\Errors\MissingDeviceCredentials::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::AUXILIARY_HEAT_RUNNING + => \Seam\Resources\UnmanagedDevice\Errors\AuxiliaryHeatRunning::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::SUBSCRIPTION_REQUIRED + => \Seam\Resources\UnmanagedDevice\Errors\SubscriptionRequired::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Errors\ErrorCode::BRIDGE_DISCONNECTED + => \Seam\Resources\UnmanagedDevice\Errors\BridgeDisconnected::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -231,18 +309,14 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null */ public string|null $error_code, - public bool|null $is_connected_account_error, - public bool|null $is_device_error, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error = null, ) {} } @@ -297,25 +371,29 @@ public static function from_json(mixed $json): Properties|null return null; } return new self( + model: isset($json->model) + ? \Seam\Resources\UnmanagedDevice\Properties\Model::from_json( + $json->model, + ) + : null, + name: $json->name ?? null, + online: $json->online ?? null, accessory_keypad: isset($json->accessory_keypad) - ? Properties\AccessoryKeypad::from_json( + ? \Seam\Resources\UnmanagedDevice\Properties\AccessoryKeypad::from_json( $json->accessory_keypad, ) : null, battery: isset($json->battery) - ? Properties\Battery::from_json($json->battery) + ? \Seam\Resources\UnmanagedDevice\Properties\Battery::from_json( + $json->battery, + ) : null, battery_level: $json->battery_level ?? null, image_alt_text: $json->image_alt_text ?? null, image_url: $json->image_url ?? null, manufacturer: $json->manufacturer ?? null, - model: isset($json->model) - ? Properties\Model::from_json($json->model) - : null, - name: $json->name ?? null, offline_access_codes_enabled: $json->offline_access_codes_enabled ?? null, - online: $json->online ?? null, online_access_codes_enabled: $json->online_access_codes_enabled ?? null, ); @@ -325,7 +403,7 @@ public function __construct( /** * Device model-related properties. */ - public Properties\Model|null $model, + public \Seam\Resources\UnmanagedDevice\Properties\Model|null $model, /** * Name of the device. * @@ -339,11 +417,11 @@ public function __construct( /** * Accessory keypad properties and state. */ - public Properties\AccessoryKeypad|null $accessory_keypad = null, + public \Seam\Resources\UnmanagedDevice\Properties\AccessoryKeypad|null $accessory_keypad = null, /** * Represents the current status of the battery charge level. */ - public Properties\Battery|null $battery = null, + public \Seam\Resources\UnmanagedDevice\Properties\Battery|null $battery = null, /** * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. */ @@ -376,7 +454,7 @@ public function __construct( } /** - * 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. + * 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. Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { @@ -385,174 +463,2247 @@ public static function from_json(mixed $json): Warnings|null if (!$json) { return null; } - return new self( - active_access_code_count: $json->active_access_code_count ?? - null, - created_at: $json->created_at ?? null, - max_active_access_code_count: $json->max_active_access_code_count ?? - null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::PARTIAL_BACKUP_ACCESS_CODE_POOL + => \Seam\Resources\UnmanagedDevice\Warnings\PartialBackupAccessCodePool::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::MANY_ACTIVE_BACKUP_CODES + => \Seam\Resources\UnmanagedDevice\Warnings\ManyActiveBackupCodes::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::THIRD_PARTY_INTEGRATION_DETECTED + => \Seam\Resources\UnmanagedDevice\Warnings\ThirdPartyIntegrationDetected::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::TTLOCK_LOCK_GATEWAY_UNLOCKING_NOT_ENABLED + => \Seam\Resources\UnmanagedDevice\Warnings\TtlockLockGatewayUnlockingNotEnabled::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::TTLOCK_WEAK_GATEWAY_SIGNAL + => \Seam\Resources\UnmanagedDevice\Warnings\TtlockWeakGatewaySignal::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::POWER_SAVING_MODE + => \Seam\Resources\UnmanagedDevice\Warnings\PowerSavingMode::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::TEMPERATURE_THRESHOLD_EXCEEDED + => \Seam\Resources\UnmanagedDevice\Warnings\TemperatureThresholdExceeded::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::DEVICE_COMMUNICATION_DEGRADED + => \Seam\Resources\UnmanagedDevice\Warnings\DeviceCommunicationDegraded::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::SCHEDULED_MAINTENANCE_WINDOW + => \Seam\Resources\UnmanagedDevice\Warnings\ScheduledMaintenanceWindow::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::DEVICE_HAS_FLAKY_CONNECTION + => \Seam\Resources\UnmanagedDevice\Warnings\DeviceHasFlakyConnection::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::SALTO_KS_OFFICE_MODE + => \Seam\Resources\UnmanagedDevice\Warnings\SaltoKsOfficeMode::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::SALTO_KS_PRIVACY_MODE + => \Seam\Resources\UnmanagedDevice\Warnings\SaltoKsPrivacyMode::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::PRIVACY_MODE + => \Seam\Resources\UnmanagedDevice\Warnings\PrivacyMode::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::SALTO_KS_SUBSCRIPTION_LIMIT_ALMOST_REACHED + => \Seam\Resources\UnmanagedDevice\Warnings\SaltoKsSubscriptionLimitAlmostReached::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::SALTO_KS_LOCK_ACCESS_CODE_SUPPORT_REMOVED + => \Seam\Resources\UnmanagedDevice\Warnings\SaltoKsLockAccessCodeSupportRemoved::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::UNKNOWN_ISSUE_WITH_PHONE + => \Seam\Resources\UnmanagedDevice\Warnings\UnknownIssueWithPhone::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::LOCKLY_TIME_ZONE_NOT_CONFIGURED + => \Seam\Resources\UnmanagedDevice\Warnings\LocklyTimeZoneNotConfigured::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::ULTRALOQ_TIME_ZONE_UNKNOWN + => \Seam\Resources\UnmanagedDevice\Warnings\UltraloqTimeZoneUnknown::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::TIME_ZONE_UNKNOWN + => \Seam\Resources\UnmanagedDevice\Warnings\TimeZoneUnknown::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::TIME_ZONE_MISMATCH + => \Seam\Resources\UnmanagedDevice\Warnings\TimeZoneMismatch::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::TWO_N_DEVICE_MISSING_TIMEZONE + => \Seam\Resources\UnmanagedDevice\Warnings\TwoNDeviceMissingTimezone::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::HUB_REQUIRED_FOR_ADDITIONAL_CAPABILITIES + => \Seam\Resources\UnmanagedDevice\Warnings\HubRequiredForAdditionalCapabilities::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::PROVIDER_ISSUE + => \Seam\Resources\UnmanagedDevice\Warnings\ProviderIssue::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::KEYNEST_UNSUPPORTED_LOCKER + => \Seam\Resources\UnmanagedDevice\Warnings\KeynestUnsupportedLocker::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::ACCESSORY_KEYPAD_SETUP_REQUIRED + => \Seam\Resources\UnmanagedDevice\Warnings\AccessoryKeypadSetupRequired::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::UNRELIABLE_ONLINE_STATUS + => \Seam\Resources\UnmanagedDevice\Warnings\UnreliableOnlineStatus::from_json( + $json, + ), + \Seam\Resources\UnmanagedDevice\Warnings\WarningCode::MAX_ACCESS_CODES_REACHED + => \Seam\Resources\UnmanagedDevice\Warnings\MaxAccessCodesReached::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; } public function __construct( - /** - * Number of active access codes on the device when the warning was set. - */ - public int|null $active_access_code_count, /** * Date and time at which Seam created the warning. */ public string|null $created_at, - /** - * Maximum number of active access codes supported by the device. - */ - public int|null $max_active_access_code_count, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null */ public string|null $warning_code, ) {} } + + enum DeviceType: string + { + case AKUVOX_LOCK = "akuvox_lock"; + case AUGUST_LOCK = "august_lock"; + case BRIVO_ACCESS_POINT = "brivo_access_point"; + case BUTTERFLYMX_PANEL = "butterflymx_panel"; + case AVIGILON_ALTA_ENTRY = "avigilon_alta_entry"; + case DOORKING_LOCK = "doorking_lock"; + case GENIE_DOOR = "genie_door"; + case IGLOO_LOCK = "igloo_lock"; + case LINEAR_LOCK = "linear_lock"; + case LOCKLY_LOCK = "lockly_lock"; + case KWIKSET_LOCK = "kwikset_lock"; + case NUKI_LOCK = "nuki_lock"; + case SALTO_LOCK = "salto_lock"; + case SCHLAGE_LOCK = "schlage_lock"; + case SMARTTHINGS_LOCK = "smartthings_lock"; + case WYZE_LOCK = "wyze_lock"; + case YALE_LOCK = "yale_lock"; + case TWO_N_INTERCOM = "two_n_intercom"; + case CONTROLBYWEB_DEVICE = "controlbyweb_device"; + case TTLOCK_LOCK = "ttlock_lock"; + case IGLOOHOME_LOCK = "igloohome_lock"; + case FOUR_SUITES_DOOR = "four_suites_door"; + case DORMAKABA_ORACODE_DOOR = "dormakaba_oracode_door"; + case TEDEE_LOCK = "tedee_lock"; + case AKILES_LOCK = "akiles_lock"; + case ULTRALOQ_LOCK = "ultraloq_lock"; + case YACAN_LOCK = "yacan_lock"; + case KEYINCODE_LOCK = "keyincode_lock"; + case OMNITEC_LOCK = "omnitec_lock"; + case KISI_LOCK = "kisi_lock"; + case AQARA_LOCK = "aqara_lock"; + case KEYNEST_KEY = "keynest_key"; + case NOISEAWARE_ACTIVITY_ZONE = "noiseaware_activity_zone"; + case MINUT_SENSOR = "minut_sensor"; + case ECOBEE_THERMOSTAT = "ecobee_thermostat"; + case NEST_THERMOSTAT = "nest_thermostat"; + case HONEYWELL_RESIDEO_THERMOSTAT = "honeywell_resideo_thermostat"; + case TADO_THERMOSTAT = "tado_thermostat"; + case SENSI_THERMOSTAT = "sensi_thermostat"; + case SMARTTHINGS_THERMOSTAT = "smartthings_thermostat"; + case IOS_PHONE = "ios_phone"; + case ANDROID_PHONE = "android_phone"; + case RING_CAMERA = "ring_camera"; + } } -namespace Seam\Resources\UnmanagedDevice\Properties { +namespace Seam\Resources\UnmanagedDevice\Errors { /** - * Accessory keypad properties and state. + * Indicates that the account is disconnected. */ - class AccessoryKeypad + final class AccountDisconnected extends + \Seam\Resources\UnmanagedDevice\Errors { - public static function from_json(mixed $json): AccessoryKeypad|null + public static function from_json(mixed $json): AccountDisconnected|null { if (!$json) { return null; } return new self( - battery: isset($json->battery) - ? AccessoryKeypad\Battery::from_json($json->battery) - : null, - is_connected: $json->is_connected ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Indicates if an accessory keypad is connected to the device. + * Date and time at which Seam created the error. */ - public bool|null $is_connected, + string|null $created_at, /** - * Keypad battery properties. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null */ - public AccessoryKeypad\Battery|null $battery = null, - ) {} + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Represents the current status of the battery charge level. + * Indicates that the Salto site user limit has been reached. */ - class Battery + final class SaltoKsSubscriptionLimitExceeded extends + \Seam\Resources\UnmanagedDevice\Errors { - public static function from_json(mixed $json): Battery|null - { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitExceeded|null { if (!$json) { return null; } return new self( - level: $json->level ?? null, - status: $json->status ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Battery charge level as a value between 0 and 1, inclusive. + * Date and time at which Seam created the error. */ - public float|null $level, + string|null $created_at, /** - * 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. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null */ - public string|null $status, - ) {} + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } /** - * Device model-related properties. + * 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. */ - class Model + final class InsufficientPermissions extends + \Seam\Resources\UnmanagedDevice\Errors { - public static function from_json(mixed $json): Model|null - { + public static function from_json( + mixed $json, + ): InsufficientPermissions|null { if (!$json) { return null; } return new self( - accessory_keypad_supported: $json->accessory_keypad_supported ?? - null, - can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? - null, - display_name: $json->display_name ?? null, - has_built_in_keypad: $json->has_built_in_keypad ?? null, - manufacturer_display_name: $json->manufacturer_display_name ?? - null, - offline_access_codes_supported: $json->offline_access_codes_supported ?? - null, - online_access_codes_supported: $json->online_access_codes_supported ?? + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Display name of the device model. - */ - public string|null $display_name, - /** - * Display name that corresponds to the manufacturer-specific terminology for the device. - */ - public string|null $manufacturer_display_name, - /** - * @deprecated use device.properties.model.can_connect_accessory_keypad + * Date and time at which Seam created the error. */ - public bool|null $accessory_keypad_supported = null, + string|null $created_at, /** - * Indicates whether the device can connect a accessory keypad. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null */ - public bool|null $can_connect_accessory_keypad = null, + string|null $error_code, /** - * Indicates whether the device has a built in accessory keypad. + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. */ - public bool|null $has_built_in_keypad = null, + public true|null $is_connected_account_error, /** - * @deprecated use device.can_program_offline_access_codes. + * Indicates that the error is not a device error. */ - public bool|null $offline_access_codes_supported = null, + public false|null $is_device_error, /** - * @deprecated use device.can_program_online_access_codes. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public bool|null $online_access_codes_supported = null, - ) {} + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } } -} -namespace Seam\Resources\UnmanagedDevice\Properties\AccessoryKeypad { /** - * Keypad battery properties. + * Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. */ - class Battery + final class DormakabaSitesDisconnected extends + \Seam\Resources\UnmanagedDevice\Errors { - public static function from_json(mixed $json): Battery|null - { + public static function from_json( + mixed $json, + ): DormakabaSitesDisconnected|null { if (!$json) { return null; } - return new self(level: $json->level ?? null); + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); } - public function __construct(public float|null $level) {} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. + */ + public true|null $is_connected_account_error, + /** + * Indicates that the error is not a device error. + */ + public false|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the device is offline. + */ + final class DeviceOffline extends \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json(mixed $json): DeviceOffline|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the device has been removed. + */ + final class DeviceRemoved extends \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json(mixed $json): DeviceRemoved|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the hub is disconnected. + */ + final class HubDisconnected extends \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json(mixed $json): HubDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the device is disconnected. + */ + final class DeviceDisconnected extends + \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json(mixed $json): DeviceDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty. + */ + final class EmptyBackupAccessCodePool extends + \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json( + mixed $json, + ): EmptyBackupAccessCodePool|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the user is not authorized to use the August lock. + */ + final class AugustLockNotAuthorized extends + \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json( + mixed $json, + ): AugustLockNotAuthorized|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that device credentials are missing. + */ + final class MissingDeviceCredentials extends + \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json( + mixed $json, + ): MissingDeviceCredentials|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the auxiliary heat is running. + */ + final class AuxiliaryHeatRunning extends + \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json(mixed $json): AuxiliaryHeatRunning|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that a subscription is required to connect. + */ + final class SubscriptionRequired extends + \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json(mixed $json): SubscriptionRequired|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that the error is a device error. + */ + public true|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + /** + * Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/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](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system-errors-seam_bridge_disconnected). + */ + final class BridgeDisconnected extends + \Seam\Resources\UnmanagedDevice\Errors + { + public static function from_json(mixed $json): BridgeDisconnected|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error = null, + /** + * Indicates whether the error is related specifically to the connected account. + */ + public bool|null $is_connected_account_error = null, + ) { + parent::__construct( + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case ACCOUNT_DISCONNECTED = "account_disconnected"; + case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; + case INSUFFICIENT_PERMISSIONS = "insufficient_permissions"; + case DORMAKABA_SITES_DISCONNECTED = "dormakaba_sites_disconnected"; + case DEVICE_OFFLINE = "device_offline"; + case DEVICE_REMOVED = "device_removed"; + case HUB_DISCONNECTED = "hub_disconnected"; + case DEVICE_DISCONNECTED = "device_disconnected"; + case EMPTY_BACKUP_ACCESS_CODE_POOL = "empty_backup_access_code_pool"; + case AUGUST_LOCK_NOT_AUTHORIZED = "august_lock_not_authorized"; + case MISSING_DEVICE_CREDENTIALS = "missing_device_credentials"; + case AUXILIARY_HEAT_RUNNING = "auxiliary_heat_running"; + case SUBSCRIPTION_REQUIRED = "subscription_required"; + case BRIDGE_DISCONNECTED = "bridge_disconnected"; + } +} + +namespace Seam\Resources\UnmanagedDevice\Properties { + /** + * Accessory keypad properties and state. + */ + class AccessoryKeypad + { + public static function from_json(mixed $json): AccessoryKeypad|null + { + if (!$json) { + return null; + } + return new self( + is_connected: $json->is_connected ?? null, + battery: isset($json->battery) + ? \Seam\Resources\UnmanagedDevice\Properties\AccessoryKeypad\Battery::from_json( + $json->battery, + ) + : null, + ); + } + + public function __construct( + /** + * Indicates if an accessory keypad is connected to the device. + */ + public bool|null $is_connected, + /** + * Keypad battery properties. + */ + public \Seam\Resources\UnmanagedDevice\Properties\AccessoryKeypad\Battery|null $battery = null, + ) {} + } + + /** + * Represents the current status of the battery charge level. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self( + level: $json->level ?? null, + status: $json->status ?? null, + ); + } + + public function __construct( + /** + * Battery charge level as a value between 0 and 1, inclusive. + */ + public float|null $level, + /** + * 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. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Properties\Battery\Status>|string|null + */ + public string|null $status, + ) {} + } + + /** + * Device model-related properties. + */ + class Model + { + public static function from_json(mixed $json): Model|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + manufacturer_display_name: $json->manufacturer_display_name ?? + null, + accessory_keypad_supported: $json->accessory_keypad_supported ?? + null, + can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? + null, + has_built_in_keypad: $json->has_built_in_keypad ?? null, + offline_access_codes_supported: $json->offline_access_codes_supported ?? + null, + online_access_codes_supported: $json->online_access_codes_supported ?? + null, + ); + } + + public function __construct( + /** + * Display name of the device model. + */ + public string|null $display_name, + /** + * Display name that corresponds to the manufacturer-specific terminology for the device. + */ + public string|null $manufacturer_display_name, + /** + * @deprecated use device.properties.model.can_connect_accessory_keypad + */ + public bool|null $accessory_keypad_supported = null, + /** + * Indicates whether the device can connect a accessory keypad. + */ + public bool|null $can_connect_accessory_keypad = null, + /** + * Indicates whether the device has a built in accessory keypad. + */ + public bool|null $has_built_in_keypad = null, + /** + * @deprecated use device.can_program_offline_access_codes. + */ + public bool|null $offline_access_codes_supported = null, + /** + * @deprecated use device.can_program_online_access_codes. + */ + public bool|null $online_access_codes_supported = null, + ) {} + } +} + +namespace Seam\Resources\UnmanagedDevice\Properties\AccessoryKeypad { + /** + * Keypad battery properties. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); + } + + public function __construct(public float|null $level) {} + } +} + +namespace Seam\Resources\UnmanagedDevice\Properties\Battery { + enum Status: string + { + case CRITICAL = "critical"; + case LOW = "low"; + case GOOD = "good"; + case FULL = "full"; + } +} + +namespace Seam\Resources\UnmanagedDevice\Warnings { + /** + * Indicates that the backup access code is unhealthy. + */ + final class PartialBackupAccessCodePool extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): PartialBackupAccessCodePool|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that there are too many backup codes. + */ + final class ManyActiveBackupCodes extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): ManyActiveBackupCodes|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that a third-party integration has been detected. + */ + final class ThirdPartyIntegrationDetected extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): ThirdPartyIntegrationDetected|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the Remote Unlock feature is not enabled in the settings." + */ + final class TtlockLockGatewayUnlockingNotEnabled extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): TtlockLockGatewayUnlockingNotEnabled|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the gateway signal is weak. + */ + final class TtlockWeakGatewaySignal extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): TtlockWeakGatewaySignal|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the device is in power saving mode and may have limited functionality. + */ + final class PowerSavingMode extends \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json(mixed $json): PowerSavingMode|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the temperature threshold has been exceeded. + */ + final class TemperatureThresholdExceeded extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): TemperatureThresholdExceeded|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the device appears to be unresponsive. + */ + final class DeviceCommunicationDegraded extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): DeviceCommunicationDegraded|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that a scheduled maintenance window has been detected. + */ + final class ScheduledMaintenanceWindow extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): ScheduledMaintenanceWindow|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the device has a flaky connection. + */ + final class DeviceHasFlakyConnection extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): DeviceHasFlakyConnection|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the Salto KS lock is in Office Mode. Access Codes will not unlock doors. + */ + final class SaltoKsOfficeMode extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json(mixed $json): SaltoKsOfficeMode|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the Salto KS lock is in Privacy Mode. Access Codes will not unlock doors. + */ + final class SaltoKsPrivacyMode extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json(mixed $json): SaltoKsPrivacyMode|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the lock is in Privacy Mode. Access codes and remote unlock are blocked until Privacy Mode is disabled. + */ + final class PrivacyMode extends \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json(mixed $json): PrivacyMode|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class SaltoKsSubscriptionLimitAlmostReached extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): SaltoKsSubscriptionLimitAlmostReached|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class SaltoKsLockAccessCodeSupportRemoved extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): SaltoKsLockAccessCodeSupportRemoved|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class UnknownIssueWithPhone extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): UnknownIssueWithPhone|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that Seam detected that the Lockly device does not have a time zone configured. Time-bound codes may not work as expected. + */ + final class LocklyTimeZoneNotConfigured extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): LocklyTimeZoneNotConfigured|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that Seam does not know the time zone of the Ultraloq device. Set a time zone to enable time-bound access codes. + */ + final class UltraloqTimeZoneUnknown extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): UltraloqTimeZoneUnknown|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that Seam does not know the device's time zone. Set a time zone to enable time-bound access codes. + */ + final class TimeZoneUnknown extends \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json(mixed $json): TimeZoneUnknown|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class TimeZoneMismatch extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json(mixed $json): TimeZoneMismatch|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the 2N device does not have a time zone configured. Configure a time zone on the device to enable access codes. + */ + final class TwoNDeviceMissingTimezone extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): TwoNDeviceMissingTimezone|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that a hub or relay must be connected to unlock additional capabilities such as remote unlock. + */ + final class HubRequiredForAdditionalCapabilities extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): HubRequiredForAdditionalCapabilities|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates a provider-specific issue that may affect device functionality. + */ + final class ProviderIssue extends \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json(mixed $json): ProviderIssue|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the key is in a locker that does not support the access codes API. + */ + final class KeynestUnsupportedLocker extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): KeynestUnsupportedLocker|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * 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. + */ + final class AccessoryKeypadSetupRequired extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): AccessoryKeypadSetupRequired|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the device may optimistically be reported as online because the provider does not reliably report its online status. + */ + final class UnreliableOnlineStatus extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): UnreliableOnlineStatus|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the device has reached its maximum number of active access codes. Delete existing codes before creating new ones. + */ + final class MaxAccessCodesReached extends + \Seam\Resources\UnmanagedDevice\Warnings + { + public static function from_json( + mixed $json, + ): MaxAccessCodesReached|null { + if (!$json) { + return null; + } + return new self( + active_access_code_count: $json->active_access_code_count ?? + null, + created_at: $json->created_at ?? null, + max_active_access_code_count: $json->max_active_access_code_count ?? + null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Number of active access codes on the device when the warning was set. + */ + public int|null $active_access_code_count, + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Maximum number of active access codes supported by the device. + */ + public int|null $max_active_access_code_count, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedDevice\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case PARTIAL_BACKUP_ACCESS_CODE_POOL = "partial_backup_access_code_pool"; + case MANY_ACTIVE_BACKUP_CODES = "many_active_backup_codes"; + case THIRD_PARTY_INTEGRATION_DETECTED = "third_party_integration_detected"; + case TTLOCK_LOCK_GATEWAY_UNLOCKING_NOT_ENABLED = "ttlock_lock_gateway_unlocking_not_enabled"; + case TTLOCK_WEAK_GATEWAY_SIGNAL = "ttlock_weak_gateway_signal"; + case POWER_SAVING_MODE = "power_saving_mode"; + case TEMPERATURE_THRESHOLD_EXCEEDED = "temperature_threshold_exceeded"; + case DEVICE_COMMUNICATION_DEGRADED = "device_communication_degraded"; + case SCHEDULED_MAINTENANCE_WINDOW = "scheduled_maintenance_window"; + case DEVICE_HAS_FLAKY_CONNECTION = "device_has_flaky_connection"; + case SALTO_KS_OFFICE_MODE = "salto_ks_office_mode"; + case SALTO_KS_PRIVACY_MODE = "salto_ks_privacy_mode"; + case PRIVACY_MODE = "privacy_mode"; + case SALTO_KS_SUBSCRIPTION_LIMIT_ALMOST_REACHED = "salto_ks_subscription_limit_almost_reached"; + case SALTO_KS_LOCK_ACCESS_CODE_SUPPORT_REMOVED = "salto_ks_lock_access_code_support_removed"; + case UNKNOWN_ISSUE_WITH_PHONE = "unknown_issue_with_phone"; + case LOCKLY_TIME_ZONE_NOT_CONFIGURED = "lockly_time_zone_not_configured"; + case ULTRALOQ_TIME_ZONE_UNKNOWN = "ultraloq_time_zone_unknown"; + case TIME_ZONE_UNKNOWN = "time_zone_unknown"; + case TIME_ZONE_MISMATCH = "time_zone_mismatch"; + case TWO_N_DEVICE_MISSING_TIMEZONE = "two_n_device_missing_timezone"; + case HUB_REQUIRED_FOR_ADDITIONAL_CAPABILITIES = "hub_required_for_additional_capabilities"; + case PROVIDER_ISSUE = "provider_issue"; + case KEYNEST_UNSUPPORTED_LOCKER = "keynest_unsupported_locker"; + case ACCESSORY_KEYPAD_SETUP_REQUIRED = "accessory_keypad_setup_required"; + case UNRELIABLE_ONLINE_STATUS = "unreliable_online_status"; + case MAX_ACCESS_CODES_REACHED = "max_access_codes_reached"; } } diff --git a/src/Resources/UnmanagedUserIdentity.php b/src/Resources/UnmanagedUserIdentity.php index a1da9a34..c7b8cfeb 100644 --- a/src/Resources/UnmanagedUserIdentity.php +++ b/src/Resources/UnmanagedUserIdentity.php @@ -18,14 +18,22 @@ public static function from_json( display_name: $json->display_name ?? null, email_address: $json->email_address ?? null, errors: array_map( - fn($e) => UnmanagedUserIdentity\Errors::from_json($e), + fn( + $e, + ) => \Seam\Resources\UnmanagedUserIdentity\Errors::from_json( + $e, + ), $json->errors ?? [], ), full_name: $json->full_name ?? null, phone_number: $json->phone_number ?? null, user_identity_id: $json->user_identity_id ?? null, warnings: array_map( - fn($w) => UnmanagedUserIdentity\Warnings::from_json($w), + fn( + $w, + ) => \Seam\Resources\UnmanagedUserIdentity\Warnings::from_json( + $w, + ), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, @@ -35,6 +43,8 @@ public static function from_json( public function __construct( /** * Array of access system user IDs associated with the user identity. + * + * @var list|null */ public array|null $acs_user_ids, /** @@ -51,6 +61,8 @@ public function __construct( public string|null $email_address, /** * 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. + * + * @var list<\Seam\Resources\UnmanagedUserIdentity\Errors> */ public array $errors, /** @@ -67,6 +79,8 @@ public function __construct( public string|null $user_identity_id, /** * 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. + * + * @var list<\Seam\Resources\UnmanagedUserIdentity\Warnings> */ public array $warnings, /** @@ -79,7 +93,7 @@ public function __construct( namespace Seam\Resources\UnmanagedUserIdentity { /** - * 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. + * 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. Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -88,13 +102,25 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\UnmanagedUserIdentity\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedUserIdentity\Errors\ErrorCode::ISSUE_WITH_ACS_USER + => \Seam\Resources\UnmanagedUserIdentity\Errors\IssueWithAcsUser::from_json( + $json, + ), + default => new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -112,6 +138,8 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedUserIdentity\Errors\ErrorCode>|string|null */ public string|null $error_code, /** @@ -122,11 +150,126 @@ public function __construct( } /** - * 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. + * 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. Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\UnmanagedUserIdentity\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UnmanagedUserIdentity\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\UnmanagedUserIdentity\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\UnmanagedUserIdentity\Warnings\WarningCode::ACS_USER_PROFILE_DOES_NOT_MATCH_USER_IDENTITY + => \Seam\Resources\UnmanagedUserIdentity\Warnings\AcsUserProfileDoesNotMatchUserIdentity::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedUserIdentity\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\UnmanagedUserIdentity\Errors { + /** + * Indicates that there is an issue with an access system user associated with this user identity. + */ + final class IssueWithAcsUser extends + \Seam\Resources\UnmanagedUserIdentity\Errors + { + public static function from_json(mixed $json): IssueWithAcsUser|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * ID of the access system that the user identity is associated with. + */ + string|null $acs_system_id, + /** + * ID of the access system user that has an issue. + */ + string|null $acs_user_id, + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedUserIdentity\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + acs_system_id: $acs_system_id, + acs_user_id: $acs_user_id, + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case ISSUE_WITH_ACS_USER = "issue_with_acs_user"; + } +} + +namespace Seam\Resources\UnmanagedUserIdentity\Warnings { + /** + * Indicates that the user identity is currently being deleted. + */ + final class BeingDeleted extends + \Seam\Resources\UnmanagedUserIdentity\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null { if (!$json) { return null; @@ -142,15 +285,72 @@ public function __construct( /** * Date and time at which Seam created the warning. */ - public string|null $created_at, + string|null $created_at, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $message, + string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedUserIdentity\Warnings\WarningCode>|string|null */ - public string|null $warning_code, - ) {} + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the ACS user's profile does not match the user identity's profile + */ + final class AcsUserProfileDoesNotMatchUserIdentity extends + \Seam\Resources\UnmanagedUserIdentity\Warnings + { + public static function from_json( + mixed $json, + ): AcsUserProfileDoesNotMatchUserIdentity|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedUserIdentity\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case BEING_DELETED = "being_deleted"; + case ACS_USER_PROFILE_DOES_NOT_MATCH_USER_IDENTITY = "acs_user_profile_does_not_match_user_identity"; } } diff --git a/src/Resources/UserIdentity.php b/src/Resources/UserIdentity.php index ededd870..3153515c 100644 --- a/src/Resources/UserIdentity.php +++ b/src/Resources/UserIdentity.php @@ -17,7 +17,9 @@ public static function from_json(mixed $json): UserIdentity|null display_name: $json->display_name ?? null, email_address: $json->email_address ?? null, errors: array_map( - fn($e) => UserIdentity\Errors::from_json($e), + fn($e) => \Seam\Resources\UserIdentity\Errors::from_json( + $e, + ), $json->errors ?? [], ), full_name: $json->full_name ?? null, @@ -25,7 +27,9 @@ public static function from_json(mixed $json): UserIdentity|null user_identity_id: $json->user_identity_id ?? null, user_identity_key: $json->user_identity_key ?? null, warnings: array_map( - fn($w) => UserIdentity\Warnings::from_json($w), + fn($w) => \Seam\Resources\UserIdentity\Warnings::from_json( + $w, + ), $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, @@ -35,6 +39,8 @@ public static function from_json(mixed $json): UserIdentity|null public function __construct( /** * Array of access system user IDs associated with the user identity. + * + * @var list|null */ public array|null $acs_user_ids, /** @@ -51,6 +57,8 @@ public function __construct( public string|null $email_address, /** * 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. + * + * @var list<\Seam\Resources\UserIdentity\Errors> */ public array $errors, /** @@ -71,6 +79,8 @@ public function __construct( public string|null $user_identity_key, /** * 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. + * + * @var list<\Seam\Resources\UserIdentity\Warnings> */ public array $warnings, /** @@ -83,7 +93,7 @@ public function __construct( namespace Seam\Resources\UserIdentity { /** - * 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. + * 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. Known error_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Errors { @@ -92,13 +102,25 @@ public static function from_json(mixed $json): Errors|null if (!$json) { return null; } - return new self( - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + $discriminant = is_string($json->error_code ?? null) + ? \Seam\Resources\UserIdentity\Errors\ErrorCode::tryFrom( + $json->error_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UserIdentity\Errors\ErrorCode::ISSUE_WITH_ACS_USER + => \Seam\Resources\UserIdentity\Errors\IssueWithAcsUser::from_json( + $json, + ), + default => new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ), + }; } public function __construct( @@ -116,6 +138,8 @@ public function __construct( public string|null $created_at, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UserIdentity\Errors\ErrorCode>|string|null */ public string|null $error_code, /** @@ -126,11 +150,124 @@ public function __construct( } /** - * 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. + * 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. Known warning_code values use subclasses; unknown values use this base class and retain their raw discriminator. */ class Warnings { public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + $discriminant = is_string($json->warning_code ?? null) + ? \Seam\Resources\UserIdentity\Warnings\WarningCode::tryFrom( + $json->warning_code, + ) + : null; + + return match ($discriminant) { + \Seam\Resources\UserIdentity\Warnings\WarningCode::BEING_DELETED + => \Seam\Resources\UserIdentity\Warnings\BeingDeleted::from_json( + $json, + ), + \Seam\Resources\UserIdentity\Warnings\WarningCode::ACS_USER_PROFILE_DOES_NOT_MATCH_USER_IDENTITY + => \Seam\Resources\UserIdentity\Warnings\AcsUserProfileDoesNotMatchUserIdentity::from_json( + $json, + ), + default => new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ), + }; + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UserIdentity\Warnings\WarningCode>|string|null + */ + public string|null $warning_code, + ) {} + } +} + +namespace Seam\Resources\UserIdentity\Errors { + /** + * Indicates that there is an issue with an access system user associated with this user identity. + */ + final class IssueWithAcsUser extends \Seam\Resources\UserIdentity\Errors + { + public static function from_json(mixed $json): IssueWithAcsUser|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } + + public function __construct( + /** + * ID of the access system that the user identity is associated with. + */ + string|null $acs_system_id, + /** + * ID of the access system user that has an issue. + */ + string|null $acs_user_id, + /** + * Date and time at which Seam created the error. + */ + string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UserIdentity\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + ) { + parent::__construct( + acs_system_id: $acs_system_id, + acs_user_id: $acs_user_id, + created_at: $created_at, + error_code: $error_code, + message: $message, + ); + } + } + + enum ErrorCode: string + { + case ISSUE_WITH_ACS_USER = "issue_with_acs_user"; + } +} + +namespace Seam\Resources\UserIdentity\Warnings { + /** + * Indicates that the user identity is currently being deleted. + */ + final class BeingDeleted extends \Seam\Resources\UserIdentity\Warnings + { + public static function from_json(mixed $json): BeingDeleted|null { if (!$json) { return null; @@ -146,15 +283,72 @@ public function __construct( /** * Date and time at which Seam created the warning. */ - public string|null $created_at, + string|null $created_at, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $message, + string|null $message, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UserIdentity\Warnings\WarningCode>|string|null */ - public string|null $warning_code, - ) {} + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + /** + * Indicates that the ACS user's profile does not match the user identity's profile + */ + final class AcsUserProfileDoesNotMatchUserIdentity extends + \Seam\Resources\UserIdentity\Warnings + { + public static function from_json( + mixed $json, + ): AcsUserProfileDoesNotMatchUserIdentity|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UserIdentity\Warnings\WarningCode>|string|null + */ + string|null $warning_code, + ) { + parent::__construct( + created_at: $created_at, + message: $message, + warning_code: $warning_code, + ); + } + } + + enum WarningCode: string + { + case BEING_DELETED = "being_deleted"; + case ACS_USER_PROFILE_DOES_NOT_MATCH_USER_IDENTITY = "acs_user_profile_does_not_match_user_identity"; } } diff --git a/src/Resources/Webhook.php b/src/Resources/Webhook.php index 69c17272..e1f4f04a 100644 --- a/src/Resources/Webhook.php +++ b/src/Resources/Webhook.php @@ -12,10 +12,10 @@ public static function from_json(mixed $json): Webhook|null return null; } return new self( - event_types: $json->event_types ?? null, - secret: $json->secret ?? null, url: $json->url ?? null, webhook_id: $json->webhook_id ?? null, + event_types: $json->event_types ?? null, + secret: $json->secret ?? null, ); } @@ -30,6 +30,8 @@ public function __construct( public string|null $webhook_id, /** * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. + * + * @var list|null */ public array|null $event_types = null, /** diff --git a/src/Resources/Workspace.php b/src/Resources/Workspace.php index 2fb45430..e68dbcd9 100644 --- a/src/Resources/Workspace.php +++ b/src/Resources/Workspace.php @@ -17,7 +17,7 @@ public static function from_json(mixed $json): Workspace|null connect_webview_customization: isset( $json->connect_webview_customization, ) - ? Workspace\ConnectWebviewCustomization::from_json( + ? \Seam\Resources\Workspace\ConnectWebviewCustomization::from_json( $json->connect_webview_customization, ) : null, @@ -27,8 +27,8 @@ public static function from_json(mixed $json): Workspace|null is_suspended: $json->is_suspended ?? null, name: $json->name ?? null, organization_id: $json->organization_id ?? null, - publishable_key: $json->publishable_key ?? null, workspace_id: $json->workspace_id ?? null, + publishable_key: $json->publishable_key ?? null, ); } @@ -41,7 +41,7 @@ public function __construct( * @deprecated Use `company_name` instead. */ public string|null $connect_partner_name, - public Workspace\ConnectWebviewCustomization|null $connect_webview_customization, + public \Seam\Resources\Workspace\ConnectWebviewCustomization|null $connect_webview_customization, /** * Indicates whether publishable key authentication is enabled for this workspace. */ @@ -100,6 +100,8 @@ public function __construct( public string|null $inviter_logo_url = null, /** * Logo shape for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + * + * @var value-of<\Seam\Resources\Workspace\ConnectWebviewCustomization\LogoShape>|string|null */ public string|null $logo_shape = null, /** @@ -117,3 +119,11 @@ public function __construct( ) {} } } + +namespace Seam\Resources\Workspace\ConnectWebviewCustomization { + enum LogoShape: string + { + case CIRCLE = "circle"; + case SQUARE = "square"; + } +} diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 9fb239cb..5a1bb240 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -160,7 +160,7 @@ public function create( * * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach and work across both standalone smart locks and access systems. Use the lower-level Access Codes API directly only when you specifically need to manage individual PIN codes. * - * @param array $device_ids IDs of the devices for which you want to create the new access codes. + * @param list $device_ids IDs of the devices for which you want to create the new access codes. * @param bool $allow_external_modification Indicates whether [external modification](https://docs.seam.co/low-level-apis/smart-locks/access-codes#external-modification) of the code is allowed. Default: `false`. * @param bool $attempt_for_offline_device * @param string $behavior_when_code_cannot_be_shared Desired behavior if any device cannot share a code. If `throw` (default), no access codes will be created if any device cannot share a code. If `create_random_code`, a random code will be created on devices that cannot share a code. @@ -353,7 +353,7 @@ public function get( * * Specify `device_id`, `access_code_ids`, `access_method_id`, `access_grant_id`, or `access_grant_key`. * - * @param array $access_code_ids IDs of the access codes that you want to retrieve. Specify `device_id`, `access_code_ids`, `access_method_id`, `access_grant_id`, or `access_grant_key`. + * @param list $access_code_ids IDs of the access codes that you want to retrieve. Specify `device_id`, `access_code_ids`, `access_method_id`, `access_grant_id`, or `access_grant_key`. * @param string $access_grant_id ID of the access grant for which you want to list access codes. Specify `device_id`, `access_code_ids`, `access_method_id`, `access_grant_id`, or `access_grant_key`. * @param string $access_grant_key Key of the access grant for which you want to list access codes. Specify `device_id`, `access_code_ids`, `access_method_id`, `access_grant_id`, or `access_grant_key`. * @param string $access_method_id ID of the access method for which you want to list access codes. Specify `device_id`, `access_code_ids`, `access_method_id`, `access_grant_id`, or `access_grant_key`. @@ -487,7 +487,7 @@ public function pull_backup_access_code(string $access_code_id): AccessCode * @param string $device_id ID of the device for which you want to report constraints. * @param int $max_code_length Maximum supported code length as an integer between 4 and 20, inclusive. You can specify either `min_code_length`/`max_code_length` or `supported_code_lengths`. * @param int $min_code_length Minimum supported code length as an integer between 4 and 20, inclusive. You can specify either `min_code_length`/`max_code_length` or `supported_code_lengths`. - * @param array $supported_code_lengths Array of supported code lengths as integers between 4 and 20, inclusive. You can specify either `supported_code_lengths` or `min_code_length`/`max_code_length`. + * @param list $supported_code_lengths Array of supported code lengths as integers between 4 and 20, inclusive. You can specify either `supported_code_lengths` or `min_code_length`/`max_code_length`. * @return void OK */ public function report_device_constraints( diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 29e5df1c..8e391a66 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -30,20 +30,20 @@ public function __construct(ClientInterface $client, array $defaults) /** * Creates a new [Access Grant](https://docs.seam.co/use-cases/granting-access/access-grants). Access Grants are the default and recommended way to grant a user access to any physical space, irrespective of the locking hardware. They work with both standalone smart locks (using `device_ids`) and access control systems (using `acs_entrance_ids` or `space_ids`), and can issue PIN codes, key cards, and mobile keys through a single request. * - * @param array $requested_access_methods + * @param list|\stdClass> $requested_access_methods * @param string $user_identity_id ID of user identity for whom access is being granted. * @param mixed $user_identity When used, creates a new user identity with the given details, and grants them access. * @param string $access_grant_key Unique key for the access grant within the workspace. - * @param array $acs_entrance_ids Set of IDs of the [entrances](https://docs.seam.co/api/acs/systems/list) to which access is being granted. + * @param list $acs_entrance_ids Set of IDs of the [entrances](https://docs.seam.co/api/acs/systems/list) to which access is being granted. * @param string $customization_profile_id ID of the customization profile to apply to the Access Grant and its access methods. - * @param array $device_ids Set of IDs of the [devices](https://docs.seam.co/api/devices/list) to which access is being granted. + * @param list $device_ids Set of IDs of the [devices](https://docs.seam.co/api/devices/list) to which access is being granted. * @param string|NullValue $ends_at Date and time at which the validity of the new grant ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. * @param mixed $location - * @param array $location_ids + * @param list $location_ids * @param string|NullValue $name Name for the access grant. * @param string $reservation_key Reservation key for the access grant. - * @param array $space_ids Set of IDs of existing spaces to which access is being granted. - * @param array $space_keys Set of keys of existing spaces to which access is being granted. + * @param list $space_ids Set of IDs of existing spaces to which access is being granted. + * @param list $space_keys Set of keys of existing spaces to which access is being granted. * @param string $starts_at Date and time at which the validity of the new grant starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @return AccessGrant OK */ @@ -181,10 +181,10 @@ public function get( /** * Gets all related resources for one or more Access Grants. * - * @param array $access_grant_ids IDs of the access grants that you want to get along with their related resources. - * @param array $access_grant_keys Keys of the access grants that you want to get along with their related resources. - * @param array $exclude - * @param array $include + * @param list $access_grant_ids IDs of the access grants that you want to get along with their related resources. + * @param list $access_grant_keys Keys of the access grants that you want to get along with their related resources. + * @param list $exclude + * @param list $include * @return Batch OK */ public function get_related( @@ -233,7 +233,7 @@ public function get_related( * Gets an Access Grant. * * @param string $access_code_id ID of the access code by which you want to filter the list of Access Grants. - * @param array $access_grant_ids IDs of the access grants to retrieve. + * @param list $access_grant_ids IDs of the access grants to retrieve. * @param string|NullValue $access_grant_key Filter Access Grants by access_grant_key. Use null to filter for Access Grants without an access_grant_key. * @param string $acs_entrance_id ID of the entrance by which you want to filter the list of Access Grants. * @param string $acs_system_id ID of the access system by which you want to filter the list of Access Grants. @@ -326,7 +326,7 @@ public function list( * Adds additional requested access methods to an existing Access Grant. * * @param string $access_grant_id ID of the Access Grant to add access methods to. - * @param array $requested_access_methods Array of requested access methods to add to the access grant. + * @param list|\stdClass> $requested_access_methods Array of requested access methods to add to the access grant. * @return AccessGrant OK */ public function request_access_methods( diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 2e3210f6..0b296a17 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -166,9 +166,9 @@ public function get(string $access_method_id): AccessMethod /** * Gets all related resources for one or more Access Methods. * - * @param array $access_method_ids IDs of the access methods that you want to get along with their related resources. - * @param array $exclude - * @param array $include + * @param list $access_method_ids IDs of the access methods that you want to get along with their related resources. + * @param list $exclude + * @param list $include * @return Batch OK */ public function get_related( diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index b8a8774e..ab4826e3 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -60,7 +60,7 @@ public function assign( * @param string $access_method Access method for the new credential. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. * @param string $acs_system_id ID of the access system to which the new credential belongs. You must provide either `acs_user_id` or the combination of `user_identity_id` and `acs_system_id`. * @param string $acs_user_id ID of the access system user to whom the new credential belongs. You must provide either `acs_user_id` or the combination of `user_identity_id` and `acs_system_id`. - * @param array $allowed_acs_entrance_ids Set of IDs of the [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for which the new credential grants access. + * @param list $allowed_acs_entrance_ids Set of IDs of the [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for which the new credential grants access. * @param mixed $assa_abloy_vostio_metadata Vostio-specific metadata for the new credential. * @param string $code Access (PIN) code for the new credential. There may be manufacturer-specific code restrictions. For details, see the applicable [device or system integration guide](https://docs.seam.co/device-and-system-integration-guides). * @param string $credential_manager_acs_system_id ACS system ID of the credential manager for the new credential. diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index 91728f03..e5fec99b 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -100,8 +100,8 @@ public function get(string $acs_encoder_id): AcsEncoder * Returns a list of all [encoders](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). * * @param string $acs_system_id ID of the access system for which you want to retrieve all encoders. - * @param array $acs_system_ids IDs of the access systems for which you want to retrieve all encoders. - * @param array $acs_encoder_ids IDs of the encoders that you want to retrieve. + * @param list $acs_system_ids IDs of the access systems for which you want to retrieve all encoders. + * @param list $acs_encoder_ids IDs of the encoders that you want to retrieve. * @param float $limit Number of encoders to return. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index be84c517..85cc23fe 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -84,7 +84,7 @@ public function grant_access( * * @param string $access_method_id ID of the access method for which you want to retrieve all entrances to which it grants access. * @param string $acs_credential_id ID of the credential for which you want to retrieve all entrances. - * @param array $acs_entrance_ids IDs of the entrances for which you want to retrieve all entrances. + * @param list $acs_entrance_ids IDs of the entrances for which you want to retrieve all entrances. * @param string $acs_system_id ID of the access system for which you want to retrieve all entrances. * @param string $connected_account_id ID of the connected account for which you want to retrieve all entrances. * @param string $customer_key Customer key for which you want to list entrances. @@ -166,7 +166,7 @@ public function list( * Returns a list of all [credentials](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) with access to a specified [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). * * @param string $acs_entrance_id ID of the entrance for which you want to list all credentials that grant access. - * @param array $include_if Conditions that credentials must meet to be included in the returned list. + * @param list $include_if Conditions that credentials must meet to be included in the returned list. * @return array OK */ public function list_credentials_with_access( diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index b4b059db..c6093452 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -123,8 +123,8 @@ public function list_compatible_credential_manager_acs_systems( * Reports ACS system device status including encoders and entrances. * * @param string $acs_system_id ID of the ACS system to report resources for - * @param array $acs_encoders Array of ACS encoders to report - * @param array $acs_entrances Array of ACS entrances to report + * @param list|\stdClass> $acs_encoders Array of ACS encoders to report + * @param list|\stdClass> $acs_entrances Array of ACS entrances to report * @return void OK */ public function report_devices( diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 64ce6cae..33551428 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -53,7 +53,7 @@ public function add_to_access_group( * @param string $acs_system_id ID of the access system to which you want to add the new access system user. * @param string $full_name Full name of the new access system user. * @param mixed $access_schedule `starts_at` and `ends_at` timestamps for the new access system user's access. If you specify an `access_schedule`, you may include both `starts_at` and `ends_at`. If you omit `starts_at`, it defaults to the current time. `ends_at` is optional and must be a time in the future and after `starts_at`. - * @param array $acs_access_group_ids Array of access group IDs to indicate the access groups to which you want to add the new access system user. + * @param list $acs_access_group_ids Array of access group IDs to indicate the access groups to which you want to add the new access system user. * @param string $email * @param string $email_address Email address of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). * @param string $phone_number Phone number of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 74acf26f..fe4fa397 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -60,7 +60,7 @@ public function get( /** * Returns a list of the [action attempts](https://docs.seam.co/core-concepts/action-attempts) that you specify as an array of `action_attempt_id`s. * - * @param array $action_attempt_ids IDs of the action attempts that you want to retrieve. + * @param list $action_attempt_ids IDs of the action attempts that you want to retrieve. * @param string $device_id ID of the device to filter action attempts by. * @param int $limit Maximum number of records to return per page. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 7146e7c8..cefeec04 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -27,14 +27,14 @@ public function __construct(ClientInterface $client, array $defaults) /** * Creates a new [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). * - * @param array $connect_webview_ids IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) for which you want to create a client session. - * @param array $connected_account_ids IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) for which you want to create a client session. + * @param list $connect_webview_ids IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) for which you want to create a client session. + * @param list $connected_account_ids IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) for which you want to create a client session. * @param string $customer_id Customer ID that you want to associate with the new client session. * @param string $customer_key Customer key that you want to associate with the new client session. * @param string $expires_at Date and time at which the client session should expire, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @param string $user_identifier_key Your user ID for the user for whom you want to create a client session. * @param string $user_identity_id ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) for which you want to create a client session. - * @param array $user_identity_ids IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) that you want to associate with the client session. + * @param list $user_identity_ids IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) that you want to associate with the client session. * @return ClientSession OK */ public function create( @@ -136,12 +136,12 @@ public function get( /** * Returns a [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) with specific characteristics or creates a new client session with these characteristics if it does not yet exist. * - * @param array $connect_webview_ids IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) that you want to associate with the client session (or that are already associated with the existing client session). - * @param array $connected_account_ids IDs of the [connected accounts](https://docs.seam.co/api/connected_accounts) that you want to associate with the client session (or that are already associated with the existing client session). + * @param list $connect_webview_ids IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) that you want to associate with the client session (or that are already associated with the existing client session). + * @param list $connected_account_ids IDs of the [connected accounts](https://docs.seam.co/api/connected_accounts) that you want to associate with the client session (or that are already associated with the existing client session). * @param string $expires_at Date and time at which the client session should expire in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. If the client session already exists, this will update the expiration before returning it. * @param string $user_identifier_key Your user ID for the user that you want to associate with the client session (or that is already associated with the existing client session). * @param string $user_identity_id ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) that you want to associate with the client session (or that are already associated with the existing client session). - * @param array $user_identity_ids IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) that you want to associate with the client session. + * @param list $user_identity_ids IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) that you want to associate with the client session. * @return ClientSession OK */ public function get_or_create( @@ -192,11 +192,11 @@ public function get_or_create( * Grants a [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) access to one or more resources, such as [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews), [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity), and so on. * * @param string $client_session_id ID of the client session to which you want to grant access to resources. - * @param array $connect_webview_ids IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) that you want to associate with the client session. - * @param array $connected_account_ids IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) that you want to associate with the client session. + * @param list $connect_webview_ids IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) that you want to associate with the client session. + * @param list $connected_account_ids IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) that you want to associate with the client session. * @param string $user_identifier_key Your user ID for the user that you want to associate with the client session. * @param string $user_identity_id ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) that you want to associate with the client session. - * @param array $user_identity_ids IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) that you want to associate with the client session. + * @param list $user_identity_ids IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) that you want to associate with the client session. * @return void OK */ public function grant_access( diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 5dc7b7e3..adacfc8f 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -34,14 +34,14 @@ public function __construct(ClientInterface $client, array $defaults) * * See also: [Connect Webview Process](https://docs.seam.co/core-concepts/connect-webviews/connect-webview-process). * - * @param array $accepted_capabilities List of accepted device capabilities that restrict the types of devices that can be connected through the Connect Webview. If not provided, defaults will be determined based on the accepted providers. - * @param array $accepted_providers Accepted device provider keys as an alternative to `provider_category`. Use this parameter to specify accepted providers explicitly. See [Customize the Brands to Display in Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). To list all provider keys, use [`/devices/list_device_providers`](https://docs.seam.co/api/devices/list_device_providers) with no filters. + * @param list $accepted_capabilities List of accepted device capabilities that restrict the types of devices that can be connected through the Connect Webview. If not provided, defaults will be determined based on the accepted providers. + * @param list $accepted_providers Accepted device provider keys as an alternative to `provider_category`. Use this parameter to specify accepted providers explicitly. See [Customize the Brands to Display in Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). To list all provider keys, use [`/devices/list_device_providers`](https://docs.seam.co/api/devices/list_device_providers) with no filters. * @param bool $automatically_manage_new_devices Indicates whether newly-added devices should appear as [managed devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). See also: [Customize the Behavior Settings of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-behavior-settings-of-your-connect-webviews). * @param array|\stdClass $custom_metadata Custom metadata that you want to associate with the Connect Webview. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview) enables you to store custom information, like customer details or internal IDs from your application. The custom metadata is then transferred to any [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) that were connected using the Connect Webview, making it easy to find and filter these resources in your [workspace](https://docs.seam.co/core-concepts/workspaces). You can also [filter Connect Webviews by custom metadata](https://docs.seam.co/core-concepts/connect-webviews/filtering-connect-webviews-by-custom-metadata). * @param string $custom_redirect_failure_url Alternative URL that you want to redirect the user to on an error. If you do not set this parameter, the Connect Webview falls back to the `custom_redirect_url`. * @param string $custom_redirect_url URL that you want to redirect the user to after the provider login is complete. * @param string $customer_key Associate the Connect Webview, the connected account, and all resources under the connected account with a customer. If the connected account already exists, it will be associated with the customer. If the connected account already exists, but is already associated with a customer, the Connect Webview will show an error. - * @param array $excluded_providers List of provider keys to exclude from the Connect Webview. These providers will not be shown when the user tries to connect an account. + * @param list $excluded_providers List of provider keys to exclude from the Connect Webview. These providers will not be shown when the user tries to connect an account. * @param string $provider_category Specifies the category of providers that you want to include. To list all providers within a category, use [`/devices/list_device_providers`](https://docs.seam.co/api/devices/list_device_providers) with the desired `provider_category` filter. * @param bool $wait_for_device_creation Indicates whether Seam should finish syncing all devices in a newly-connected account before completing the associated Connect Webview. See also: [Customize the Behavior Settings of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-behavior-settings-of-your-connect-webviews). * @return ConnectWebview OK diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 7dc68fef..1b311387 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -174,7 +174,7 @@ public function sync(string $connected_account_id): void * Updates a [connected account](https://docs.seam.co/core-concepts/connected-accounts). * * @param string $connected_account_id ID of the connected account that you want to update. - * @param array $accepted_capabilities List of accepted device capabilities that restrict the types of devices that can be connected through this connected account. Valid values are `lock`, `thermostat`, `noise_sensor`, and `access_control`. + * @param list $accepted_capabilities List of accepted device capabilities that restrict the types of devices that can be connected through this connected account. Valid values are `lock`, `thermostat`, `noise_sensor`, and `access_control`. * @param bool $automatically_manage_new_devices Indicates whether newly-added devices should appear as [managed devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). * @param array|\stdClass $custom_metadata Custom metadata that you want to associate with the connected account. Entirely replaces the existing custom metadata object. If a new Connect Webview contains custom metadata and is used to reconnect a connected account, the custom metadata from the Connect Webview will entirely replace the entire custom metadata object on the connected account. Supports up to 50 JSON key:value pairs. [Adding custom metadata to a connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account) enables you to store custom information, like customer details or internal IDs from your application. Then, you can [filter connected accounts by the desired metadata](https://docs.seam.co/core-concepts/connected-accounts/filtering-connected-accounts-by-custom-metadata). * @param string $customer_key The customer key to associate with this connected account. If provided, the connected account and all resources under the connected account will be moved to this customer. May only be provided if the connected account is not already associated with a customer. diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index 7baced07..1ee55265 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -27,7 +27,7 @@ public function __construct(ClientInterface $client, array $defaults) /** * Creates a new customer portal magic link with configurable features. * - * @param array $customer_resources_filters Filter configuration for resources based on their custom_metadata. Each filter specifies a field, operation, and value to match against resource custom_metadata. + * @param list|\stdClass> $customer_resources_filters Filter configuration for resources based on their custom_metadata. Each filter specifies a field, operation, and value to match against resource custom_metadata. * @param string $customization_profile_id The ID of the customization profile to use for the portal. * @param mixed $deep_link Deep link target resource for initial redirect. When set, the portal will navigate directly to the specified resource. * @param bool $exclude_locale_picker Whether to exclude the option to select a locale within the portal UI. @@ -108,25 +108,25 @@ public function create_portal( * Deletes customer data including resources like spaces, properties, rooms, users, etc. * This will delete the partner resources and any related Seam resources (user identities, access grants, spaces). * - * @param array $access_grant_keys List of access grant keys to delete. - * @param array $booking_keys List of booking keys to delete. - * @param array $building_keys List of building keys to delete. - * @param array $common_area_keys List of common area keys to delete. - * @param array $customer_keys List of customer keys to delete all data for. - * @param array $facility_keys List of facility keys to delete. - * @param array $guest_keys List of guest keys to delete. - * @param array $listing_keys List of listing keys to delete. - * @param array $property_keys List of property keys to delete. - * @param array $property_listing_keys List of property listing keys to delete. - * @param array $reservation_keys List of reservation keys to delete. - * @param array $resident_keys List of resident keys to delete. - * @param array $room_keys List of room keys to delete. - * @param array $space_keys List of space keys to delete. - * @param array $staff_member_keys List of staff member keys to delete. - * @param array $tenant_keys List of tenant keys to delete. - * @param array $unit_keys List of unit keys to delete. - * @param array $user_identity_keys List of user identity keys to delete. - * @param array $user_keys List of user keys to delete. + * @param list $access_grant_keys List of access grant keys to delete. + * @param list $booking_keys List of booking keys to delete. + * @param list $building_keys List of building keys to delete. + * @param list $common_area_keys List of common area keys to delete. + * @param list $customer_keys List of customer keys to delete all data for. + * @param list $facility_keys List of facility keys to delete. + * @param list $guest_keys List of guest keys to delete. + * @param list $listing_keys List of listing keys to delete. + * @param list $property_keys List of property keys to delete. + * @param list $property_listing_keys List of property listing keys to delete. + * @param list $reservation_keys List of reservation keys to delete. + * @param list $resident_keys List of resident keys to delete. + * @param list $room_keys List of room keys to delete. + * @param list $space_keys List of space keys to delete. + * @param list $staff_member_keys List of staff member keys to delete. + * @param list $tenant_keys List of tenant keys to delete. + * @param list $unit_keys List of unit keys to delete. + * @param list $user_identity_keys List of user identity keys to delete. + * @param list $user_keys List of user keys to delete. * @return void OK */ public function delete_data( @@ -219,25 +219,25 @@ public function delete_data( * Pushes customer data including resources like spaces, properties, rooms, users, etc. * * @param string $customer_key Your unique identifier for the customer. - * @param array $access_grants List of access grants. - * @param array $bookings List of bookings. - * @param array $buildings List of buildings. - * @param array $common_areas List of shared common areas. - * @param array $facilities List of gym or fitness facilities. - * @param array $guests List of guests. - * @param array $listings List of property listings. - * @param array $properties List of short-term rental properties. - * @param array $property_listings List of property listings. - * @param array $reservations List of reservations. - * @param array $residents List of residents. - * @param array $rooms List of hotel or hospitality rooms. - * @param array $sites List of general sites or areas. - * @param array $spaces List of general spaces or areas. - * @param array $staff_members List of staff members. - * @param array $tenants List of tenants. - * @param array $units List of multi-family residential units. - * @param array $user_identities List of user identities. - * @param array $users List of users. + * @param list|\stdClass> $access_grants List of access grants. + * @param list|\stdClass> $bookings List of bookings. + * @param list|\stdClass> $buildings List of buildings. + * @param list|\stdClass> $common_areas List of shared common areas. + * @param list|\stdClass> $facilities List of gym or fitness facilities. + * @param list|\stdClass> $guests List of guests. + * @param list|\stdClass> $listings List of property listings. + * @param list|\stdClass> $properties List of short-term rental properties. + * @param list|\stdClass> $property_listings List of property listings. + * @param list|\stdClass> $reservations List of reservations. + * @param list|\stdClass> $residents List of residents. + * @param list|\stdClass> $rooms List of hotel or hospitality rooms. + * @param list|\stdClass> $sites List of general sites or areas. + * @param list|\stdClass> $spaces List of general spaces or areas. + * @param list|\stdClass> $staff_members List of staff members. + * @param list|\stdClass> $tenants List of tenants. + * @param list|\stdClass> $units List of multi-family residential units. + * @param list|\stdClass> $user_identities List of user identities. + * @param list|\stdClass> $users List of users. * @return void OK */ public function push_data( diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 7cd98776..ab41c023 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -68,13 +68,13 @@ public function get(?string $device_id = null, ?string $name = null): Device * * @param string $connect_webview_id ID of the Connect Webview for which you want to list devices. * @param string $connected_account_id ID of the connected account for which you want to list devices. - * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. + * @param list $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. * @param array|\stdClass $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. - * @param array $device_ids Array of device IDs for which you want to list devices. + * @param list $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type for which you want to list devices. - * @param array $device_types Array of device types for which you want to list devices. + * @param list $device_types Array of device types for which you want to list devices. * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturer for which you want to list devices. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. @@ -209,7 +209,7 @@ public function list_device_providers( /** * Updates provider-specific metadata for devices. * - * @param array $devices Array of devices with provider metadata to update + * @param list|\stdClass> $devices Array of devices with provider metadata to update * @return void OK */ public function report_provider_metadata(array $devices): void diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 9ebb9e2f..2f70c9b2 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -72,12 +72,12 @@ public function get( * * @param string $connect_webview_id ID of the Connect Webview for which you want to list devices. * @param string $connected_account_id ID of the connected account for which you want to list devices. - * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. + * @param list $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. * @param string $customer_key Customer key for which you want to list devices. - * @param array $device_ids Array of device IDs for which you want to list devices. + * @param list $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type for which you want to list devices. - * @param array $device_types Array of device types for which you want to list devices. + * @param list $device_types Array of device types for which you want to list devices. * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturer for which you want to list devices. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index 671745be..6a4509ce 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -67,31 +67,31 @@ public function get( * Returns a list of all events. This endpoint returns the same events that would be sent to a [webhook](https://docs.seam.co/developer-tools/webhooks), but it enables you to filter or see events that already took place. * * @param string $access_code_id ID of the access code for which you want to list events. - * @param array $access_code_ids IDs of the access codes for which you want to list events. + * @param list $access_code_ids IDs of the access codes for which you want to list events. * @param string $access_grant_id ID of the access grant for which you want to list events. - * @param array $access_grant_ids IDs of the access grants for which you want to list events. + * @param list $access_grant_ids IDs of the access grants for which you want to list events. * @param string $access_method_id ID of the access method for which you want to list events. - * @param array $access_method_ids IDs of the access methods for which you want to list events. + * @param list $access_method_ids IDs of the access methods for which you want to list events. * @param string $acs_access_group_id ID of the ACS access group for which you want to list events. * @param string $acs_credential_id ID of the ACS credential for which you want to list events. * @param string $acs_encoder_id ID of the ACS encoder for which you want to list events. * @param string $acs_entrance_id ID of the ACS entrance for which you want to list events. * @param string $acs_system_id ID of the access system for which you want to list events. - * @param array $acs_system_ids IDs of the access systems for which you want to list events. + * @param list $acs_system_ids IDs of the access systems for which you want to list events. * @param string $acs_user_id ID of the ACS user for which you want to list events. - * @param array $between Lower and upper timestamps to define an exclusive interval containing the events that you want to list. You must include `since` or `between`. + * @param list $between Lower and upper timestamps to define an exclusive interval containing the events that you want to list. You must include `since` or `between`. * @param string $connect_webview_id ID of the Connect Webview for which you want to list events. * @param string $connected_account_id ID of the connected account for which you want to list events. * @param string $customer_key Customer key for which you want to list events. * @param string $device_id ID of the device for which you want to list events. - * @param array $device_ids IDs of the devices for which you want to list events. - * @param array $event_ids IDs of the events that you want to list. + * @param list $device_ids IDs of the devices for which you want to list events. + * @param list $event_ids IDs of the events that you want to list. * @param string $event_type Type of the events that you want to list. - * @param array $event_types Types of the events that you want to list. + * @param list $event_types Types of the events that you want to list. * @param float $limit Numerical limit on the number of events to return. * @param string $since Timestamp to indicate the beginning generation time for the events that you want to list. You must include `since` or `between`. * @param string $space_id ID of the space for which you want to list events. - * @param array $space_ids IDs of the spaces for which you want to list events. + * @param list $space_ids IDs of the spaces for which you want to list events. * @param float $unstable_offset Offset for the events that you want to list. * @param string $user_identity_id ID of the user identity for which you want to list events. * @return array OK diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index d02b6263..a1d64a7c 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -112,7 +112,7 @@ public function get(?string $device_id = null, ?string $name = null): Device * @param string $connected_account_id ID of the connected account for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. * @param string $device_type Device type of the locks that you want to list. - * @param array $device_types Device types of the locks that you want to list. + * @param list $device_types Device types of the locks that you want to list. * @param string $manufacturer Manufacturer of the locks that you want to list. * @return array OK */ diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index 4901eabb..05079f0e 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -37,7 +37,7 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. * @param string $device_type Device type of the noise sensors that you want to list. - * @param array $device_types Device types of the noise sensors that you want to list. + * @param list $device_types Device types of the noise sensors that you want to list. * @param string $manufacturer Manufacturers of the noise sensors that you want to list. * @return array OK */ diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index e4e3f398..beb92404 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -29,7 +29,7 @@ public function __construct(ClientInterface $client, array $defaults) /** * Adds [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) to a specific space. * - * @param array $acs_entrance_ids IDs of the entrances that you want to add to the space. + * @param list $acs_entrance_ids IDs of the entrances that you want to add to the space. * @param string $space_id ID of the space to which you want to add entrances. * @return void OK */ @@ -71,7 +71,7 @@ public function add_connected_account( /** * Adds devices to a specific space. * - * @param array $device_ids IDs of the devices that you want to add to the space. + * @param list $device_ids IDs of the devices that you want to add to the space. * @param string $space_id ID of the space to which you want to add devices. * @return void OK */ @@ -91,11 +91,11 @@ public function add_devices(array $device_ids, string $space_id): void * Creates a new space. * * @param string $name Name of the space that you want to create. - * @param array $acs_entrance_ids IDs of the entrances that you want to add to the new space. - * @param array $connected_account_ids IDs of connected accounts to associate with the new space. Persisted on seam.location_third_party_account so the UI can show which provider account(s) a space came from. + * @param list $acs_entrance_ids IDs of the entrances that you want to add to the new space. + * @param list $connected_account_ids IDs of connected accounts to associate with the new space. Persisted on seam.location_third_party_account so the UI can show which provider account(s) a space came from. * @param mixed $customer_data Reservation/stay-related defaults for the space. * @param string $customer_key Customer key for which you want to create the space. - * @param array $device_ids IDs of the devices that you want to add to the new space. + * @param list $device_ids IDs of the devices that you want to add to the new space. * @param string $space_key Unique key for the space within the workspace. * @return Space OK */ @@ -193,10 +193,10 @@ public function get( /** * Gets all related resources for one or more Spaces. * - * @param array $exclude - * @param array $include - * @param array $space_ids IDs of the spaces that you want to get along with their related resources. - * @param array $space_keys Keys of the spaces that you want to get along with their related resources. + * @param list $exclude + * @param list $include + * @param list $space_ids IDs of the spaces that you want to get along with their related resources. + * @param list $space_keys Keys of the spaces that you want to get along with their related resources. * @return Batch OK */ public function get_related( @@ -297,7 +297,7 @@ public function list( /** * Removes [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) from a specific space. * - * @param array $acs_entrance_ids IDs of the entrances that you want to remove from the space. + * @param list $acs_entrance_ids IDs of the entrances that you want to remove from the space. * @param string $space_id ID of the space from which you want to remove entrances. * @return void OK */ @@ -339,7 +339,7 @@ public function remove_connected_account( /** * Removes devices from a specific space. * - * @param array $device_ids IDs of the devices that you want to remove from the space. + * @param list $device_ids IDs of the devices that you want to remove from the space. * @param string $space_id ID of the space from which you want to remove devices. * @return void OK */ @@ -358,9 +358,9 @@ public function remove_devices(array $device_ids, string $space_id): void /** * Updates an existing space. * - * @param array $acs_entrance_ids IDs of the entrances that you want to set for the space. If specified, this will replace all existing entrances. + * @param list $acs_entrance_ids IDs of the entrances that you want to set for the space. If specified, this will replace all existing entrances. * @param mixed $customer_data Reservation/stay-related defaults for the space. Only the keys you provide are updated; omit a key to leave it unchanged. Pass null on a key to clear it. - * @param array $device_ids IDs of the devices that you want to set for the space. If specified, this will replace all existing devices. + * @param list $device_ids IDs of the devices that you want to set for the space. If specified, this will replace all existing devices. * @param string $name Name of the space. * @param string $space_id ID of the space that you want to update. * @param string $space_key Unique key of the space that you want to update. diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index a20245d3..bac9241b 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -333,7 +333,7 @@ public function heat_cool( * @param string $connected_account_id ID of the connected account for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. * @param string $device_type Device type by which you want to filter thermostat devices. - * @param array $device_types Array of device types by which you want to filter thermostat devices. + * @param list $device_types Array of device types by which you want to filter thermostat devices. * @param string $manufacturer Manufacturer by which you want to filter thermostat devices. * @return array OK */ diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index e6a71d7c..32458eca 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -31,7 +31,7 @@ public function __construct(ClientInterface $client, array $defaults) * * @param string $device_id ID of the thermostat device for which you want to create a daily program. * @param string $name Name of the thermostat daily program. - * @param array $periods Array of thermostat daily program periods. + * @param list|\stdClass> $periods Array of thermostat daily program periods. * @return ThermostatDailyProgram OK */ public function create( @@ -85,7 +85,7 @@ public function delete(string $thermostat_daily_program_id): void * Updates a specified thermostat daily program. The periods that you specify overwrite any existing periods for the daily program. * * @param string $name Name of the thermostat daily program that you want to update. - * @param array $periods Array of thermostat daily program periods. The periods that you specify overwrite any existing periods for the daily program. + * @param list|\stdClass> $periods Array of thermostat daily program periods. The periods that you specify overwrite any existing periods for the daily program. * @param string $thermostat_daily_program_id ID of the thermostat daily program that you want to update. * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 390fc72b..3bb35ff6 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -69,7 +69,7 @@ public function add_acs_user( /** * Creates a new [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity). * - * @param array $acs_system_ids List of access system IDs to associate with the new user identity through access system users. If there's no user with the same email address or phone number in the specified access systems, a new access system user is created. If there is an existing user with the same email or phone number in the specified access systems, the user is linked to the user identity. + * @param list $acs_system_ids List of access system IDs to associate with the new user identity through access system users. If there's no user with the same email address or phone number in the specified access systems, a new access system user is created. If there is an existing user with the same email or phone number in the specified access systems, the user is linked to the user identity. * @param string|NullValue $email_address Unique email address for the new user identity. * @param string|NullValue $full_name Full name of the user associated with the new user identity. * @param string|NullValue $phone_number Unique phone number for the new user identity in E.164 format (for example, +15555550100). @@ -238,7 +238,7 @@ public function grant_access_to_device( * @param int $limit Maximum number of records to return per page. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned user identities to include all records that satisfy a partial match using `full_name`, `phone_number`, `email_address` or `user_identity_id`. - * @param array $user_identity_ids Array of user identity IDs by which to filter the list of user identities. + * @param list $user_identity_ids Array of user identity IDs by which to filter the list of user identities. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 9d53b37e..042dda4e 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -28,7 +28,7 @@ public function __construct(ClientInterface $client, array $defaults) * Creates a new [webhook](https://docs.seam.co/developer-tools/webhooks). * * @param string $url URL for the new webhook. - * @param array $event_types Types of events that you want the new webhook to receive. + * @param list $event_types Types of events that you want the new webhook to receive. * @return Webhook OK */ public function create(string $url, ?array $event_types = null): Webhook @@ -107,7 +107,7 @@ public function list(): array /** * Updates a specified [webhook](https://docs.seam.co/developer-tools/webhooks). * - * @param array $event_types Types of events that you want the webhook to receive. + * @param list $event_types Types of events that you want the webhook to receive. * @param string $webhook_id ID of the webhook that you want to update. * @return void OK */ diff --git a/src/SeamWebhook.php b/src/SeamWebhook.php index e46d9ec5..fb2ec5b2 100644 --- a/src/SeamWebhook.php +++ b/src/SeamWebhook.php @@ -26,6 +26,16 @@ public function __construct(string $secret) /** * Verifies an incoming webhook request and returns the event it carries. + * Known event types are returned as concrete Event subclasses for + * instanceof or match-based dispatch; newer types use Event itself. + * + * ```php + * match (true) { + * $event instanceof Event\AccessCodeCreated => $event->access_code_id, + * $event::class === Event::class => $event->event_type, + * default => null, + * }; + * ``` * * @param string $payload The raw HTTP request body. * @param array $headers The HTTP request headers. diff --git a/tests/LiteralBooleanTypesTest.php b/tests/LiteralBooleanTypesTest.php index d1e74e9f..d0f5fadc 100644 --- a/tests/LiteralBooleanTypesTest.php +++ b/tests/LiteralBooleanTypesTest.php @@ -32,7 +32,10 @@ public function testAccessCodeBooleanConstraintsArePreserved(): void ); foreach ( - [AccessCode\Errors::class, UnmanagedAccessCode\Errors::class] + [ + AccessCode\Errors\ProviderIssue::class, + UnmanagedAccessCode\Errors\ProviderIssue::class, + ] as $class ) { $this->assertSame( @@ -42,6 +45,15 @@ public function testAccessCodeBooleanConstraintsArePreserved(): void "is_access_code_error", ))->getType(), ); + } + + foreach ( + [ + AccessCode\Errors\BridgeDisconnected::class, + UnmanagedAccessCode\Errors\BridgeDisconnected::class, + ] + as $class + ) { $this->assertSame( "?bool", (string) (new ReflectionProperty( @@ -54,7 +66,7 @@ public function testAccessCodeBooleanConstraintsArePreserved(): void $this->assertSame( "?bool", (string) (new ReflectionProperty( - ActionAttempt\Result::class, + ActionAttempt\ScanCredential\Result\AcsCredentialOnSeam::class, "is_managed", ))->getType(), ); diff --git a/tests/RecordTypesTest.php b/tests/RecordTypesTest.php index 9ee40b22..41a072a2 100644 --- a/tests/RecordTypesTest.php +++ b/tests/RecordTypesTest.php @@ -8,7 +8,8 @@ use ReflectionMethod; use ReflectionProperty; use Seam\Resources\Device; -use Seam\Resources\Event; +use Seam\Resources\Event\AccessCodeCreated; +use Seam\Resources\Event\NoiseSensorNoiseThresholdTriggered; use Seam\Routes\DevicesClient; final class RecordTypesTest extends TestCase @@ -32,14 +33,14 @@ public function testRecordValueTypesAreGenerated(): void $this->assertStringContainsString( "@var array|\\stdClass|null", (string) (new ReflectionProperty( - Event::class, + AccessCodeCreated::class, "connected_account_custom_metadata", ))->getDocComment(), ); $this->assertStringContainsString( "@var array|\\stdClass|null", (string) (new ReflectionProperty( - Event::class, + NoiseSensorNoiseThresholdTriggered::class, "minut_metadata", ))->getDocComment(), ); diff --git a/tests/ResourceTest.php b/tests/ResourceTest.php index 58d19ed7..582489b0 100644 --- a/tests/ResourceTest.php +++ b/tests/ResourceTest.php @@ -6,6 +6,10 @@ use PHPUnit\Framework\TestCase; use Seam\Resources\Device; +use Seam\Resources\Device\Errors\DeviceOffline; +use Seam\Resources\Device\Errors\ErrorCode; +use Seam\Resources\Device\Properties\AvailableClimatePresets\EcobeeMetadata\Owner; +use Seam\Resources\Device\Properties\Battery\Status; /** * The generated resource classes nest a property's class inside the namespace @@ -39,6 +43,36 @@ private function device(): Device return $device; } + public function testUnknownEnumValuesRemainReadable(): void + { + $device = Device::from_json( + (object) [ + "device_type" => "future_device_type", + ], + ); + + $this->assertSame("future_device_type", $device->device_type); + } + + public function testUnknownDiscriminatedValuesUseTheBaseClass(): void + { + $device = Device::from_json( + (object) [ + "errors" => [ + (object) [ + "error_code" => "future_error", + "message" => "Future error", + ], + ], + ], + ); + $error = $device->errors[0]; + + $this->assertSame(Device\Errors::class, $error::class); + $this->assertSame("future_error", $error->error_code); + $this->assertSame("Future error", $error->message); + } + public function testNestedPropertyClassesAreNamespacedByTheirOwner(): void { $device = $this->device(); @@ -74,6 +108,7 @@ public function testSameNamedPropertiesAtDifferentDepthsKeepTheirShapes(): void $this->assertSame(0.4, $device->properties->battery->level); $this->assertSame("low", $device->properties->battery->status); + $this->assertSame("low", Status::LOW->value); $this->assertSame( 0.25, @@ -97,22 +132,15 @@ class_exists("Seam\\Resources\\DeviceProperties"), ); } - /** - * A list of discriminated objects collapses into one class carrying the - * union of the variant properties, so a field only some variants declare is - * still readable. - */ - public function testDiscriminatedListMergesEveryVariantProperty(): void + public function testDiscriminatedListReturnsTheSpecificVariant(): void { - $device = $this->device(); - - $error = $device->errors[0]; + $error = $this->device()->errors[0]; - $this->assertInstanceOf(Device\Errors::class, $error); + $this->assertInstanceOf(DeviceOffline::class, $error); $this->assertSame("device_offline", $error->error_code); + $this->assertSame("device_offline", ErrorCode::DEVICE_OFFLINE->value); $this->assertTrue($error->is_device_error); - $this->assertObjectHasProperty("is_bridge_error", $error); - $this->assertNull($error->is_bridge_error); + $this->assertObjectNotHasProperty("is_bridge_error", $error); } /** @@ -157,5 +185,6 @@ public function testClimatePresetMetadataKeepsItsOwnShape(): void $this->assertSame("sleep", $preset->climate_ref); $this->assertTrue($preset->is_optimized); $this->assertSame("user", $preset->owner); + $this->assertSame("user", Owner::USER->value); } } diff --git a/tests/SeamWebhookTest.php b/tests/SeamWebhookTest.php index f60e96fc..37b9d888 100644 --- a/tests/SeamWebhookTest.php +++ b/tests/SeamWebhookTest.php @@ -6,6 +6,10 @@ use PHPUnit\Framework\TestCase; use Seam\InvalidWebhookPayloadError; +use Seam\Resources\Event; +use Seam\Resources\Event\AccessCodeCreated; +use Seam\Resources\Event\DeviceConnected; +use Seam\Resources\Event\EventType; use Seam\SeamException; use Seam\SeamWebhook; use Svix\Exception\WebhookVerificationException; @@ -57,7 +61,12 @@ public function testVerifyReturnsTheEvent(): void $this->signed_headers($payload), ); + $this->assertInstanceOf(DeviceConnected::class, $event); $this->assertSame("device.connected", $event->event_type); + $this->assertSame( + "device.connected", + EventType::DEVICE_CONNECTED->value, + ); $this->assertSame( "8d7e0b26-5e6c-4a1f-9b3d-1b0f0e5a9c11", $event->event_id, @@ -78,6 +87,40 @@ public function testVerifyAcceptsHeadersInAnyCase(): void $this->assertSame("device.connected", $event->event_type); } + public function testDecodesAnAccessCodeEventIntoItsSpecificClass(): void + { + $event = Event::from_json( + json_decode( + json_encode([ + "event_id" => "event_1", + "event_type" => "access_code.created", + "workspace_id" => "workspace_1", + "access_code_id" => "access_code_1", + "connected_account_id" => "connected_account_1", + "device_id" => "device_1", + "created_at" => "2024-01-01T00:00:00.000Z", + "occurred_at" => "2024-01-01T00:00:00.000Z", + ]), + ), + ); + + $this->assertInstanceOf(AccessCodeCreated::class, $event); + $this->assertSame("access_code_1", $event->access_code_id); + } + + public function testUnknownEventTypeUsesTheBaseClass(): void + { + $event = Event::from_json( + (object) [ + "event_id" => "event_1", + "event_type" => "device.future_event", + ], + ); + + $this->assertSame(Event::class, $event::class); + $this->assertSame("device.future_event", $event->event_type); + } + public function testVerifyRejectsATamperedPayload(): void { $payload = $this->payload(); diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index 601433cf..00906988 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -9,19 +9,43 @@ use Seam\ActionAttemptTimeoutError; use Seam\InvalidOptionsError; use Seam\Resources\ActionAttempt; +use Seam\Resources\ActionAttempt\Status; +use Seam\Resources\ActionAttempt\UnlockDoor; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; use Tests\Support\RecordingClient; final class WaitForActionAttemptTest extends FakeSeamConnectTestCase { + public function testUnknownValuesUseTheBaseTypeAndRemainReadable(): void + { + $attempt = ActionAttempt::from_json( + (object) [ + "action_attempt_id" => "attempt_1", + "action_type" => "FUTURE_ACTION", + "status" => "future_status", + ], + ); + + $this->assertSame(ActionAttempt::class, $attempt::class); + $this->assertSame("FUTURE_ACTION", $attempt->action_type); + $this->assertSame("future_status", $attempt->status); + } + private function pending_action_attempt(Seam $seam): ActionAttempt { $action_attempt = $seam->locks->unlock_door( $this->seed["august_device_1"], ); + $this->assertInstanceOf(UnlockDoor::class, $action_attempt); $this->assertSame("pending", $action_attempt->status); + $this->assertSame( + Status::PENDING, + Status::tryFrom($action_attempt->status), + ); + $this->assertNull($action_attempt->error); + $this->assertNull($action_attempt->result); $this->set_status($seam, $action_attempt, "pending"); From c13c6726d9cb4f765ef83c6e590caaf75b6a3f42 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 20 Aug 2026 02:47:24 +0000 Subject: [PATCH 78/80] 4.0.0-beta.19 --- package-lock.json | 4 ++-- package.json | 2 +- src/Version.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index ab887011..b35ab47b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.18", + "version": "4.0.0-beta.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.18", + "version": "4.0.0-beta.19", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index ca0eefc6..a79cdce0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.18", + "version": "4.0.0-beta.19", "type": "module", "private": true, "license": "MIT", diff --git a/src/Version.php b/src/Version.php index aa10c7f8..d486de52 100644 --- a/src/Version.php +++ b/src/Version.php @@ -10,7 +10,7 @@ class Version * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.18"; + public const VERSION = "4.0.0-beta.19"; public static function get(): string { From 1f2efc1e402ef1bd5c5cb19eefe3ba540df783d2 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 21:07:04 -0700 Subject: [PATCH 79/80] Update MIGRATION guide --- MIGRATION.md | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 87b0926e..1b7b5c9a 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -22,6 +22,7 @@ composer require "seamapi/seam:^4" | [Requests are retried and time out sooner](#requests-are-retried-and-time-out-sooner) | You depend on requests never being retried, or on the 60-second timeout | | [`poll_until_ready` is replaced](#poll_until_ready-is-replaced-by-wait_for_action_attempt) | You call `$seam->action_attempts->poll_until_ready()` or rely on its 20 s/0.4 s timing | | [Nested resource classes are namespaced](#nested-resource-classes-are-namespaced) | You type-hint nested classes such as `Seam\Resources\DeviceProperties` | +| [Discriminated resources use specific subclasses](#discriminated-resources-use-specific-subclasses) | You inspect exact classes or construct events, action attempts, errors, or warnings | | [Resource constructors take required properties first](#resource-constructors-take-required-properties-first) | You construct a resource positionally rather than with named arguments | | [Missing required parameters fail locally](#missing-required-parameters-fail-locally) | You call endpoints with missing parameters and rely on the server's 400 response | | [Preferred HTTP methods and URL search params](#endpoints-use-preferred-http-methods) | You inspect traffic in a proxy, mock server, or firewall rules | @@ -132,6 +133,45 @@ This rename also fixes a class of bugs where two nested shapes competed for one Property reads are unaffected — only explicit references to the nested class names need updating. +## Discriminated resources use specific subclasses + +In v3, events and action attempts were returned as base resource classes that +combined the properties of every possible variant. Errors and warnings were +similarly represented by one class per containing resource. In v4, known +discriminator values return subclasses containing only that variant's +properties: + +```php +use Seam\Resources\Event; +use Seam\Resources\Event\AccessCodeCreated; + +$event = $seam->events->get(event_id: $event_id); + +if ($event instanceof AccessCodeCreated) { + print $event->access_code_id; +} +``` + +The same pattern applies to action attempts and nested errors and warnings, for +example `Seam\Resources\ActionAttempt\UnlockDoor` and +`Seam\Resources\Device\Errors\DeviceOffline`. Base-class type hints and +`instanceof` checks continue to work because every variant extends its base. +Code that checks an exact class with `$resource::class`, or constructs these +resources directly, must use the appropriate variant class instead. + +If the API returns an unknown discriminator, the SDK returns the concrete base +class and preserves the raw value. Discriminants and other enum-valued response +properties remain strings, so existing string comparisons continue to work. +Generated backed enums are available as optional companions: + +```php +use Seam\Resources\Event\EventType; + +$event->event_type === "access_code.created"; +$event->event_type === EventType::ACCESS_CODE_CREATED->value; +$event_type = EventType::tryFrom($event->event_type); +``` + ## Resource constructors take required properties first In v3, resource constructor parameters were ordered by property name alone. In v4 required properties come first and optional ones follow, alphabetical within each group. @@ -256,10 +296,11 @@ The other Seam SDKs spell the sentinel `NULL` and its type `Null`, but both name 5. Replace `poll_until_ready()` with `wait_for_action_attempt`, and review the new 10 s/1 s defaults. 6. Review the new retry policy and 30-second timeout; pass `retries: 0` or `timeout: 60.0` to keep v3 behavior. 7. Update type hints on nested resource classes (`Seam\Resources\DeviceProperties` → `Seam\Resources\Device\Properties`) and on `$seam->client` (`ClientInterface`). -8. Switch endpoint calls to named arguments, and handle `ArgumentCountError`/`InvalidArgumentException` where calls might be missing parameters. -9. If proxies, firewalls, or test mocks assume all requests are `POST`, update them for `GET`/`PATCH`/`PUT`/`DELETE`. -10. Rename `Seam\Utils\PackageVersion` to `Seam\Version`. -11. Optionally, adopt personal access tokens, `SeamWebhook`, and `NullValue::NULL`. +8. Replace exact base-class checks and direct construction of events, action attempts, errors, and warnings with their discriminated variant classes. +9. Switch endpoint calls to named arguments, and handle `ArgumentCountError`/`InvalidArgumentException` where calls might be missing parameters. +10. If proxies, firewalls, or test mocks assume all requests are `POST`, update them for `GET`/`PATCH`/`PUT`/`DELETE`. +11. Rename `Seam\Utils\PackageVersion` to `Seam\Version`. +12. Optionally, adopt personal access tokens, `SeamWebhook`, and `NullValue::NULL`. # Migrating from seamapi/seam v2 to v3 From 79efcbf51a69b9cbe228ea97e9beec1625221484 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 20 Aug 2026 12:06:05 -0700 Subject: [PATCH 80/80] Add "packageManager": "npm@11.19.0", --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index a79cdce0..03d0f79e 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "version": "^11.0.0 || ^10.9.4" } }, + "packageManager": "npm@11.19.0", "devDependencies": { "@prettier/plugin-php": "^0.25.0", "@seamapi/blueprint": "^1.8.0",