import { expect, tap } from '@git.zone/tstest/tapbundle'; import { DevialetClient, DevialetConfigFlow, DevialetIntegration, DevialetMapper, HomeAssistantDevialetIntegration, createDevialetDiscoveryDescriptor, devialetProfile, type IDevialetSnapshot } from '../../ts/integrations/devialet/index.js'; const rawData = { device: { name: 'Living Room Phantom', manufacturer: 'Devialet', model: 'Phantom I', serialNumber: 'devialet-phantom-1', }, entities: [ { id: 'speaker', name: 'Speaker', platform: 'media_player' as const, state: 'playing', attributes: { source: 'AirPlay', soundMode: 'Flat', volumeLevel: 0.35, }, }, ], }; tap.test('matches manual Devialet candidates and creates config flow output', async () => { const descriptor = createDevialetDiscoveryDescriptor(); const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'devialet-manual-match'); const result = await matcher!.matches({ host: 'phantom.local', name: 'Devialet Phantom', metadata: { rawData } }, {}); expect(result.matched).toBeTrue(); expect(result.candidate?.integrationDomain).toEqual('devialet'); const validation = await descriptor.getValidators()[0].validate(result.candidate!, {}); expect(validation.matched).toBeTrue(); const done = await (await new DevialetConfigFlow().start(result.candidate!, {})).submit!({}); expect(done.kind).toEqual('done'); expect(done.config?.host).toEqual('phantom.local'); expect(done.config?.rawData).toEqual(rawData); }); tap.test('maps Devialet raw snapshots to runtime devices and entities', async () => { const client = new DevialetClient({ name: 'Devialet Runtime Speaker', rawData }); const snapshot = await client.getSnapshot(); const devices = DevialetMapper.toDevices(snapshot); const entities = DevialetMapper.toEntities(snapshot); expect(snapshot.online).toBeTrue(); expect(snapshot.source).toEqual('manual'); expect(devices[0].integrationDomain).toEqual('devialet'); expect(devices[0].manufacturer).toEqual('Devialet'); expect(entities[0].platform).toEqual('media_player'); }); tap.test('exposes Devialet runtime status and explicit unsupported control without executor', async () => { const alias = new HomeAssistantDevialetIntegration(); expect(alias instanceof DevialetIntegration).toBeTrue(); expect(alias.domain).toEqual('devialet'); expect(devialetProfile.status).toEqual('read-only-runtime'); expect(devialetProfile.metadata.requirements).toEqual(['devialet==1.5.7']); expect(devialetProfile.metadata.afterDependencies).toEqual(['zeroconf']); const runtime = await new DevialetIntegration().setup({ name: 'Devialet Runtime', rawData }, {}); const status = await runtime.callService!({ domain: 'devialet', service: 'status', target: {} }); const snapshot = status.data as IDevialetSnapshot; expect(status.success).toBeTrue(); expect(snapshot.online).toBeTrue(); expect((await runtime.callService!({ domain: 'devialet', service: 'refresh', target: {} })).success).toBeTrue(); expect((await runtime.entities())[0].name).toEqual('Speaker'); const controlCommand = await runtime.callService!({ domain: 'media_player', service: 'turn_off', target: {} }); expect(controlCommand.success).toBeFalse(); expect(controlCommand.error?.includes('requires an injected client.execute() or commandExecutor')).toBeTrue(); await runtime.destroy(); }); export default tap.start();