import { expect, tap } from '@git.zone/tstest/tapbundle'; import { BroadlinkClient, BroadlinkIntegration, BroadlinkMapper } from '../../ts/integrations/broadlink/index.js'; import type { IBroadlinkCommand, IBroadlinkSnapshot } from '../../ts/integrations/broadlink/index.js'; const irCode = 'JgAcAB0dHB44HhweGx4cHR06HB0cHhwdHB8bHhwADQUAAAAAAAAAAAAAAAA='; const snapshot: IBroadlinkSnapshot = { connected: true, host: '192.168.1.50', port: 80, events: [], entities: [], devices: [ { id: '34ea34b45d2c', host: '192.168.1.50', macAddress: '34:ea:34:b4:5d:2c', type: 'RM4PRO', name: 'Bedroom RM4 Pro', model: 'RM4 pro', available: true, state: { temperature: 23.5, humidity: 45 }, codes: { television: { power: irCode } }, switches: [{ name: 'Bedroom TV', commandOn: irCode, commandOff: irCode }], }, { id: 'desk-plug', host: '192.168.1.60', macAddress: '24:df:a7:00:00:01', type: 'SP4B', name: 'Desk Plug', available: true, state: { pwr: 1, power: 12.3, volt: 230.1, current: 0.11, totalconsum: 1.5 }, }, ], }; tap.test('maps Broadlink snapshots to remote, switch, and sensor entities', async () => { const entities = BroadlinkMapper.toEntities(snapshot); expect(entities.find((entityArg) => entityArg.id === 'remote.bedroom_rm4_pro')?.platform).toEqual('remote'); expect(entities.find((entityArg) => entityArg.id === 'remote.bedroom_rm4_pro')?.attributes?.supportsRf).toBeTrue(); expect(entities.find((entityArg) => entityArg.id === 'switch.bedroom_tv')?.state).toEqual('off'); expect(entities.find((entityArg) => entityArg.id === 'switch.desk_plug')?.state).toEqual('on'); expect(entities.find((entityArg) => entityArg.id === 'sensor.bedroom_rm4_pro_temperature')?.state).toEqual(23.5); expect(entities.find((entityArg) => entityArg.id === 'sensor.desk_plug_power')?.attributes?.unitOfMeasurement).toEqual('W'); const devices = BroadlinkMapper.toDevices(snapshot); expect(devices.find((deviceArg) => deviceArg.id === 'broadlink.device.34ea34b45d2c')?.features.some((featureArg) => featureArg.id === 'remote')).toBeTrue(); expect(devices.find((deviceArg) => deviceArg.id === 'broadlink.device.desk_plug')?.features.some((featureArg) => featureArg.id === 'power')).toBeTrue(); }); tap.test('maps learned remote commands, raw IR, RF, and switch services', async () => { const remoteCommand = BroadlinkMapper.commandForService(snapshot, { domain: 'remote', service: 'send_command', target: { entityId: 'remote.bedroom_rm4_pro' }, data: { device: 'television', command: 'power', num_repeats: 2 }, }); expect(remoteCommand?.method).toEqual('send_data'); expect(remoteCommand?.packets?.[0].kind).toEqual('ir'); expect(remoteCommand?.packets?.[0].firstByte).toEqual(0x26); expect(remoteCommand?.numRepeats).toEqual(2); const irCommand = BroadlinkMapper.commandForService(snapshot, { domain: 'infrared', service: 'send_command', target: { entityId: 'remote.bedroom_rm4_pro' }, data: { rawTimings: [9000, -4500, 560, -560] }, }); expect(irCommand?.packet?.kind).toEqual('ir'); expect(irCommand?.packet?.firstByte).toEqual(0x26); const rfCommand = BroadlinkMapper.commandForService(snapshot, { domain: 'radio_frequency', service: 'send_command', target: { entityId: 'remote.bedroom_rm4_pro' }, data: { frequency: 433_920_000, rawTimings: [300, -900, 300, -900], repeat_count: 3 }, }); expect(rfCommand?.packet?.kind).toEqual('rf433'); expect(rfCommand?.packet?.firstByte).toEqual(0xb2); expect(rfCommand?.packet?.repeatCount).toEqual(3); const switchCommand = BroadlinkMapper.commandForService(snapshot, { domain: 'switch', service: 'turn_off', target: { entityId: 'switch.desk_plug' }, data: {}, }); expect(switchCommand?.method).toEqual('set_state'); expect(switchCommand?.payload?.pwr).toEqual(false); }); tap.test('does not report live UDP success without an injected executor', async () => { const integration = new BroadlinkIntegration(); const runtime = await integration.setup({ snapshot }, {}); const result = await runtime.callService?.({ domain: 'remote', service: 'send_command', target: { entityId: 'remote.bedroom_rm4_pro' }, data: { device: 'television', command: 'power' }, }); expect(result?.success).toBeFalse(); expect(String(result?.error).includes('not implemented')).toBeTrue(); }); tap.test('delegates mapped commands to injected Broadlink executor', async () => { const commands: IBroadlinkCommand[] = []; const integration = new BroadlinkIntegration(); const runtime = await integration.setup({ snapshot, commandExecutor: async (commandArg) => { commands.push(commandArg); return { success: true, transmitted: true, data: { ok: true } }; }, }, {}); const result = await runtime.callService?.({ domain: 'remote', service: 'send_command', target: { entityId: 'remote.bedroom_rm4_pro' }, data: { device: 'television', command: 'power' }, }); expect(result?.success).toBeTrue(); expect(commands[0].method).toEqual('send_data'); expect(commands[0].packets?.[0].base64).toEqual(BroadlinkClient.packetFromBase64(irCode).base64); }); tap.test('exposes native Broadlink IR and RF packet encoders', async () => { const irPacket = BroadlinkClient.irPacketFromTimings([9000, -4500, 560, -560]); expect(irPacket.firstByte).toEqual(0x26); expect(irPacket.kind).toEqual('ir'); const rfPacket = BroadlinkClient.rfPacketFromTimings({ frequency: 315_000_000, timings: [300, -900], repeatCount: 1 }); expect(rfPacket.firstByte).toEqual(0xb4); expect(rfPacket.kind).toEqual('rf315'); }); export default tap.start();