diff --git a/package-lock.json b/package-lock.json index b9f72882..1b3a0ab8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@seamapi/blueprint": "^1.10.0", "@seamapi/fake-seam-connect": "^2.0.4", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.1081.0", + "@seamapi/types": "1.1086.0", "@types/jsonwebtoken": "^9.0.6", "@types/node": "^24.10.9", "ava": "^8.0.1", @@ -1133,9 +1133,9 @@ } }, "node_modules/@seamapi/types": { - "version": "1.1081.0", - "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.1081.0.tgz", - "integrity": "sha512-bFh94NyhpR1nukmihSxLR68TAgYGvbk+JeBnOWHtmNkP9FABSeuplw6cXBjB7S+xiVoazUCnclgrJ9lVnijCvw==", + "version": "1.1086.0", + "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.1086.0.tgz", + "integrity": "sha512-QlrUQhOjbhtTQKkk36osoTmx1ypWGZJuEEKhbYII7buWD2r9AKrDpeOXSqKnFXREAnYguAffeFlv6V7qVjxzvw==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index e476dff6..298d7052 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "@seamapi/blueprint": "^1.10.0", "@seamapi/fake-seam-connect": "^2.0.4", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.1081.0", + "@seamapi/types": "1.1086.0", "@types/jsonwebtoken": "^9.0.6", "@types/node": "^24.10.9", "ava": "^8.0.1", diff --git a/src/lib/resources/action-attempt.ts b/src/lib/resources/action-attempt.ts index 424305f0..3bd74f32 100644 --- a/src/lib/resources/action-attempt.ts +++ b/src/lib/resources/action-attempt.ts @@ -1801,6 +1801,83 @@ export type ActionAttempt = */ action_attempt_id: string + /** + * Action attempt to track the status of returning a thermostat to its weekly program. + */ + action_type: 'ACTIVATE_WEEKLY_PROGRAM' + + /** + * Error associated with the action. + */ + error: null + /** + * Result of the action. + */ + result: {} + + status: 'success' + } + | { + /** + * ID of the action attempt. + */ + action_attempt_id: string + + /** + * Action attempt to track the status of returning a thermostat to its weekly program. + */ + action_type: 'ACTIVATE_WEEKLY_PROGRAM' + + /** + * Error associated with the action. + */ + error: null + /** + * Result of the action. + */ + result: null + + status: 'pending' + } + | { + /** + * ID of the action attempt. + */ + action_attempt_id: string + + /** + * Action attempt to track the status of returning a thermostat to its weekly program. + */ + action_type: 'ACTIVATE_WEEKLY_PROGRAM' + + /** + * Error associated with the action. + */ + error: { + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + message: string + + /** + * Type of the error. + */ + type: string + } + + /** + * Result of the action. + */ + result: null + + status: 'error' + } + | { + /** + * ID of the action attempt. + */ + action_attempt_id: string + /** * Action attempt to track the status of simulating a keypad code entry. */ diff --git a/src/lib/resources/camera-live-view-answer.ts b/src/lib/resources/camera-live-view-answer.ts new file mode 100644 index 00000000..244f6233 --- /dev/null +++ b/src/lib/resources/camera-live-view-answer.ts @@ -0,0 +1,14 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file. + */ + +/** + * Represents the WebRTC SDP answer that starts streaming video from a camera for a live view session. + */ +export type CameraLiveViewAnswer = { + /** + * WebRTC SDP answer for the offer, limited to 64 KiB of UTF-8 data. + */ + sdp_answer: string +} diff --git a/src/lib/resources/camera-live-view-session.ts b/src/lib/resources/camera-live-view-session.ts new file mode 100644 index 00000000..2e3e6ba7 --- /dev/null +++ b/src/lib/resources/camera-live-view-session.ts @@ -0,0 +1,29 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file. + */ + +/** + * Represents a short-lived live view session for a single camera. Use the session ID and token to start a WebRTC stream and to stop the session. + */ +export type CameraLiveViewSession = { + /** + * ID of the camera live view session. + */ + camera_live_view_session_id: string + + /** + * ID of the camera. + */ + device_id: string + + /** + * Date and time at which the live view session expires. + */ + expires_at: string + + /** + * Token that authorizes the offer and stop requests for this session. + */ + token: string +} diff --git a/src/lib/resources/device-provider.ts b/src/lib/resources/device-provider.ts index 33edaff9..dd15dab6 100644 --- a/src/lib/resources/device-provider.ts +++ b/src/lib/resources/device-provider.ts @@ -4,6 +4,10 @@ */ export type DeviceProvider = { + /** + * Indicates whether the thermostat can be returned to its weekly program, the schedule that is configured on the device itself, releasing any hold that Seam has set. + */ + can_activate_weekly_program?: boolean | undefined /** * Indicates whether the lock supports configuring automatic locking. */ @@ -76,6 +80,10 @@ export type DeviceProvider = { * Indicates whether the device supports simulating removal in a sandbox. */ can_simulate_removal?: boolean | undefined + /** + * Indicates whether the camera supports streaming live video through a camera live view session. + */ + can_stream_live_video?: boolean | undefined /** * Indicates whether the thermostat can be turned off. */ diff --git a/src/lib/resources/device.ts b/src/lib/resources/device.ts index c5bf3a50..71f5b1aa 100644 --- a/src/lib/resources/device.ts +++ b/src/lib/resources/device.ts @@ -7,6 +7,10 @@ * Represents a [device](https://www.seam.co/docs/core-concepts/devices) that has been connected to Seam. */ export type Device = { + /** + * Indicates whether the thermostat can be returned to its weekly program, the schedule that is configured on the device itself, releasing any hold that Seam has set. + */ + can_activate_weekly_program?: boolean | undefined /** * Indicates whether the lock supports configuring automatic locking. */ @@ -79,6 +83,10 @@ export type Device = { * Indicates whether the device supports simulating removal in a sandbox. */ can_simulate_removal?: boolean | undefined + /** + * Indicates whether the camera supports streaming live video through a camera live view session. + */ + can_stream_live_video?: boolean | undefined /** * Indicates whether the thermostat can be turned off. */ diff --git a/src/lib/resources/event.ts b/src/lib/resources/event.ts index d113b062..852a33cd 100644 --- a/src/lib/resources/event.ts +++ b/src/lib/resources/event.ts @@ -6363,6 +6363,10 @@ export type SeamEvent = * URL to a thumbnail image captured at the time of activation. */ image_url?: string | undefined + /** + * IDs of the media, such as a video clip and a thumbnail image, captured for this activation. Use `/media/get` to retrieve each one. + */ + media_ids?: Array | undefined /** * Sub-type of motion detected, if available. */ @@ -6425,6 +6429,10 @@ export type SeamEvent = * URL to a thumbnail image captured at the time the doorbell was pressed. */ image_url?: string | undefined + /** + * IDs of the media, such as a video clip and a thumbnail image, captured when the doorbell was pressed. Use `/media/get` to retrieve each one. + */ + media_ids?: Array | undefined /** * Date and time at which the event occurred. */ diff --git a/src/lib/resources/index.ts b/src/lib/resources/index.ts index 1570f8b1..7c78ca93 100644 --- a/src/lib/resources/index.ts +++ b/src/lib/resources/index.ts @@ -14,6 +14,8 @@ export type { AcsSystem } from './acs-system.js' export type { AcsUser } from './acs-user.js' export type { ActionAttempt } from './action-attempt.js' export type { Batch } from './batch.js' +export type { CameraLiveViewAnswer } from './camera-live-view-answer.js' +export type { CameraLiveViewSession } from './camera-live-view-session.js' export type { ClientSession } from './client-session.js' export type { ConnectWebview } from './connect-webview.js' export type { ConnectedAccount } from './connected-account.js' @@ -22,6 +24,7 @@ export type { Device } from './device.js' export type { DeviceProvider } from './device-provider.js' export type { SeamEvent } from './event.js' export type { InstantKey } from './instant-key.js' +export type { Media } from './media.js' export type { NoiseThreshold } from './noise-threshold.js' export type { Phone } from './phone.js' export type { Space } from './space.js' diff --git a/src/lib/resources/media.ts b/src/lib/resources/media.ts new file mode 100644 index 00000000..361ebaf7 --- /dev/null +++ b/src/lib/resources/media.ts @@ -0,0 +1,54 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file. + */ + +/** + * Represents a piece of media, such as a video clip or a thumbnail image, that a device captured for an event. Media is in beta. + */ +export type Media = { + /** + * MIME type of the media, such as `video/mp4` or `image/jpeg`. + */ + content_type: string | null + /** + * Date and time at which the media was created. + */ + created_at: string + + /** + * ID of the device that captured the media. + */ + device_id: string | null + /** + * ID of the event that the media belongs to. + */ + event_id: string | null + /** + * Date and time at which the media stops being available. Null when Seam does not know when the media expires. + */ + expires_at: string | null + /** + * ID of the media. + */ + media_id: string + + /** + * Type of the media: a video clip or a still image. + */ + media_type: 'video' | 'image' + + /** + * Status of the media. `pending` means that Seam is still retrieving the media. `available` means that `url` can be used to download it. `unavailable` means that no media exists for the event, and `failed` means that Seam could not retrieve it. + */ + status: 'pending' | 'available' | 'unavailable' | 'failed' + + /** + * Short-lived URL from which you can download the media. Null unless `status` is `available`. The URL expires after about five minutes. Call `/media/get` again for a new URL. + */ + url: string | null + /** + * ID of the workspace that contains the media. + */ + workspace_id: string +} diff --git a/src/lib/resources/unmanaged-device.ts b/src/lib/resources/unmanaged-device.ts index 76086734..aec1f05a 100644 --- a/src/lib/resources/unmanaged-device.ts +++ b/src/lib/resources/unmanaged-device.ts @@ -7,6 +7,10 @@ * Represents an [unmanaged device](https://www.seam.co/docs/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://www.seam.co/docs/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://www.seam.co/docs/core-concepts/devices/managed-and-unmanaged-devices#convert-an-unmanaged-device-to-managed). */ export type UnmanagedDevice = { + /** + * Indicates whether the thermostat can be returned to its weekly program, the schedule that is configured on the device itself, releasing any hold that Seam has set. + */ + can_activate_weekly_program?: boolean | undefined /** * Indicates whether the lock supports configuring automatic locking. */ @@ -79,6 +83,10 @@ export type UnmanagedDevice = { * Indicates whether the device supports simulating removal in a sandbox. */ can_simulate_removal?: boolean | undefined + /** + * Indicates whether the camera supports streaming live video through a camera live view session. + */ + can_stream_live_video?: boolean | undefined /** * Indicates whether the thermostat can be turned off. */ diff --git a/src/lib/routes/cameras/cameras.ts b/src/lib/routes/cameras/cameras.ts new file mode 100644 index 00000000..57ef893b --- /dev/null +++ b/src/lib/routes/cameras/cameras.ts @@ -0,0 +1,165 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file or add other files to this directory. + */ + +import { + getAuthHeadersForClientSessionToken, + warnOnInsecureuserIdentifierKey, +} from 'lib/auth.js' +import { type Client, createClient } from 'lib/client.js' +import { + isSeamHttpOptionsWithApiKey, + isSeamHttpOptionsWithClient, + isSeamHttpOptionsWithClientSessionToken, + isSeamHttpOptionsWithConsoleSessionToken, + isSeamHttpOptionsWithPersonalAccessToken, + type SeamHttpFromPublishableKeyOptions, + SeamHttpInvalidOptionsError, + type SeamHttpOptions, + type SeamHttpOptionsWithApiKey, + type SeamHttpOptionsWithClient, + type SeamHttpOptionsWithClientSessionToken, + type SeamHttpOptionsWithConsoleSessionToken, + type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, +} from 'lib/options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/parse-options.js' +import { SeamHttpClientSessions } from 'lib/routes/client-sessions/index.js' +import type { SeamPaginatedRequest } from 'lib/seam-http-request.js' +import { SeamPaginator } from 'lib/seam-paginator.js' + +import { SeamHttpCamerasLiveViews } from './live-views/index.js' + +export class SeamHttpCameras { + client: Client + readonly defaults: Required + + constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) + } + + static fromClient( + client: SeamHttpOptionsWithClient['client'], + options: Omit = {}, + ): SeamHttpCameras { + const constructorOptions = { ...options, client } + if (!isSeamHttpOptionsWithClient(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing client') + } + return new SeamHttpCameras(constructorOptions) + } + + static fromApiKey( + apiKey: SeamHttpOptionsWithApiKey['apiKey'], + options: Omit = {}, + ): SeamHttpCameras { + const constructorOptions = { ...options, apiKey } + if (!isSeamHttpOptionsWithApiKey(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing apiKey') + } + return new SeamHttpCameras(constructorOptions) + } + + static fromClientSessionToken( + clientSessionToken: SeamHttpOptionsWithClientSessionToken['clientSessionToken'], + options: Omit< + SeamHttpOptionsWithClientSessionToken, + 'clientSessionToken' + > = {}, + ): SeamHttpCameras { + const constructorOptions = { ...options, clientSessionToken } + if (!isSeamHttpOptionsWithClientSessionToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing clientSessionToken') + } + return new SeamHttpCameras(constructorOptions) + } + + static async fromPublishableKey( + publishableKey: string, + userIdentifierKey: string, + options: SeamHttpFromPublishableKeyOptions = {}, + ): Promise { + warnOnInsecureuserIdentifierKey(userIdentifierKey) + const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttpCameras.fromPublishableKey', + ) + } + const client = createClient(clientOptions) + const clientSessions = SeamHttpClientSessions.fromClient(client) + const { token } = await clientSessions.getOrCreate({ + user_identifier_key: userIdentifierKey, + }) + return SeamHttpCameras.fromClientSessionToken(token, options) + } + + static fromConsoleSessionToken( + consoleSessionToken: SeamHttpOptionsWithConsoleSessionToken['consoleSessionToken'], + workspaceId: SeamHttpOptionsWithConsoleSessionToken['workspaceId'], + options: Omit< + SeamHttpOptionsWithConsoleSessionToken, + 'consoleSessionToken' | 'workspaceId' + > = {}, + ): SeamHttpCameras { + const constructorOptions = { ...options, consoleSessionToken, workspaceId } + if (!isSeamHttpOptionsWithConsoleSessionToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError( + 'Missing consoleSessionToken or workspaceId', + ) + } + return new SeamHttpCameras(constructorOptions) + } + + static fromPersonalAccessToken( + personalAccessToken: SeamHttpOptionsWithPersonalAccessToken['personalAccessToken'], + workspaceId: SeamHttpOptionsWithPersonalAccessToken['workspaceId'], + options: Omit< + SeamHttpOptionsWithPersonalAccessToken, + 'personalAccessToken' | 'workspaceId' + > = {}, + ): SeamHttpCameras { + const constructorOptions = { ...options, personalAccessToken, workspaceId } + if (!isSeamHttpOptionsWithPersonalAccessToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError( + 'Missing personalAccessToken or workspaceId', + ) + } + return new SeamHttpCameras(constructorOptions) + } + + createPaginator( + request: SeamPaginatedRequest, + ): SeamPaginator { + return new SeamPaginator(request) + } + + async updateClientSessionToken( + clientSessionToken: SeamHttpOptionsWithClientSessionToken['clientSessionToken'], + ): Promise { + const { headers } = this.client.defaults + const authHeaders = getAuthHeadersForClientSessionToken({ + clientSessionToken, + }) + for (const key of Object.keys(authHeaders)) { + if (headers[key] == null) { + throw new Error( + 'Cannot update a clientSessionToken on a client created without a clientSessionToken', + ) + } + } + this.client.defaults.headers = { ...headers, ...authHeaders } + const clientSessions = SeamHttpClientSessions.fromClient(this.client) + await clientSessions.get() + } + + get liveViews(): SeamHttpCamerasLiveViews { + return SeamHttpCamerasLiveViews.fromClient(this.client, this.defaults) + } +} diff --git a/src/lib/routes/cameras/index.ts b/src/lib/routes/cameras/index.ts new file mode 100644 index 00000000..23a4aa1a --- /dev/null +++ b/src/lib/routes/cameras/index.ts @@ -0,0 +1,7 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file or add other files to this directory. + */ + +export * from './cameras.js' +export * from './live-views/index.js' diff --git a/src/lib/routes/cameras/live-views/index.ts b/src/lib/routes/cameras/live-views/index.ts new file mode 100644 index 00000000..cf59a5c0 --- /dev/null +++ b/src/lib/routes/cameras/live-views/index.ts @@ -0,0 +1,6 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file or add other files to this directory. + */ + +export * from './live-views.js' diff --git a/src/lib/routes/cameras/live-views/live-views.ts b/src/lib/routes/cameras/live-views/live-views.ts new file mode 100644 index 00000000..dbe06cc7 --- /dev/null +++ b/src/lib/routes/cameras/live-views/live-views.ts @@ -0,0 +1,316 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file or add other files to this directory. + */ + +import { + getAuthHeadersForClientSessionToken, + warnOnInsecureuserIdentifierKey, +} from 'lib/auth.js' +import { type Client, createClient } from 'lib/client.js' +import { + isSeamHttpOptionsWithApiKey, + isSeamHttpOptionsWithClient, + isSeamHttpOptionsWithClientSessionToken, + isSeamHttpOptionsWithConsoleSessionToken, + isSeamHttpOptionsWithPersonalAccessToken, + type SeamHttpFromPublishableKeyOptions, + SeamHttpInvalidOptionsError, + type SeamHttpOptions, + type SeamHttpOptionsWithApiKey, + type SeamHttpOptionsWithClient, + type SeamHttpOptionsWithClientSessionToken, + type SeamHttpOptionsWithConsoleSessionToken, + type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, +} from 'lib/options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/parse-options.js' +import type { CameraLiveViewAnswer } from 'lib/resources/camera-live-view-answer.js' +import type { CameraLiveViewSession } from 'lib/resources/camera-live-view-session.js' +import { SeamHttpClientSessions } from 'lib/routes/client-sessions/index.js' +import { + SeamHttpRequest, + type SeamPaginatedRequest, +} from 'lib/seam-http-request.js' +import { SeamPaginator } from 'lib/seam-paginator.js' + +export class SeamHttpCamerasLiveViews { + client: Client + readonly defaults: Required + + constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) + } + + static fromClient( + client: SeamHttpOptionsWithClient['client'], + options: Omit = {}, + ): SeamHttpCamerasLiveViews { + const constructorOptions = { ...options, client } + if (!isSeamHttpOptionsWithClient(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing client') + } + return new SeamHttpCamerasLiveViews(constructorOptions) + } + + static fromApiKey( + apiKey: SeamHttpOptionsWithApiKey['apiKey'], + options: Omit = {}, + ): SeamHttpCamerasLiveViews { + const constructorOptions = { ...options, apiKey } + if (!isSeamHttpOptionsWithApiKey(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing apiKey') + } + return new SeamHttpCamerasLiveViews(constructorOptions) + } + + static fromClientSessionToken( + clientSessionToken: SeamHttpOptionsWithClientSessionToken['clientSessionToken'], + options: Omit< + SeamHttpOptionsWithClientSessionToken, + 'clientSessionToken' + > = {}, + ): SeamHttpCamerasLiveViews { + const constructorOptions = { ...options, clientSessionToken } + if (!isSeamHttpOptionsWithClientSessionToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing clientSessionToken') + } + return new SeamHttpCamerasLiveViews(constructorOptions) + } + + static async fromPublishableKey( + publishableKey: string, + userIdentifierKey: string, + options: SeamHttpFromPublishableKeyOptions = {}, + ): Promise { + warnOnInsecureuserIdentifierKey(userIdentifierKey) + const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttpCamerasLiveViews.fromPublishableKey', + ) + } + const client = createClient(clientOptions) + const clientSessions = SeamHttpClientSessions.fromClient(client) + const { token } = await clientSessions.getOrCreate({ + user_identifier_key: userIdentifierKey, + }) + return SeamHttpCamerasLiveViews.fromClientSessionToken(token, options) + } + + static fromConsoleSessionToken( + consoleSessionToken: SeamHttpOptionsWithConsoleSessionToken['consoleSessionToken'], + workspaceId: SeamHttpOptionsWithConsoleSessionToken['workspaceId'], + options: Omit< + SeamHttpOptionsWithConsoleSessionToken, + 'consoleSessionToken' | 'workspaceId' + > = {}, + ): SeamHttpCamerasLiveViews { + const constructorOptions = { ...options, consoleSessionToken, workspaceId } + if (!isSeamHttpOptionsWithConsoleSessionToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError( + 'Missing consoleSessionToken or workspaceId', + ) + } + return new SeamHttpCamerasLiveViews(constructorOptions) + } + + static fromPersonalAccessToken( + personalAccessToken: SeamHttpOptionsWithPersonalAccessToken['personalAccessToken'], + workspaceId: SeamHttpOptionsWithPersonalAccessToken['workspaceId'], + options: Omit< + SeamHttpOptionsWithPersonalAccessToken, + 'personalAccessToken' | 'workspaceId' + > = {}, + ): SeamHttpCamerasLiveViews { + const constructorOptions = { ...options, personalAccessToken, workspaceId } + if (!isSeamHttpOptionsWithPersonalAccessToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError( + 'Missing personalAccessToken or workspaceId', + ) + } + return new SeamHttpCamerasLiveViews(constructorOptions) + } + + createPaginator( + request: SeamPaginatedRequest, + ): SeamPaginator { + return new SeamPaginator(request) + } + + async updateClientSessionToken( + clientSessionToken: SeamHttpOptionsWithClientSessionToken['clientSessionToken'], + ): Promise { + const { headers } = this.client.defaults + const authHeaders = getAuthHeadersForClientSessionToken({ + clientSessionToken, + }) + for (const key of Object.keys(authHeaders)) { + if (headers[key] == null) { + throw new Error( + 'Cannot update a clientSessionToken on a client created without a clientSessionToken', + ) + } + } + this.client.defaults.headers = { ...headers, ...authHeaders } + const clientSessions = SeamHttpClientSessions.fromClient(this.client) + await clientSessions.get() + } + + /** + * Creates a short-lived live view session for a single camera. Pass the returned session ID and token to `/cameras/live_views/offer` to start a WebRTC stream, and to `/cameras/live_views/stop` to end the session. + * + * Camera live view is in beta. To enable it for your workspace, contact Seam support. To check whether a camera supports live view, use `device.can_stream_live_video`. + */ + create( + parameters: CamerasLiveViewsCreateParameters, + options: CamerasLiveViewsCreateOptions = {}, + ): CamerasLiveViewsCreateRequest { + return new SeamHttpRequest(this, { + pathname: '/cameras/live_views/create', + method: 'POST', + body: parameters, + parameters, + hasRequiredParameters: true, + requiredParameterNames: ['device_id'], + atLeastOneParameterNames: [], + responseKey: 'camera_live_view_session', + options, + }) + } + + /** + * Exchanges a WebRTC SDP offer for an SDP answer that starts streaming video from the camera, for a live view session that you created using `/cameras/live_views/create`. + * + * Camera live view is in beta. To enable it for your workspace, contact Seam support. + */ + offer( + parameters: CamerasLiveViewsOfferParameters, + options: CamerasLiveViewsOfferOptions = {}, + ): CamerasLiveViewsOfferRequest { + return new SeamHttpRequest(this, { + pathname: '/cameras/live_views/offer', + method: 'POST', + body: parameters, + parameters, + hasRequiredParameters: true, + requiredParameterNames: [ + 'camera_live_view_session_id', + 'sdp_offer', + 'token', + ], + atLeastOneParameterNames: [], + responseKey: 'camera_live_view_answer', + options, + }) + } + + /** + * Stops a camera live view session that the current client session owns. + * + * Camera live view is in beta. To enable it for your workspace, contact Seam support. + */ + stop( + parameters: CamerasLiveViewsStopParameters, + options: CamerasLiveViewsStopOptions = {}, + ): CamerasLiveViewsStopRequest { + return new SeamHttpRequest(this, { + pathname: '/cameras/live_views/stop', + method: 'POST', + body: parameters, + parameters, + hasRequiredParameters: true, + requiredParameterNames: ['camera_live_view_session_id', 'token'], + atLeastOneParameterNames: [], + responseKey: undefined, + options, + }) + } +} + +export type CamerasLiveViewsCreateParameters = { + /** + * ID of the camera to view. + */ + device_id: string + + /** + * Number of seconds for which the live view session is valid, up to 600. + */ + duration_seconds?: number | undefined + /** + * Indicates whether to include the camera's audio. + */ + include_audio?: boolean | undefined +} + +/** + * @deprecated Use CamerasLiveViewsCreateRequest instead. + */ +export type CamerasLiveViewsCreateResponse = { + camera_live_view_session: CameraLiveViewSession +} + +export type CamerasLiveViewsCreateRequest = SeamHttpRequest< + CamerasLiveViewsCreateResponse, + 'camera_live_view_session' +> + +export interface CamerasLiveViewsCreateOptions {} + +export type CamerasLiveViewsOfferParameters = { + /** + * ID of the camera live view session. + */ + camera_live_view_session_id: string + + /** + * WebRTC SDP offer from the viewer, limited to 64 KiB of UTF-8 data. + */ + sdp_offer: string + + /** + * Token returned when the camera live view session was created. + */ + token: string +} + +/** + * @deprecated Use CamerasLiveViewsOfferRequest instead. + */ +export type CamerasLiveViewsOfferResponse = { + camera_live_view_answer: CameraLiveViewAnswer +} + +export type CamerasLiveViewsOfferRequest = SeamHttpRequest< + CamerasLiveViewsOfferResponse, + 'camera_live_view_answer' +> + +export interface CamerasLiveViewsOfferOptions {} + +export type CamerasLiveViewsStopParameters = { + /** + * ID of the camera live view session. + */ + camera_live_view_session_id: string + + /** + * Token returned when the camera live view session was created. + */ + token: string +} + +/** + * @deprecated Use CamerasLiveViewsStopRequest instead. + */ +export type CamerasLiveViewsStopResponse = void + +export type CamerasLiveViewsStopRequest = SeamHttpRequest + +export interface CamerasLiveViewsStopOptions {} diff --git a/src/lib/routes/index.ts b/src/lib/routes/index.ts index 4bf03881..68532f1f 100644 --- a/src/lib/routes/index.ts +++ b/src/lib/routes/index.ts @@ -8,6 +8,7 @@ export * from './access-grants/index.js' export * from './access-methods/index.js' export * from './acs/index.js' export * from './action-attempts/index.js' +export * from './cameras/index.js' export * from './client-sessions/index.js' export * from './connect-webviews/index.js' export * from './connected-accounts/index.js' @@ -16,6 +17,7 @@ export * from './devices/index.js' export * from './events/index.js' export * from './instant-keys/index.js' export * from './locks/index.js' +export * from './media/index.js' export * from './noise-sensors/index.js' export * from './phones/index.js' export * from './seam-http.js' diff --git a/src/lib/routes/media/index.ts b/src/lib/routes/media/index.ts new file mode 100644 index 00000000..df24a731 --- /dev/null +++ b/src/lib/routes/media/index.ts @@ -0,0 +1,6 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file or add other files to this directory. + */ + +export * from './media.js' diff --git a/src/lib/routes/media/media.ts b/src/lib/routes/media/media.ts new file mode 100644 index 00000000..d3d07ad7 --- /dev/null +++ b/src/lib/routes/media/media.ts @@ -0,0 +1,203 @@ +/* + * Automatically generated by codegen/smith.ts. + * Do not edit this file or add other files to this directory. + */ + +import { + getAuthHeadersForClientSessionToken, + warnOnInsecureuserIdentifierKey, +} from 'lib/auth.js' +import { type Client, createClient } from 'lib/client.js' +import { + isSeamHttpOptionsWithApiKey, + isSeamHttpOptionsWithClient, + isSeamHttpOptionsWithClientSessionToken, + isSeamHttpOptionsWithConsoleSessionToken, + isSeamHttpOptionsWithPersonalAccessToken, + type SeamHttpFromPublishableKeyOptions, + SeamHttpInvalidOptionsError, + type SeamHttpOptions, + type SeamHttpOptionsWithApiKey, + type SeamHttpOptionsWithClient, + type SeamHttpOptionsWithClientSessionToken, + type SeamHttpOptionsWithConsoleSessionToken, + type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, +} from 'lib/options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/parse-options.js' +import type { Media } from 'lib/resources/media.js' +import { SeamHttpClientSessions } from 'lib/routes/client-sessions/index.js' +import { + SeamHttpRequest, + type SeamPaginatedRequest, +} from 'lib/seam-http-request.js' +import { SeamPaginator } from 'lib/seam-paginator.js' + +export class SeamHttpMedia { + client: Client + readonly defaults: Required + + constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) + } + + static fromClient( + client: SeamHttpOptionsWithClient['client'], + options: Omit = {}, + ): SeamHttpMedia { + const constructorOptions = { ...options, client } + if (!isSeamHttpOptionsWithClient(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing client') + } + return new SeamHttpMedia(constructorOptions) + } + + static fromApiKey( + apiKey: SeamHttpOptionsWithApiKey['apiKey'], + options: Omit = {}, + ): SeamHttpMedia { + const constructorOptions = { ...options, apiKey } + if (!isSeamHttpOptionsWithApiKey(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing apiKey') + } + return new SeamHttpMedia(constructorOptions) + } + + static fromClientSessionToken( + clientSessionToken: SeamHttpOptionsWithClientSessionToken['clientSessionToken'], + options: Omit< + SeamHttpOptionsWithClientSessionToken, + 'clientSessionToken' + > = {}, + ): SeamHttpMedia { + const constructorOptions = { ...options, clientSessionToken } + if (!isSeamHttpOptionsWithClientSessionToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError('Missing clientSessionToken') + } + return new SeamHttpMedia(constructorOptions) + } + + static async fromPublishableKey( + publishableKey: string, + userIdentifierKey: string, + options: SeamHttpFromPublishableKeyOptions = {}, + ): Promise { + warnOnInsecureuserIdentifierKey(userIdentifierKey) + const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttpMedia.fromPublishableKey', + ) + } + const client = createClient(clientOptions) + const clientSessions = SeamHttpClientSessions.fromClient(client) + const { token } = await clientSessions.getOrCreate({ + user_identifier_key: userIdentifierKey, + }) + return SeamHttpMedia.fromClientSessionToken(token, options) + } + + static fromConsoleSessionToken( + consoleSessionToken: SeamHttpOptionsWithConsoleSessionToken['consoleSessionToken'], + workspaceId: SeamHttpOptionsWithConsoleSessionToken['workspaceId'], + options: Omit< + SeamHttpOptionsWithConsoleSessionToken, + 'consoleSessionToken' | 'workspaceId' + > = {}, + ): SeamHttpMedia { + const constructorOptions = { ...options, consoleSessionToken, workspaceId } + if (!isSeamHttpOptionsWithConsoleSessionToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError( + 'Missing consoleSessionToken or workspaceId', + ) + } + return new SeamHttpMedia(constructorOptions) + } + + static fromPersonalAccessToken( + personalAccessToken: SeamHttpOptionsWithPersonalAccessToken['personalAccessToken'], + workspaceId: SeamHttpOptionsWithPersonalAccessToken['workspaceId'], + options: Omit< + SeamHttpOptionsWithPersonalAccessToken, + 'personalAccessToken' | 'workspaceId' + > = {}, + ): SeamHttpMedia { + const constructorOptions = { ...options, personalAccessToken, workspaceId } + if (!isSeamHttpOptionsWithPersonalAccessToken(constructorOptions)) { + throw new SeamHttpInvalidOptionsError( + 'Missing personalAccessToken or workspaceId', + ) + } + return new SeamHttpMedia(constructorOptions) + } + + createPaginator( + request: SeamPaginatedRequest, + ): SeamPaginator { + return new SeamPaginator(request) + } + + async updateClientSessionToken( + clientSessionToken: SeamHttpOptionsWithClientSessionToken['clientSessionToken'], + ): Promise { + const { headers } = this.client.defaults + const authHeaders = getAuthHeadersForClientSessionToken({ + clientSessionToken, + }) + for (const key of Object.keys(authHeaders)) { + if (headers[key] == null) { + throw new Error( + 'Cannot update a clientSessionToken on a client created without a clientSessionToken', + ) + } + } + this.client.defaults.headers = { ...headers, ...authHeaders } + const clientSessions = SeamHttpClientSessions.fromClient(this.client) + await clientSessions.get() + } + + /** + * Returns a specified piece of media, such as a video clip or thumbnail image captured for a camera event, with a short-lived URL from which you can download it. Camera events list their media in `media_ids`. This endpoint is in beta. + */ + get( + parameters: MediaGetParameters, + options: MediaGetOptions = {}, + ): MediaGetRequest { + return new SeamHttpRequest(this, { + pathname: '/media/get', + method: 'GET', + params: parameters, + parameters, + hasRequiredParameters: true, + requiredParameterNames: ['media_id'], + atLeastOneParameterNames: [], + responseKey: 'media', + options, + }) + } +} + +export type MediaGetParameters = { + /** + * Response format. `json` returns the media object. `redirect` responds with a `302` redirect to the media's download URL, so you can use this endpoint directly as the source of an image or video. + */ + format?: 'json' | 'redirect' | undefined + /** + * ID of the media that you want to get. + */ + media_id: string +} + +/** + * @deprecated Use MediaGetRequest instead. + */ +export type MediaGetResponse = { media: Media } + +export type MediaGetRequest = SeamHttpRequest + +export interface MediaGetOptions {} diff --git a/src/lib/routes/seam-http-endpoints.ts b/src/lib/routes/seam-http-endpoints.ts index 4357b16c..97f414e5 100644 --- a/src/lib/routes/seam-http-endpoints.ts +++ b/src/lib/routes/seam-http-endpoints.ts @@ -319,6 +319,18 @@ import { type ActionAttemptsListRequest, SeamHttpActionAttempts, } from './action-attempts/index.js' +import { + type CamerasLiveViewsCreateOptions, + type CamerasLiveViewsCreateParameters, + type CamerasLiveViewsCreateRequest, + type CamerasLiveViewsOfferOptions, + type CamerasLiveViewsOfferParameters, + type CamerasLiveViewsOfferRequest, + type CamerasLiveViewsStopOptions, + type CamerasLiveViewsStopParameters, + type CamerasLiveViewsStopRequest, + SeamHttpCamerasLiveViews, +} from './cameras/live-views/index.js' import { type ClientSessionsCreateOptions, type ClientSessionsCreateParameters, @@ -493,6 +505,12 @@ import { type LocksSimulateManualLockViaKeypadRequest, SeamHttpLocksSimulate, } from './locks/simulate/index.js' +import { + type MediaGetOptions, + type MediaGetParameters, + type MediaGetRequest, + SeamHttpMedia, +} from './media/index.js' import { type NoiseSensorsListOptions, type NoiseSensorsListParameters, @@ -597,6 +615,9 @@ import { type ThermostatsActivateClimatePresetOptions, type ThermostatsActivateClimatePresetParameters, type ThermostatsActivateClimatePresetRequest, + type ThermostatsActivateWeeklyProgramOptions, + type ThermostatsActivateWeeklyProgramParameters, + type ThermostatsActivateWeeklyProgramRequest, type ThermostatsCoolOptions, type ThermostatsCoolParameters, type ThermostatsCoolRequest, @@ -2244,6 +2265,60 @@ export class SeamHttpEndpoints { } } + /** + * Creates a short-lived live view session for a single camera. Pass the returned session ID and token to `/cameras/live_views/offer` to start a WebRTC stream, and to `/cameras/live_views/stop` to end the session. + * + * Camera live view is in beta. To enable it for your workspace, contact Seam support. To check whether a camera supports live view, use `device.can_stream_live_video`. + */ + get '/cameras/live_views/create'(): ( + parameters: CamerasLiveViewsCreateParameters, + options?: CamerasLiveViewsCreateOptions, + ) => CamerasLiveViewsCreateRequest { + const { client, defaults } = this + return function camerasLiveViewsCreate( + ...args: Parameters + ): ReturnType { + const seam = SeamHttpCamerasLiveViews.fromClient(client, defaults) + return seam.create(...args) + } + } + + /** + * Exchanges a WebRTC SDP offer for an SDP answer that starts streaming video from the camera, for a live view session that you created using `/cameras/live_views/create`. + * + * Camera live view is in beta. To enable it for your workspace, contact Seam support. + */ + get '/cameras/live_views/offer'(): ( + parameters: CamerasLiveViewsOfferParameters, + options?: CamerasLiveViewsOfferOptions, + ) => CamerasLiveViewsOfferRequest { + const { client, defaults } = this + return function camerasLiveViewsOffer( + ...args: Parameters + ): ReturnType { + const seam = SeamHttpCamerasLiveViews.fromClient(client, defaults) + return seam.offer(...args) + } + } + + /** + * Stops a camera live view session that the current client session owns. + * + * Camera live view is in beta. To enable it for your workspace, contact Seam support. + */ + get '/cameras/live_views/stop'(): ( + parameters: CamerasLiveViewsStopParameters, + options?: CamerasLiveViewsStopOptions, + ) => CamerasLiveViewsStopRequest { + const { client, defaults } = this + return function camerasLiveViewsStop( + ...args: Parameters + ): ReturnType { + const seam = SeamHttpCamerasLiveViews.fromClient(client, defaults) + return seam.stop(...args) + } + } + /** * Creates a new [client session](https://www.seam.co/docs/core-concepts/authentication/client-session-tokens). */ @@ -3026,6 +3101,22 @@ export class SeamHttpEndpoints { } } + /** + * Returns a specified piece of media, such as a video clip or thumbnail image captured for a camera event, with a short-lived URL from which you can download it. Camera events list their media in `media_ids`. This endpoint is in beta. + */ + get '/media/get'(): ( + parameters: MediaGetParameters, + options?: MediaGetOptions, + ) => MediaGetRequest { + const { client, defaults } = this + return function mediaGet( + ...args: Parameters + ): ReturnType { + const seam = SeamHttpMedia.fromClient(client, defaults) + return seam.get(...args) + } + } + /** * Returns a list of all [noise sensors](https://www.seam.co/docs/capability-guides/noise-sensors). */ @@ -3425,6 +3516,22 @@ export class SeamHttpEndpoints { } } + /** + * Returns a specified [thermostat](https://www.seam.co/docs/capability-guides/thermostats) to its [weekly program](https://www.seam.co/docs/capability-guides/thermostats/creating-and-managing-thermostat-programs), the schedule that is configured on the device itself, releasing any hold that Seam has set. Seam stops managing the thermostat's climate until you [activate a climate preset](https://www.seam.co/docs/capability-guides/thermostats/configure-current-climate-settings), make a direct climate setting change, or one of the thermostat's [scheduled](https://www.seam.co/docs/capability-guides/thermostats/creating-and-managing-thermostat-schedules) presets reaches its activation time. + */ + get '/thermostats/activate_weekly_program'(): ( + parameters: ThermostatsActivateWeeklyProgramParameters, + options?: ThermostatsActivateWeeklyProgramOptions, + ) => ThermostatsActivateWeeklyProgramRequest { + const { client, defaults } = this + return function thermostatsActivateWeeklyProgram( + ...args: Parameters + ): ReturnType { + const seam = SeamHttpThermostats.fromClient(client, defaults) + return seam.activateWeeklyProgram(...args) + } + } + /** * Sets a specified [thermostat](https://www.seam.co/docs/capability-guides/thermostats) to [cool mode](https://www.seam.co/docs/capability-guides/thermostats/configure-current-climate-settings). */ @@ -4307,6 +4414,7 @@ export type SeamHttpEndpointQueryPaths = | '/instant_keys/list' | '/locks/get' | '/locks/list' + | '/media/get' | '/noise_sensors/list' | '/noise_sensors/noise_thresholds/get' | '/noise_sensors/noise_thresholds/list' @@ -4397,6 +4505,9 @@ export type SeamHttpEndpointMutationPaths = | '/acs/users/suspend' | '/acs/users/unsuspend' | '/acs/users/update' + | '/cameras/live_views/create' + | '/cameras/live_views/offer' + | '/cameras/live_views/stop' | '/client_sessions/create' | '/client_sessions/delete' | '/client_sessions/get_or_create' @@ -4442,6 +4553,7 @@ export type SeamHttpEndpointMutationPaths = | '/spaces/remove_devices' | '/spaces/update' | '/thermostats/activate_climate_preset' + | '/thermostats/activate_weekly_program' | '/thermostats/cool' | '/thermostats/create_climate_preset' | '/thermostats/delete_climate_preset' diff --git a/src/lib/routes/seam-http.ts b/src/lib/routes/seam-http.ts index d1ef72d6..ca627af7 100644 --- a/src/lib/routes/seam-http.ts +++ b/src/lib/routes/seam-http.ts @@ -36,6 +36,7 @@ import { SeamHttpAccessGrants } from './access-grants/index.js' import { SeamHttpAccessMethods } from './access-methods/index.js' import { SeamHttpAcs } from './acs/index.js' import { SeamHttpActionAttempts } from './action-attempts/index.js' +import { SeamHttpCameras } from './cameras/index.js' import { SeamHttpClientSessions } from './client-sessions/index.js' import { SeamHttpConnectWebviews } from './connect-webviews/index.js' import { SeamHttpConnectedAccounts } from './connected-accounts/index.js' @@ -44,6 +45,7 @@ import { SeamHttpDevices } from './devices/index.js' import { SeamHttpEvents } from './events/index.js' import { SeamHttpInstantKeys } from './instant-keys/index.js' import { SeamHttpLocks } from './locks/index.js' +import { SeamHttpMedia } from './media/index.js' import { SeamHttpNoiseSensors } from './noise-sensors/index.js' import { SeamHttpPhones } from './phones/index.js' import { SeamHttpSpaces } from './spaces/index.js' @@ -197,6 +199,10 @@ export class SeamHttp { return SeamHttpActionAttempts.fromClient(this.client, this.defaults) } + get cameras(): SeamHttpCameras { + return SeamHttpCameras.fromClient(this.client, this.defaults) + } + get clientSessions(): SeamHttpClientSessions { return SeamHttpClientSessions.fromClient(this.client, this.defaults) } @@ -229,6 +235,10 @@ export class SeamHttp { return SeamHttpLocks.fromClient(this.client, this.defaults) } + get media(): SeamHttpMedia { + return SeamHttpMedia.fromClient(this.client, this.defaults) + } + get noiseSensors(): SeamHttpNoiseSensors { return SeamHttpNoiseSensors.fromClient(this.client, this.defaults) } diff --git a/src/lib/routes/thermostats/thermostats.ts b/src/lib/routes/thermostats/thermostats.ts index dd35b08c..4fb04446 100644 --- a/src/lib/routes/thermostats/thermostats.ts +++ b/src/lib/routes/thermostats/thermostats.ts @@ -207,6 +207,31 @@ export class SeamHttpThermostats { }) } + /** + * Returns a specified [thermostat](https://www.seam.co/docs/capability-guides/thermostats) to its [weekly program](https://www.seam.co/docs/capability-guides/thermostats/creating-and-managing-thermostat-programs), the schedule that is configured on the device itself, releasing any hold that Seam has set. Seam stops managing the thermostat's climate until you [activate a climate preset](https://www.seam.co/docs/capability-guides/thermostats/configure-current-climate-settings), make a direct climate setting change, or one of the thermostat's [scheduled](https://www.seam.co/docs/capability-guides/thermostats/creating-and-managing-thermostat-schedules) presets reaches its activation time. + */ + activateWeeklyProgram( + parameters: ThermostatsActivateWeeklyProgramParameters, + options: ThermostatsActivateWeeklyProgramOptions = {}, + ): ThermostatsActivateWeeklyProgramRequest { + return new SeamHttpRequest(this, { + pathname: '/thermostats/activate_weekly_program', + method: 'POST', + body: parameters, + parameters, + hasRequiredParameters: true, + requiredParameterNames: ['device_id'], + atLeastOneParameterNames: [], + responseKey: 'action_attempt', + options, + actionAttempts: () => + SeamHttpActionAttempts.fromClient(this.client, { + ...this.defaults, + waitForActionAttempt: false, + }), + }) + } + /** * Sets a specified [thermostat](https://www.seam.co/docs/capability-guides/thermostats) to [cool mode](https://www.seam.co/docs/capability-guides/thermostats/configure-current-climate-settings). */ @@ -532,6 +557,30 @@ export type ThermostatsActivateClimatePresetOptions = Pick< 'waitForActionAttempt' > +export type ThermostatsActivateWeeklyProgramParameters = { + /** + * ID of the thermostat device that you want to return to its weekly program. + */ + device_id: string +} + +/** + * @deprecated Use ThermostatsActivateWeeklyProgramRequest instead. + */ +export type ThermostatsActivateWeeklyProgramResponse = { + action_attempt: ActionAttempt +} + +export type ThermostatsActivateWeeklyProgramRequest = SeamHttpRequest< + ThermostatsActivateWeeklyProgramResponse, + 'action_attempt' +> + +export type ThermostatsActivateWeeklyProgramOptions = Pick< + SeamHttpRequestOptions, + 'waitForActionAttempt' +> + export type ThermostatsCoolParameters = { /** * [Cooling set point](https://www.seam.co/docs/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.