diff --git a/simulation/acme/src/schemas/capabilities.ts b/simulation/acme/src/schemas/capabilities.ts index b8c64b6..930e1aa 100644 --- a/simulation/acme/src/schemas/capabilities.ts +++ b/simulation/acme/src/schemas/capabilities.ts @@ -366,4 +366,47 @@ export const deviceCapabilityMap: Record< // }, // }, //], + + SECURITY_CAMERA: [ + { + name: 'isRecording', + type: capabilityTypeSchema.enum.POWER, + isReadOnly: true, + }, + { + name: 'motionDetected', + type: capabilityTypeSchema.enum.POWER, + isReadOnly: true, + }, + { + name: 'currentResolution', + type: capabilityTypeSchema.enum.RANGE, + isReadOnly: true, + minValue: 480, + maxValue: 2160, + unit: 'p', + }, + { + name: 'resolution', + type: capabilityTypeSchema.enum.RANGE, + minValue: 480, + maxValue: 2160, + unit: 'p', + }, + ], + + SECURITY_LOCK: [ + { + name: 'lockState', + type: capabilityTypeSchema.enum.POWER, + }, + { + name: 'batteryLevel', + type: capabilityTypeSchema.enum.RANGE, + isReadOnly: true, + minValue: 0, + maxValue: 100, + unit: '%', + }, + ], }; diff --git a/simulation/acme/src/schemas/device.ts b/simulation/acme/src/schemas/device.ts index 5414110..3166e72 100644 --- a/simulation/acme/src/schemas/device.ts +++ b/simulation/acme/src/schemas/device.ts @@ -13,8 +13,12 @@ export const deviceTypeSchema = z.enum([ 'THERMOMETER', 'HUMIDITY_SENSOR', 'POWER_METER', + 'SECURITY_LOCK', + 'SECURITY_CAMERA', ]); + + // Base device schema with required type and id fields const baseDeviceSchema = z.object({ id: z.string(), @@ -24,6 +28,20 @@ const baseDeviceSchema = z.object({ //activeActions: z.record(deviceActionSchema).default({}), }); +export const securityLockSchema = baseDeviceSchema.extend({ + type: z.literal(deviceTypeSchema.enum.SECURITY_LOCK), + locked: z.boolean(), + batteryLevel: z.number().min(0).max(100), +}); + +export const securityCameraSchema = baseDeviceSchema.extend({ + type: z.literal(deviceTypeSchema.enum.SECURITY_CAMERA), + recording: z.boolean(), + resolution: z.enum(['720p', '1080p', '4K']), + batteryLevel: z.number().min(0).max(100).optional(), + isNightVisionEnabled: z.boolean().default(false), + motionDetected: z.boolean().default(false), +}); // bulbs // Device type-specific schemas @@ -131,8 +149,13 @@ export const deviceSchema = z.union([ powerMeterSchema, tempColorBulbSchema, //coffeeMachineSchema, + securityLockSchema, + securityCameraSchema, ]); +export type SecurityLock = z.infer; +export type SecurityCamera = z.infer; + // Types export type DeviceType = z.infer; export type Device = z.infer; @@ -152,6 +175,8 @@ export const deviceSchemaMap: Record> = { [deviceTypeSchema.enum.POWER_METER]: powerMeterSchema, // Using onOffBulbSchema as base, might need powerMeterSchema [deviceTypeSchema.enum.BULB_TEMP_COLOR]: tempColorBulbSchema, // Using limitedColorBulbSchema as it's most similar //[deviceTypeSchema.enum.COFFEE_MACHINE]: coffeeMachineSchema, // Using onOffBulbSchema as base, might need coffeeMachineSchema + [deviceTypeSchema.enum.SECURITY_LOCK]: securityLockSchema, + [deviceTypeSchema.enum.SECURITY_CAMERA]: securityCameraSchema, }; // Specific device types @@ -246,6 +271,21 @@ export const defaultStates: Record = { // connected: true, // pairedApiKeys: [], //}, + + SECURITY_LOCK: { + connected: true, + pairedApiKeys: [], + locked: true, + batteryLevel: 100, + }, + SECURITY_CAMERA: { + connected: true, + pairedApiKeys: [], + recording: false, + resolution: '1080p', + batteryLevel: 100, + isNightVisionEnabled: false, + }, }; // Type guards @@ -271,3 +311,8 @@ export const isAC = (device: Device): device is AC => // device.type === deviceTypeSchema.enum.GARAGE_DOOR; export const isSolarPanel = (device: Device): device is SolarPanel => device.type === deviceTypeSchema.enum.SOLAR_PANEL; + +export const isSecurityLock = (device: Device): device is SecurityLock => + device.type === deviceTypeSchema.enum.SECURITY_LOCK; +export const isSecurityCamera = (device: Device): device is SecurityCamera => + device.type === deviceTypeSchema.enum.SECURITY_CAMERA; diff --git a/simulation/acme/src/services/device-simulation.ts b/simulation/acme/src/services/device-simulation.ts index b5d06f4..67a5da0 100644 --- a/simulation/acme/src/services/device-simulation.ts +++ b/simulation/acme/src/services/device-simulation.ts @@ -8,6 +8,8 @@ import { PowerMeter, TempColorBulb, AC, + SecurityLock, + SecurityCamera, } from '../schemas/device'; export class DeviceSimulator { @@ -27,6 +29,8 @@ export class DeviceSimulator { 'POWER_METER', 'BULB_TEMP_COLOR', 'AC', + 'SECURITY_LOCK', + 'SECURITY_CAMERA', ]; this.simulationActive = false; } @@ -38,6 +42,10 @@ export class DeviceSimulator { return DeviceSimulator.instance; } + private getRandomBoolean(): boolean { + return Math.random() > 0.5; + } + private getRandomFloat(min: number, max: number): number { return Math.random() * (max - min) + min; } @@ -68,6 +76,28 @@ export class DeviceSimulator { ); } + private async simulateSecurityLock(device: SecurityLock) { + const isLocked = this.getRandomBoolean(); + + await this.db.updateDeviceState(device.id, { locked: isLocked }); + + console.log( + `SECURITY LOCK [${device.id}] ${isLocked ? 'Locked 🔒' : 'Unlocked 🔓'}`, + ); + } + + private async simulateSecurityCamera(device: SecurityCamera) { + const motionDetected = this.getRandomBoolean(); + + await this.db.updateDeviceState(device.id, { + motionDetected, + }); + + console.log( + `SECURITY CAMERA [${device.id}] Motion ${motionDetected ? 'Detected 🚨' : 'Clear ✅'}`, + ); + } + private async simulateHumiditySensor(device: HumiditySensor) { // Simulate humidity changes between 30-70% with very small random fluctuations const currentHumidity = device.humidity; @@ -306,6 +336,12 @@ export class DeviceSimulator { case 'AC': await this.simulateAC(device as AC); break; + case 'SECURITY_LOCK': + await this.simulateSecurityLock(device as SecurityLock); + break; + case 'SECURITY_CAMERA': + await this.simulateSecurityCamera(device as SecurityCamera); + break; } }