fireboard-api-client 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +276 -2
- package/dist/client.d.ts +198 -0
- package/dist/client.js +68 -0
- package/dist/device-cache.d.ts +11 -0
- package/dist/device-cache.js +40 -0
- package/dist/drive.d.ts +10 -0
- package/dist/drive.js +53 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +10 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/login.d.ts +6 -0
- package/dist/login.js +19 -0
- package/dist/request.d.ts +6 -0
- package/dist/request.js +56 -0
- package/dist/schemas.d.ts +212 -0
- package/dist/schemas.js +276 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.js +1 -0
- package/dist/validation.d.ts +4 -0
- package/dist/validation.js +29 -0
- package/package.json +55 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 premium-bonds-client contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,277 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Fireboard API Client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An unofficial TypeScript client for the [Fireboard](https://fireboard.io) temperature monitoring API. Read devices, temperatures, and cook sessions, or control a Drive fan with typed methods.
|
|
4
|
+
|
|
5
|
+
Requires Node.js 22 or newer. ESM only.
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install fireboard-api-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Obtain an API token with `login`:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { FireboardClient, login } from "fireboard-api-client";
|
|
17
|
+
|
|
18
|
+
const token = await login({
|
|
19
|
+
username: process.env.FIREBOARD_USERNAME!,
|
|
20
|
+
password: process.env.FIREBOARD_PASSWORD!,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const fireboard = new FireboardClient({ token });
|
|
24
|
+
const devices = await fireboard.getDevices();
|
|
25
|
+
|
|
26
|
+
for (const device of devices) {
|
|
27
|
+
console.log(device.uuid, device.title, device.channels);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
If you already have a token, pass it directly to `new FireboardClient({ token })`. Reuse one client per account: device lists are cached for two minutes by default. The client supplies Fireboard’s required User-Agent header and does not store login credentials.
|
|
32
|
+
|
|
33
|
+
API reference: [Fireboard API](https://docs.fireboard.io/app/app-api/).
|
|
34
|
+
|
|
35
|
+
## Methods
|
|
36
|
+
|
|
37
|
+
Read and write methods are called on `FireboardClient`. `login` is a standalone function. Read methods return promises of the listed types.
|
|
38
|
+
|
|
39
|
+
### Read methods
|
|
40
|
+
|
|
41
|
+
| Method | What it does | Returns |
|
|
42
|
+
| ------------------------------------------- | ------------------------------------------------------------------------------- | ----------------------- |
|
|
43
|
+
| `getDevices()` | Lists the account’s devices with channels, temperatures, and available metadata | `RawDevice[]` |
|
|
44
|
+
| `getDevicesWithCache()` | Lists devices with cache freshness metadata | `DeviceListResult` |
|
|
45
|
+
| `getDevice(deviceUuid)` | Retrieves detailed information for one device | `RawDevice` |
|
|
46
|
+
| `getDeviceTemperatures(deviceUuid)` | Retrieves the latest available readings | `RawDeviceTemperatures` |
|
|
47
|
+
| `getDriveLog(deviceUuid)` | Retrieves a recent Drive log or an empty object | `RawDriveLogResponse` |
|
|
48
|
+
| `getSessions()` | Lists current and historical account sessions | `RawSessionSummary[]` |
|
|
49
|
+
| `getSessionDetail(sessionId)` | Retrieves a session’s metadata, devices, and available notes | `RawSessionDetail` |
|
|
50
|
+
| `getSessionChart(sessionId, includeDrive?)` | Retrieves temperature time-series, optionally including Drive data | `RawChartChannel[]` |
|
|
51
|
+
|
|
52
|
+
### Write methods
|
|
53
|
+
|
|
54
|
+
| Method | What it changes |
|
|
55
|
+
| --------------------------------------- | ------------------------------------------------------- |
|
|
56
|
+
| `setDriveSetpoint(deviceUuid, options)` | Auto-mode temperature target and/or PID control channel |
|
|
57
|
+
| `setDriveSpeed(deviceUuid, percent)` | Manual fan speed, from 0 to 100 percent |
|
|
58
|
+
| `setDriveOff(deviceUuid)` | Clears the target and stops the fan |
|
|
59
|
+
|
|
60
|
+
Writes return `Promise<void>` and are never automatically retried. Drive control uses a write API absent from Fireboard’s public reference.
|
|
61
|
+
|
|
62
|
+
### `login(credentials, options?)`
|
|
63
|
+
|
|
64
|
+
Pass `{ username, password }`; `username` can be a Fireboard username or email address. Returns the API token.
|
|
65
|
+
|
|
66
|
+
Options are `loginUrl`, `timeoutMs`, `fetch`, and `userAgent`. Store the returned token in your application if needed.
|
|
67
|
+
|
|
68
|
+
### `getDevices()`
|
|
69
|
+
|
|
70
|
+
Returns an array of devices, cached for two minutes by default. Pass a device’s `uuid`, not its numeric `id`, to device reads and Drive controls.
|
|
71
|
+
|
|
72
|
+
### `getDevicesWithCache()`
|
|
73
|
+
|
|
74
|
+
Returns `{ data, fromCache, cacheAgeSeconds }`, where `data` is `RawDevice[]`. Shares the cache with `getDevices()`. Newly fetched responses have `fromCache: false` and age `0`; cached responses report their rounded age in seconds. This age describes the cached response, not the readings within it.
|
|
75
|
+
|
|
76
|
+
### `getDevice(deviceUuid)`
|
|
77
|
+
|
|
78
|
+
Returns the device and its available channel metadata without using the device-list cache.
|
|
79
|
+
|
|
80
|
+
### `getDeviceTemperatures(deviceUuid)`
|
|
81
|
+
|
|
82
|
+
Retrieves the latest available channel temperatures. Fireboard documents a one-minute freshness cutoff, but live checks returned older data after power-off. Check reading timestamps before treating values as current.
|
|
83
|
+
|
|
84
|
+
Fireboard does not document this response’s JSON shape. `RawDeviceTemperatures` preserves the returned JSON; narrow the result before accessing its fields.
|
|
85
|
+
|
|
86
|
+
### `getDriveLog(deviceUuid)`
|
|
87
|
+
|
|
88
|
+
Returns a recent Drive log with `driveper`, `setpoint`, `modetype`, `tiedchannel`, `degreetype`, and `created` fields, or `{}` when no recent log is available.
|
|
89
|
+
|
|
90
|
+
Check for an empty object before accessing log fields. Incomplete non-empty logs and other response shapes raise `INVALID_RESPONSE`.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const drive = await fireboard.getDriveLog("your-device-uuid");
|
|
94
|
+
|
|
95
|
+
if (Object.keys(drive).length === 0) {
|
|
96
|
+
console.log("No recent Drive data");
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `getSessions()`
|
|
101
|
+
|
|
102
|
+
Returns account session summaries. Ordering is not guaranteed; sort or filter results as needed.
|
|
103
|
+
|
|
104
|
+
Use each summary’s numeric `id` for session-detail and chart requests. `device_ids` is optional.
|
|
105
|
+
|
|
106
|
+
### `getSessionDetail(sessionId)`
|
|
107
|
+
|
|
108
|
+
Pass a non-negative integer session ID. Returns metadata, linked devices, and cook notes; omitted notes become `[]`.
|
|
109
|
+
|
|
110
|
+
### `getSessionChart(sessionId, includeDrive = false)`
|
|
111
|
+
|
|
112
|
+
Returns an array of channel series. Each includes `x` Unix timestamps in seconds, `y` temperature values or Drive percentages, `device`, `label`, and `degreetype`. `channel_id` is optional; numeric identifiers become strings.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const temperatures = await fireboard.getSessionChart(42);
|
|
116
|
+
const temperaturesAndDrive = await fireboard.getSessionChart(42, true);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `setDriveSetpoint(deviceUuid, options)`
|
|
120
|
+
|
|
121
|
+
Pass a finite numeric `setpoint`, a positive integer `channel`, or both. At least one is required, and all arguments are validated before any write is sent.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
await fireboard.setDriveSetpoint("your-device-uuid", { setpoint: 110 });
|
|
125
|
+
await fireboard.setDriveSetpoint("your-device-uuid", { channel: 2 });
|
|
126
|
+
await fireboard.setDriveSetpoint("your-device-uuid", { setpoint: 110, channel: 2 });
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
A setpoint switches Drive to automatic control. Its temperature must use the device’s configured unit; the client does not convert Celsius/Fahrenheit. A channel-only call changes the PID control probe without setting a new target.
|
|
130
|
+
|
|
131
|
+
Changes to the target and control channel are not atomic. If the method fails, one change may already have been applied.
|
|
132
|
+
|
|
133
|
+
### `setDriveSpeed(deviceUuid, percent)`
|
|
134
|
+
|
|
135
|
+
Pass a finite percentage between `0` and `100`. This puts Drive in manual mode and overrides the active automatic target.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
await fireboard.setDriveSpeed("your-device-uuid", 50);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
A value of `0` stops the fan. Use `setDriveOff()` to clear the setpoint as well.
|
|
142
|
+
|
|
143
|
+
### `setDriveOff(deviceUuid)`
|
|
144
|
+
|
|
145
|
+
Clears the setpoint by setting it to zero and stops the Drive fan.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
await fireboard.setDriveOff("your-device-uuid");
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Response validation
|
|
152
|
+
|
|
153
|
+
Responses retain Fireboard’s field names and extra fields, including nested fields. Zod validates known fields; extra fields are preserved without individual validation or specific TypeScript types. Missing alerts and notes become empty arrays, and numeric chart `channel_id` values become strings.
|
|
154
|
+
|
|
155
|
+
Use each reading or chart series’ `degreetype` to interpret temperatures: `1` is Celsius and `2` is Fahrenheit. Device-level unit metadata may differ from the reading’s unit. Channels remain listed when probes are unplugged, but reading fields may be omitted.
|
|
156
|
+
|
|
157
|
+
The exported TypeScript types describe the client’s validated fields, including observed fields not covered by Fireboard’s public reference.
|
|
158
|
+
|
|
159
|
+
## Options
|
|
160
|
+
|
|
161
|
+
`new FireboardClient({ token, baseUrl?, deviceCacheTtlMs?, timeoutMs?, fetch?, userAgent? })`
|
|
162
|
+
|
|
163
|
+
- `baseUrl`: defaults to `https://fireboard.io/api/v1`.
|
|
164
|
+
- `deviceCacheTtlMs`: device-list cache TTL; defaults to 120,000 ms. Set `0` to disable caching and shared in-flight reads.
|
|
165
|
+
- `timeoutMs`: defaults to 15,000 ms and covers fetching the response body.
|
|
166
|
+
- `fetch`: injectable for testing or custom transports.
|
|
167
|
+
- `userAgent`: optional application identification; defaults to `fireboard-api-client/<version>`.
|
|
168
|
+
|
|
169
|
+
`login` accepts `loginUrl`, `timeoutMs`, `fetch`, and `userAgent` separately. URL overrides must use HTTP(S), without credentials, query strings, or fragments. Redirects are rejected to avoid forwarding credentials.
|
|
170
|
+
|
|
171
|
+
## Caching and rate limits
|
|
172
|
+
|
|
173
|
+
Only `getDevices()` and `getDevicesWithCache()` are cached. Each client has its own cache, and concurrent cache misses share a request. The TTL starts when a valid response arrives; failures are never cached. Returned data is copied so caller mutations do not affect the cache.
|
|
174
|
+
|
|
175
|
+
Other reads and all writes are uncached. Writes do not invalidate cached device data. Nested temperature and Drive readings can remain stale until expiry; use `deviceCacheTtlMs: 0` for fresh device-list reads.
|
|
176
|
+
|
|
177
|
+
The client does not schedule requests or retry failures. Consuming applications remain responsible for rate-limit mitigation.
|
|
178
|
+
|
|
179
|
+
Fireboard limits each user to **17 API calls within any five-minute period**. After a rate-limit error, pause requests: continued calls can extend the block. Access generally resumes after approximately five minutes, once the request rate falls below the threshold.
|
|
180
|
+
|
|
181
|
+
## Errors
|
|
182
|
+
|
|
183
|
+
API and transport failures throw `FireboardError`, with a `code` and an optional HTTP `status`. Invalid arguments throw `TypeError`.
|
|
184
|
+
|
|
185
|
+
| Code | Meaning |
|
|
186
|
+
| ------------------ | ----------------------------------------------------------------------- |
|
|
187
|
+
| `AUTHENTICATION` | Fireboard rejected the token or login credentials |
|
|
188
|
+
| `RATE_LIMIT` | Fireboard returned HTTP 429; stop making requests until access recovers |
|
|
189
|
+
| `API_UNAVAILABLE` | Fireboard returned another unsuccessful HTTP response |
|
|
190
|
+
| `INVALID_RESPONSE` | Response JSON could not be parsed or did not match the expected model |
|
|
191
|
+
| `TIMEOUT` | The request or response-body read exceeded `timeoutMs` |
|
|
192
|
+
| `NETWORK` | The transport failed, including rejected redirects |
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { FireboardError } from "fireboard-api-client";
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
await fireboard.getSessions();
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (error instanceof FireboardError) {
|
|
201
|
+
console.error(error.code, error.status, error.message);
|
|
202
|
+
} else {
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Errors omit response bodies and credentials.
|
|
209
|
+
|
|
210
|
+
## Other public exports
|
|
211
|
+
|
|
212
|
+
All public exports are imported from `"fireboard-api-client"`; no internal file paths are needed.
|
|
213
|
+
|
|
214
|
+
### TypeScript types
|
|
215
|
+
|
|
216
|
+
| Type | Describes |
|
|
217
|
+
| ------------------------ | ---------------------------------------------------------------------- |
|
|
218
|
+
| `FireboardClientOptions` | Constructor options, including the required token |
|
|
219
|
+
| `RequestOptions` | Shared transport options: `fetch`, `timeoutMs`, and `userAgent` |
|
|
220
|
+
| `LoginOptions` | Login transport options and optional `loginUrl` |
|
|
221
|
+
| `DeviceListResult` | Device-list data and cache freshness metadata |
|
|
222
|
+
| `DriveSetpointOptions` | A setpoint, a control channel, or both for `setDriveSetpoint` |
|
|
223
|
+
| `FireboardErrorCode` | The union of error codes listed above |
|
|
224
|
+
| `RawDevice` | One parsed device and its available metadata/channels |
|
|
225
|
+
| `RawDeviceTemperatures` | JSON returned by the temperature read, without an assumed field schema |
|
|
226
|
+
| `RawAlert` | An observed channel-alert model |
|
|
227
|
+
| `RawDriveLogResponse` | A populated `RawDriveLog` or an empty object |
|
|
228
|
+
| `RawDriveLog` | An observed Drive-log model |
|
|
229
|
+
| `RawSessionSummary` | A session-list entry |
|
|
230
|
+
| `RawSessionDetail` | Detailed session metadata, devices, and notes |
|
|
231
|
+
| `RawChartChannel` | One channel’s chart series |
|
|
232
|
+
|
|
233
|
+
### Default constants
|
|
234
|
+
|
|
235
|
+
| Constant | Value |
|
|
236
|
+
| ----------------------------- | ------------------------------------------- |
|
|
237
|
+
| `DEFAULT_BASE_URL` | `https://fireboard.io/api/v1` |
|
|
238
|
+
| `DEFAULT_LOGIN_URL` | `https://fireboard.io/api/rest-auth/login/` |
|
|
239
|
+
| `DEFAULT_DEVICE_CACHE_TTL_MS` | `120_000` |
|
|
240
|
+
| `DEFAULT_TIMEOUT_MS` | `15_000` |
|
|
241
|
+
|
|
242
|
+
### Zod validators
|
|
243
|
+
|
|
244
|
+
The client already validates responses. These exports are also available if you need to validate saved or externally fetched data:
|
|
245
|
+
|
|
246
|
+
| Validator | Accepts |
|
|
247
|
+
| ---------------------------------- | ------------------------------------------------------- |
|
|
248
|
+
| `deviceSchema` | One device |
|
|
249
|
+
| `devicesResponseSchema` | An array of devices |
|
|
250
|
+
| `deviceTemperaturesResponseSchema` | JSON temperature data without an assumed response shape |
|
|
251
|
+
| `driveLogResponseSchema` | A populated Drive log or an empty object |
|
|
252
|
+
| `sessionsResponseSchema` | An array of session summaries |
|
|
253
|
+
| `sessionDetailSchema` | One detailed session |
|
|
254
|
+
| `chartChannelSchema` | One chart channel |
|
|
255
|
+
| `chartResponseSchema` | An array of chart channels |
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { devicesResponseSchema } from "fireboard-api-client";
|
|
259
|
+
|
|
260
|
+
const devices = devicesResponseSchema.parse(savedDeviceData);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Development and releases
|
|
264
|
+
|
|
265
|
+
```sh
|
|
266
|
+
npm ci
|
|
267
|
+
npm run typecheck
|
|
268
|
+
npm run lint
|
|
269
|
+
npm run format:check
|
|
270
|
+
npm test
|
|
271
|
+
npm run smoke
|
|
272
|
+
npm pack
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The package contains compiled JavaScript and declaration files under `dist/`. `prepack` builds these automatically. Tests and smoke checks use synthetic responses and make no Fireboard account calls.
|
|
276
|
+
|
|
277
|
+
No Fireboard credentials are needed for local tests or CI. Release configuration is maintained in `.github/workflows/`.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { DeviceListResult, DriveSetpointOptions, FireboardClientOptions } from "./types.js";
|
|
3
|
+
export declare const DEFAULT_BASE_URL = "https://fireboard.io/api/v1";
|
|
4
|
+
export declare class FireboardClient {
|
|
5
|
+
#private;
|
|
6
|
+
private readonly baseUrl;
|
|
7
|
+
private readonly drive;
|
|
8
|
+
private readonly deviceCache;
|
|
9
|
+
constructor(options: FireboardClientOptions);
|
|
10
|
+
private get;
|
|
11
|
+
/** Lists account devices using the per-client device cache. */
|
|
12
|
+
getDevices(): Promise<{
|
|
13
|
+
[x: string]: unknown;
|
|
14
|
+
uuid: string;
|
|
15
|
+
id: number;
|
|
16
|
+
title: string;
|
|
17
|
+
channels: {
|
|
18
|
+
[x: string]: unknown;
|
|
19
|
+
id: number;
|
|
20
|
+
channel: number;
|
|
21
|
+
channel_label: string;
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
alerts: {
|
|
24
|
+
[x: string]: unknown;
|
|
25
|
+
id: number;
|
|
26
|
+
temp_min: number;
|
|
27
|
+
temp_max: number;
|
|
28
|
+
notify_app: boolean;
|
|
29
|
+
notify_email: boolean;
|
|
30
|
+
notify_sms: boolean;
|
|
31
|
+
time_start: string;
|
|
32
|
+
time_stop: string;
|
|
33
|
+
minutes_buffer: number;
|
|
34
|
+
minutes_repeat: number;
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
created: string;
|
|
37
|
+
channel: number;
|
|
38
|
+
device_id: number;
|
|
39
|
+
session_id: number;
|
|
40
|
+
}[];
|
|
41
|
+
current_temp?: number | undefined;
|
|
42
|
+
degreetype?: number | null | undefined;
|
|
43
|
+
last_templog?: {
|
|
44
|
+
[x: string]: unknown;
|
|
45
|
+
temp: number;
|
|
46
|
+
created: string;
|
|
47
|
+
degreetype?: number | null | undefined;
|
|
48
|
+
} | null | undefined;
|
|
49
|
+
}[];
|
|
50
|
+
channel_count?: number | undefined;
|
|
51
|
+
last_drivelog?: {
|
|
52
|
+
[x: string]: unknown;
|
|
53
|
+
driveper: number;
|
|
54
|
+
setpoint: number;
|
|
55
|
+
modetype: string | number;
|
|
56
|
+
tiedchannel: number;
|
|
57
|
+
degreetype: number;
|
|
58
|
+
created: string;
|
|
59
|
+
} | null | undefined;
|
|
60
|
+
active?: boolean | undefined;
|
|
61
|
+
last_battery_reading?: number | null | undefined;
|
|
62
|
+
model_name?: string | undefined;
|
|
63
|
+
created?: string | undefined;
|
|
64
|
+
hardware_id?: string | undefined;
|
|
65
|
+
latest_temps?: unknown[] | undefined;
|
|
66
|
+
device_log?: Record<string, unknown> | null | undefined;
|
|
67
|
+
last_templog?: string | null | undefined;
|
|
68
|
+
}[]>;
|
|
69
|
+
/** Lists devices with cache freshness metadata. */
|
|
70
|
+
getDevicesWithCache(): Promise<DeviceListResult>;
|
|
71
|
+
/** Retrieves one device and its available metadata. */
|
|
72
|
+
getDevice(uuid: string): Promise<{
|
|
73
|
+
[x: string]: unknown;
|
|
74
|
+
uuid: string;
|
|
75
|
+
id: number;
|
|
76
|
+
title: string;
|
|
77
|
+
channels: {
|
|
78
|
+
[x: string]: unknown;
|
|
79
|
+
id: number;
|
|
80
|
+
channel: number;
|
|
81
|
+
channel_label: string;
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
alerts: {
|
|
84
|
+
[x: string]: unknown;
|
|
85
|
+
id: number;
|
|
86
|
+
temp_min: number;
|
|
87
|
+
temp_max: number;
|
|
88
|
+
notify_app: boolean;
|
|
89
|
+
notify_email: boolean;
|
|
90
|
+
notify_sms: boolean;
|
|
91
|
+
time_start: string;
|
|
92
|
+
time_stop: string;
|
|
93
|
+
minutes_buffer: number;
|
|
94
|
+
minutes_repeat: number;
|
|
95
|
+
enabled: boolean;
|
|
96
|
+
created: string;
|
|
97
|
+
channel: number;
|
|
98
|
+
device_id: number;
|
|
99
|
+
session_id: number;
|
|
100
|
+
}[];
|
|
101
|
+
current_temp?: number | undefined;
|
|
102
|
+
degreetype?: number | null | undefined;
|
|
103
|
+
last_templog?: {
|
|
104
|
+
[x: string]: unknown;
|
|
105
|
+
temp: number;
|
|
106
|
+
created: string;
|
|
107
|
+
degreetype?: number | null | undefined;
|
|
108
|
+
} | null | undefined;
|
|
109
|
+
}[];
|
|
110
|
+
channel_count?: number | undefined;
|
|
111
|
+
last_drivelog?: {
|
|
112
|
+
[x: string]: unknown;
|
|
113
|
+
driveper: number;
|
|
114
|
+
setpoint: number;
|
|
115
|
+
modetype: string | number;
|
|
116
|
+
tiedchannel: number;
|
|
117
|
+
degreetype: number;
|
|
118
|
+
created: string;
|
|
119
|
+
} | null | undefined;
|
|
120
|
+
active?: boolean | undefined;
|
|
121
|
+
last_battery_reading?: number | null | undefined;
|
|
122
|
+
model_name?: string | undefined;
|
|
123
|
+
created?: string | undefined;
|
|
124
|
+
hardware_id?: string | undefined;
|
|
125
|
+
latest_temps?: unknown[] | undefined;
|
|
126
|
+
device_log?: Record<string, unknown> | null | undefined;
|
|
127
|
+
last_templog?: string | null | undefined;
|
|
128
|
+
}>;
|
|
129
|
+
/** Retrieves recent temperatures; the official reference does not define their JSON fields. */
|
|
130
|
+
getDeviceTemperatures(uuid: string): Promise<z.core.util.JSONType>;
|
|
131
|
+
/** Returns a recent Drive log; Fireboard only returns log data less than a minute old. */
|
|
132
|
+
getDriveLog(uuid: string): Promise<{
|
|
133
|
+
[x: string]: unknown;
|
|
134
|
+
driveper: number;
|
|
135
|
+
setpoint: number;
|
|
136
|
+
modetype: string | number;
|
|
137
|
+
tiedchannel: number;
|
|
138
|
+
degreetype: number;
|
|
139
|
+
created: string;
|
|
140
|
+
} | Record<string, never>>;
|
|
141
|
+
/** Lists account sessions; the API does not document their ordering. */
|
|
142
|
+
getSessions(): Promise<{
|
|
143
|
+
[x: string]: unknown;
|
|
144
|
+
id: number;
|
|
145
|
+
title: string;
|
|
146
|
+
description: string;
|
|
147
|
+
start_time: string;
|
|
148
|
+
end_time: string | null;
|
|
149
|
+
device_ids?: string[] | undefined;
|
|
150
|
+
devices?: unknown[] | undefined;
|
|
151
|
+
duration?: string | undefined;
|
|
152
|
+
created?: string | undefined;
|
|
153
|
+
owner?: Record<string, unknown> | undefined;
|
|
154
|
+
}[]>;
|
|
155
|
+
getSessionDetail(id: number): Promise<{
|
|
156
|
+
[x: string]: unknown;
|
|
157
|
+
id: number;
|
|
158
|
+
title: string;
|
|
159
|
+
description: string;
|
|
160
|
+
start_time: string;
|
|
161
|
+
end_time: string | null;
|
|
162
|
+
devices: {
|
|
163
|
+
[x: string]: unknown;
|
|
164
|
+
uuid: string;
|
|
165
|
+
title: string;
|
|
166
|
+
channels: {
|
|
167
|
+
[x: string]: unknown;
|
|
168
|
+
channel_label: string;
|
|
169
|
+
channel: number;
|
|
170
|
+
}[];
|
|
171
|
+
}[];
|
|
172
|
+
notes: {
|
|
173
|
+
[x: string]: unknown;
|
|
174
|
+
note_time: string;
|
|
175
|
+
note_text: string;
|
|
176
|
+
channel: number | null;
|
|
177
|
+
device: string;
|
|
178
|
+
}[];
|
|
179
|
+
duration?: string | undefined;
|
|
180
|
+
created?: string | undefined;
|
|
181
|
+
owner?: Record<string, unknown> | undefined;
|
|
182
|
+
}>;
|
|
183
|
+
getSessionChart(id: number, includeDrive?: boolean): Promise<{
|
|
184
|
+
[x: string]: unknown;
|
|
185
|
+
device: string;
|
|
186
|
+
label: string;
|
|
187
|
+
degreetype: number;
|
|
188
|
+
x: number[];
|
|
189
|
+
y: number[];
|
|
190
|
+
channel_id?: string | undefined;
|
|
191
|
+
}[]>;
|
|
192
|
+
/** Sets an auto-mode target in the device's temperature unit and/or its control channel. */
|
|
193
|
+
setDriveSetpoint(uuid: string, options: DriveSetpointOptions): Promise<void>;
|
|
194
|
+
/** Sets a manual fan speed from 0 to 100 percent. */
|
|
195
|
+
setDriveSpeed(uuid: string, percent: number): Promise<void>;
|
|
196
|
+
/** Clears the setpoint and turns the Drive fan off. */
|
|
197
|
+
setDriveOff(uuid: string): Promise<void>;
|
|
198
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { DeviceCache, DEFAULT_DEVICE_CACHE_TTL_MS } from "./device-cache.js";
|
|
2
|
+
import { DriveControls } from "./drive.js";
|
|
3
|
+
import { decode, headers, request, DEFAULT_TIMEOUT_MS } from "./request.js";
|
|
4
|
+
import { chartResponseSchema, devicesResponseSchema, deviceSchema, deviceTemperaturesResponseSchema, driveLogResponseSchema, sessionDetailSchema, sessionsResponseSchema, } from "./schemas.js";
|
|
5
|
+
import { encodeDeviceId, validateEndpoint, validateSessionId, validateTiming, } from "./validation.js";
|
|
6
|
+
export const DEFAULT_BASE_URL = "https://fireboard.io/api/v1";
|
|
7
|
+
export class FireboardClient {
|
|
8
|
+
#options;
|
|
9
|
+
baseUrl;
|
|
10
|
+
drive;
|
|
11
|
+
deviceCache;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
if (typeof options?.token !== "string" || !options.token.trim()) {
|
|
14
|
+
throw new TypeError("Fireboard token is required");
|
|
15
|
+
}
|
|
16
|
+
validateTiming(options.timeoutMs ?? DEFAULT_TIMEOUT_MS, "timeoutMs");
|
|
17
|
+
this.deviceCache = new DeviceCache(validateTiming(options.deviceCacheTtlMs ?? DEFAULT_DEVICE_CACHE_TTL_MS, "deviceCacheTtlMs", true));
|
|
18
|
+
this.baseUrl = validateEndpoint(options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
19
|
+
this.#options = { ...options };
|
|
20
|
+
this.drive = new DriveControls(this.baseUrl, this.#options);
|
|
21
|
+
}
|
|
22
|
+
async get(path, schema) {
|
|
23
|
+
const response = await request(`${this.baseUrl}${path}`, { headers: headers(this.#options, this.#options.token) }, this.#options);
|
|
24
|
+
return decode(response, schema);
|
|
25
|
+
}
|
|
26
|
+
/** Lists account devices using the per-client device cache. */
|
|
27
|
+
async getDevices() {
|
|
28
|
+
return (await this.getDevicesWithCache()).data;
|
|
29
|
+
}
|
|
30
|
+
/** Lists devices with cache freshness metadata. */
|
|
31
|
+
getDevicesWithCache() {
|
|
32
|
+
return this.deviceCache.get(() => this.get("/devices.json", devicesResponseSchema));
|
|
33
|
+
}
|
|
34
|
+
/** Retrieves one device and its available metadata. */
|
|
35
|
+
getDevice(uuid) {
|
|
36
|
+
return this.get(`/devices/${encodeDeviceId(uuid)}.json`, deviceSchema);
|
|
37
|
+
}
|
|
38
|
+
/** Retrieves recent temperatures; the official reference does not define their JSON fields. */
|
|
39
|
+
getDeviceTemperatures(uuid) {
|
|
40
|
+
return this.get(`/devices/${encodeDeviceId(uuid)}/temps.json`, deviceTemperaturesResponseSchema);
|
|
41
|
+
}
|
|
42
|
+
/** Returns a recent Drive log; Fireboard only returns log data less than a minute old. */
|
|
43
|
+
getDriveLog(uuid) {
|
|
44
|
+
return this.get(`/devices/${encodeDeviceId(uuid)}/drivelog.json`, driveLogResponseSchema);
|
|
45
|
+
}
|
|
46
|
+
/** Lists account sessions; the API does not document their ordering. */
|
|
47
|
+
getSessions() {
|
|
48
|
+
return this.get("/sessions.json", sessionsResponseSchema);
|
|
49
|
+
}
|
|
50
|
+
getSessionDetail(id) {
|
|
51
|
+
return this.get(`/sessions/${validateSessionId(id)}.json`, sessionDetailSchema);
|
|
52
|
+
}
|
|
53
|
+
getSessionChart(id, includeDrive = false) {
|
|
54
|
+
return this.get(`/sessions/${validateSessionId(id)}/chart.json${includeDrive ? "?drive=1" : ""}`, chartResponseSchema);
|
|
55
|
+
}
|
|
56
|
+
/** Sets an auto-mode target in the device's temperature unit and/or its control channel. */
|
|
57
|
+
setDriveSetpoint(uuid, options) {
|
|
58
|
+
return this.drive.setSetpoint(uuid, options);
|
|
59
|
+
}
|
|
60
|
+
/** Sets a manual fan speed from 0 to 100 percent. */
|
|
61
|
+
setDriveSpeed(uuid, percent) {
|
|
62
|
+
return this.drive.setSpeed(uuid, percent);
|
|
63
|
+
}
|
|
64
|
+
/** Clears the setpoint and turns the Drive fan off. */
|
|
65
|
+
setDriveOff(uuid) {
|
|
66
|
+
return this.drive.off(uuid);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RawDevice } from "./schemas.js";
|
|
2
|
+
import type { DeviceListResult } from "./types.js";
|
|
3
|
+
export declare const DEFAULT_DEVICE_CACHE_TTL_MS = 120000;
|
|
4
|
+
export declare class DeviceCache {
|
|
5
|
+
private readonly ttlMs;
|
|
6
|
+
private cached;
|
|
7
|
+
private pending;
|
|
8
|
+
constructor(ttlMs: number);
|
|
9
|
+
get(load: () => Promise<RawDevice[]>): Promise<DeviceListResult>;
|
|
10
|
+
private fetch;
|
|
11
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const DEFAULT_DEVICE_CACHE_TTL_MS = 120_000;
|
|
2
|
+
export class DeviceCache {
|
|
3
|
+
ttlMs;
|
|
4
|
+
cached;
|
|
5
|
+
pending;
|
|
6
|
+
constructor(ttlMs) {
|
|
7
|
+
this.ttlMs = ttlMs;
|
|
8
|
+
}
|
|
9
|
+
async get(load) {
|
|
10
|
+
if (this.ttlMs === 0) {
|
|
11
|
+
return { data: await load(), fromCache: false, cacheAgeSeconds: 0 };
|
|
12
|
+
}
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
if (this.cached && now - this.cached.fetchedAt < this.ttlMs) {
|
|
15
|
+
return {
|
|
16
|
+
data: structuredClone(this.cached.data),
|
|
17
|
+
fromCache: true,
|
|
18
|
+
cacheAgeSeconds: Math.round((now - this.cached.fetchedAt) / 1000),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (!this.pending) {
|
|
22
|
+
this.pending = this.fetch(load);
|
|
23
|
+
}
|
|
24
|
+
const pending = this.pending;
|
|
25
|
+
try {
|
|
26
|
+
const result = await pending;
|
|
27
|
+
return { ...result, data: structuredClone(result.data) };
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
if (this.pending === pending) {
|
|
31
|
+
this.pending = undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async fetch(load) {
|
|
36
|
+
const data = await load();
|
|
37
|
+
this.cached = { data, fetchedAt: Date.now() };
|
|
38
|
+
return { data, fromCache: false, cacheAgeSeconds: 0 };
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/drive.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DriveSetpointOptions, FireboardClientOptions } from "./types.js";
|
|
2
|
+
export declare class DriveControls {
|
|
3
|
+
#private;
|
|
4
|
+
private readonly baseUrl;
|
|
5
|
+
constructor(baseUrl: string, options: FireboardClientOptions);
|
|
6
|
+
private send;
|
|
7
|
+
setSetpoint(uuid: string, options: DriveSetpointOptions): Promise<void>;
|
|
8
|
+
setSpeed(uuid: string, percent: number): Promise<void>;
|
|
9
|
+
off(uuid: string): Promise<void>;
|
|
10
|
+
}
|