File
Blob: worker/src/shared/contracts/robot.ts
| 1 | import { z } from 'zod'; |
| 2 | import { nowPlayingSchema } from './track.ts'; |
| 3 | import { hardwareSchema } from './hardware.ts'; |
| 4 | |
| 5 | const count = z.number().int().nonnegative(); |
| 6 | export const rgbSchema = z.tuple([ |
| 7 | z.number().int().min(0).max(255), |
| 8 | z.number().int().min(0).max(255), |
| 9 | z.number().int().min(0).max(255), |
| 10 | ]); |
| 11 | export const spectrumStatsSchema = z.object({ |
| 12 | source: z.string().max(32), |
| 13 | fftSize: count, |
| 14 | core: count, |
| 15 | sampleRate: count, |
| 16 | frames: count, |
| 17 | errors: count, |
| 18 | inputDropped: count, |
| 19 | outputDropped: count, |
| 20 | meanUs: count, |
| 21 | maxUs: count, |
| 22 | stackFree: count.optional(), |
| 23 | }); |
| 24 | export const telemetrySchema = z.object({ |
| 25 | event: z.literal('telemetry'), |
| 26 | firmware: z.string().max(32), |
| 27 | sequence: count, |
| 28 | uptimeMs: count, |
| 29 | positionMs: count, |
| 30 | durationMs: count.positive(), |
| 31 | playbackRevision: count.max(0xffff_ffff).optional(), |
| 32 | paused: z.boolean(), |
| 33 | rssi: z.number().int().min(-127).max(0).nullable(), |
| 34 | heap: count, |
| 35 | hardware: hardwareSchema.nullish(), |
| 36 | stackFree: count.optional(), |
| 37 | led: rgbSchema, |
| 38 | audioErrors: count, |
| 39 | dataErrors: count, |
| 40 | skippedFrames: count, |
| 41 | spectrum: spectrumStatsSchema.optional(), |
| 42 | music: z |
| 43 | .object({ cacheBytes: count, indexBytes: count, reads: count, maxReadUs: count }) |
| 44 | .optional(), |
| 45 | }); |
| 46 | export const ackSchema = z.object({ |
| 47 | event: z.literal('ack'), |
| 48 | command_id: z.string().max(64), |
| 49 | result: z.number().int(), |
| 50 | led: rgbSchema, |
| 51 | paused: z.boolean(), |
| 52 | }); |
| 53 | export const nowPlayingMessageSchema = z.object({ |
| 54 | event: z.literal('nowPlaying'), |
| 55 | nowPlaying: nowPlayingSchema, |
| 56 | }); |
| 57 | export const robotMessageSchema = z.discriminatedUnion('event', [ |
| 58 | telemetrySchema, |
| 59 | ackSchema, |
| 60 | nowPlayingMessageSchema, |
| 61 | ]); |
| 62 | export const commandSchema = z.union([ |
| 63 | z.object({ led: rgbSchema }).strict(), |
| 64 | z.object({ action: z.enum(['play', 'pause', 'restart', 'next']) }).strict(), |
| 65 | ]); |
| 66 | |
| 67 | export type RGB = z.infer<typeof rgbSchema>; |
| 68 | export type SpectrumStats = z.infer<typeof spectrumStatsSchema>; |
| 69 | export type Telemetry = z.infer<typeof telemetrySchema>; |
| 70 | export type Ack = z.infer<typeof ackSchema>; |
| 71 | export type Command = z.infer<typeof commandSchema>; |
| 72 | export type RobotMessage = z.infer<typeof robotMessageSchema>; |