import { expect, tap } from '@git.zone/tstest/tapbundle'; import { DevoloHomeControlClient, DevoloHomeControlConfigFlow, DevoloHomeControlIntegration, DevoloHomeControlMapper, HomeAssistantDevoloHomeControlIntegration, createDevoloHomeControlDiscoveryDescriptor, devoloHomeControlProfile, type IDevoloHomeControlSnapshot } from '../../ts/integrations/devolo_home_control/index.js'; const rawData = { device: { name: 'devolo Home Control Gateway', manufacturer: 'devolo', model: 'Central Unit 2600', serialNumber: 'devolo-hc-1', }, entities: [ { id: 'front_door', name: 'Front door', platform: 'binary_sensor' as const, state: false, deviceClass: 'door', }, { id: 'hall_thermostat', name: 'Hall thermostat', platform: 'climate' as const, state: 21.5, unit: 'C', }, { id: 'living_room_blind', name: 'Living room blind', platform: 'cover' as const, state: 75, }, { id: 'dimmer', name: 'Dimmer', platform: 'light' as const, state: true, }, { id: 'battery', name: 'Battery', platform: 'sensor' as const, state: 87, unit: '%', deviceClass: 'battery', }, { id: 'outlet', name: 'Outlet', platform: 'switch' as const, state: false, }, ], }; tap.test('matches manual devolo Home Control candidates and creates config flow output', async () => { const descriptor = createDevoloHomeControlDiscoveryDescriptor(); const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'devolo_home_control-manual-match'); const result = await matcher!.matches({ name: 'devolo Home Control gateway 2600', metadata: { rawData } }, {}); expect(result.matched).toBeTrue(); expect(result.candidate?.integrationDomain).toEqual('devolo_home_control'); const validation = await descriptor.getValidators()[0].validate(result.candidate!, {}); expect(validation.matched).toBeTrue(); const done = await (await new DevoloHomeControlConfigFlow().start(result.candidate!, {})).submit!({ username: 'user@example.com', password: 'secret' }); expect(done.kind).toEqual('done'); expect(done.config?.username).toEqual('user@example.com'); expect(done.config?.rawData).toEqual(rawData); }); tap.test('maps devolo Home Control raw snapshots to runtime devices and entities', async () => { const client = new DevoloHomeControlClient({ name: 'devolo Runtime Gateway', rawData }); const snapshot = await client.getSnapshot(); const devices = DevoloHomeControlMapper.toDevices(snapshot); const entities = DevoloHomeControlMapper.toEntities(snapshot); expect(snapshot.online).toBeTrue(); expect(snapshot.source).toEqual('manual'); expect(devices[0].integrationDomain).toEqual('devolo_home_control'); expect(devices[0].manufacturer).toEqual('devolo'); expect(entities.some((entityArg) => entityArg.platform === 'climate')).toBeTrue(); expect(entities.some((entityArg) => entityArg.platform === 'light')).toBeTrue(); }); tap.test('exposes devolo Home Control runtime status and explicit unsupported control without executor', async () => { const alias = new HomeAssistantDevoloHomeControlIntegration(); expect(alias instanceof DevoloHomeControlIntegration).toBeTrue(); expect(alias.domain).toEqual('devolo_home_control'); expect(devoloHomeControlProfile.status).toEqual('read-only-runtime'); expect(devoloHomeControlProfile.metadata.qualityScale).toEqual('silver'); expect(devoloHomeControlProfile.metadata.requirements).toEqual(['devolo-home-control-api==0.19.0']); const runtime = await new DevoloHomeControlIntegration().setup({ name: 'devolo Runtime', rawData }, {}); const status = await runtime.callService!({ domain: 'devolo_home_control', service: 'status', target: {} }); const snapshot = status.data as IDevoloHomeControlSnapshot; expect(status.success).toBeTrue(); expect(snapshot.online).toBeTrue(); expect((await runtime.callService!({ domain: 'devolo_home_control', service: 'refresh', target: {} })).success).toBeTrue(); expect((await runtime.devices())[0].name).toEqual('devolo Home Control Gateway'); const controlCommand = await runtime.callService!({ domain: 'switch', service: 'turn_on', target: {} }); expect(controlCommand.success).toBeFalse(); expect(controlCommand.error?.includes('requires an injected client.execute() or commandExecutor')).toBeTrue(); await runtime.destroy(); }); export default tap.start();