import { expect, tap } from '@git.zone/tstest/tapbundle'; import { AndroidIpWebcamMapper, type IAndroidIpWebcamSnapshot } from '../../ts/integrations/android_ip_webcam/index.js'; const snapshot: IAndroidIpWebcamSnapshot = { deviceInfo: { id: 'kitchen-phone', name: 'Kitchen Phone', manufacturer: 'Android IP Webcam', host: '192.168.1.20', port: 8080, protocol: 'http', online: true, }, camera: { id: 'camera', name: 'Kitchen Phone Camera', mjpegUrl: 'http://192.168.1.20:8080/video', imageUrl: 'http://192.168.1.20:8080/shot.jpg', rtspUrl: 'rtsp://192.168.1.20:8080/h264_aac.sdp', supportedFeatures: ['stream'], available: true, }, sensors: [ { key: 'audio_connections', name: 'Audio connections', value: 1, stateClass: 'total', entityCategory: 'diagnostic', available: true }, { key: 'battery_level', name: 'Battery level', value: 87, unit: '%', deviceClass: 'battery', stateClass: 'measurement', entityCategory: 'diagnostic', available: true }, ], binarySensors: [{ key: 'motion_active', name: 'Motion active', isOn: true, deviceClass: 'motion', available: true }], switches: [ { key: 'torch', name: 'Torch', isOn: false, command: 'torch', entityCategory: 'config', available: true }, { key: 'motion_detect', name: 'Motion detection', isOn: true, command: 'setting', entityCategory: 'config', available: true }, ], statusData: { audio_connections: 1, curvals: { torch: 'off', motion_detect: 'on' } }, sensorData: { battery_level: { unit: '%', data: [[ [87, 123] ]] }, motion_active: { data: [[ [1, 123] ]] } }, currentSettings: { torch: false, motion_detect: true }, enabledSensors: ['battery_level', 'motion_active'], enabledSettings: ['torch', 'motion_detect'], availableSettings: {}, connected: true, updatedAt: '2026-01-01T00:00:00.000Z', }; tap.test('maps Android IP Webcam camera, sensors, binary sensor, and switches', async () => { const devices = AndroidIpWebcamMapper.toDevices(snapshot); const entities = AndroidIpWebcamMapper.toEntities(snapshot); expect(devices.length).toEqual(1); expect(devices[0].features.some((featureArg) => featureArg.capability === 'camera')).toBeTrue(); expect(entities.find((entityArg) => entityArg.id === 'camera.kitchen_phone_camera')?.attributes?.mjpegUrl).toEqual('http://192.168.1.20:8080/video'); expect(entities.find((entityArg) => entityArg.id === 'sensor.battery_level')?.state).toEqual(87); expect(entities.find((entityArg) => entityArg.id === 'binary_sensor.motion_active')?.state).toEqual('on'); expect(entities.find((entityArg) => entityArg.id === 'switch.torch')?.state).toEqual('off'); }); tap.test('models camera and setting services as explicit commands', async () => { const streamCommand = AndroidIpWebcamMapper.commandForService(snapshot, { domain: 'camera', service: 'stream_source', target: { entityId: 'camera.kitchen_phone_camera' }, }); const snapshotCommand = AndroidIpWebcamMapper.commandForService(snapshot, { domain: 'camera', service: 'snapshot', target: { entityId: 'camera.kitchen_phone_camera' }, }); const torchCommand = AndroidIpWebcamMapper.commandForService(snapshot, { domain: 'switch', service: 'turn_on', target: { entityId: 'switch.torch' }, }); const zoomCommand = AndroidIpWebcamMapper.commandForService(snapshot, { domain: 'android_ip_webcam', service: 'set_zoom', target: {}, data: { zoom: '42' }, }); expect(streamCommand?.type).toEqual('stream_source'); expect(snapshotCommand?.type).toEqual('snapshot_image'); expect(torchCommand?.type).toEqual('torch'); expect(torchCommand?.activate).toBeTrue(); expect(zoomCommand?.type).toEqual('set_zoom'); expect(zoomCommand?.zoom).toEqual(42); }); export default tap.start();