109 lines
5.5 KiB
TypeScript
109 lines
5.5 KiB
TypeScript
import { createServer } from 'node:http';
|
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { DoorbirdClient, DoorbirdIntegration } from '../../ts/integrations/doorbird/index.js';
|
|
|
|
tap.test('reads DoorBird data and executes relay/light over native local HTTP', async () => {
|
|
const requests: Array<{ url: string; method?: string; authorization?: string }> = [];
|
|
const server = createServer((requestArg, responseArg) => {
|
|
requests.push({ url: requestArg.url || '', method: requestArg.method, authorization: requestArg.headers.authorization });
|
|
if (!requestArg.headers.authorization) {
|
|
responseArg.statusCode = 401;
|
|
responseArg.setHeader('www-authenticate', 'Basic realm="DoorBird"');
|
|
responseArg.end('auth required');
|
|
return;
|
|
}
|
|
if (requestArg.headers.authorization !== `Basic ${Buffer.from('user:secret').toString('base64')}`) {
|
|
responseArg.statusCode = 401;
|
|
responseArg.end('bad credentials');
|
|
return;
|
|
}
|
|
|
|
if (requestArg.url === '/bha-api/info.cgi') {
|
|
responseArg.setHeader('content-type', 'application/json');
|
|
responseArg.end(JSON.stringify({ BHA: { RETURNCODE: '1', VERSION: [{ FIRMWARE: '000122', BUILD_NUMBER: '42', 'DEVICE-TYPE': 'D1101V', PRIMARY_MAC_ADDR: '1CCAE3AABBCC', RELAYS: ['1', '2'] }] } }));
|
|
return;
|
|
}
|
|
if (requestArg.url === '/bha-api/monitor.cgi?check=doorbell') {
|
|
responseArg.setHeader('content-type', 'text/plain');
|
|
responseArg.end('doorbell=1');
|
|
return;
|
|
}
|
|
if (requestArg.url === '/bha-api/monitor.cgi?check=motionsensor') {
|
|
responseArg.setHeader('content-type', 'text/plain');
|
|
responseArg.end('motionsensor=0');
|
|
return;
|
|
}
|
|
if (requestArg.url === '/bha-api/favorites.cgi') {
|
|
responseArg.setHeader('content-type', 'application/json');
|
|
responseArg.end(JSON.stringify({ http: { '1': { title: 'Home Assistant (front_door_doorbell)', value: 'http://ha/api/doorbird/front?token=abc' } } }));
|
|
return;
|
|
}
|
|
if (requestArg.url === '/bha-api/schedule.cgi') {
|
|
responseArg.setHeader('content-type', 'application/json');
|
|
responseArg.end(JSON.stringify([{ input: 'doorbell', param: '', output: [{ enabled: '1', event: 'http', param: '1', schedule: { weekdays: [{ from: '104400', to: '104399' }] } }] }]));
|
|
return;
|
|
}
|
|
if (requestArg.url === '/bha-api/image.cgi') {
|
|
responseArg.setHeader('content-type', 'image/jpeg');
|
|
responseArg.end(Buffer.from([1, 2, 3, 4]));
|
|
return;
|
|
}
|
|
if (requestArg.url === '/bha-api/open-door.cgi?r=2') {
|
|
responseArg.setHeader('content-type', 'application/json');
|
|
responseArg.end(JSON.stringify({ BHA: { RETURNCODE: '1' } }));
|
|
return;
|
|
}
|
|
if (requestArg.url === '/bha-api/light-on.cgi') {
|
|
responseArg.setHeader('content-type', 'application/json');
|
|
responseArg.end(JSON.stringify({ BHA: { RETURNCODE: '1' } }));
|
|
return;
|
|
}
|
|
responseArg.statusCode = 404;
|
|
responseArg.end('not found');
|
|
});
|
|
|
|
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
try {
|
|
const address = server.address();
|
|
const port = typeof address === 'object' && address ? address.port : 0;
|
|
const supported = await DoorbirdClient.verifySupportedDevice('127.0.0.1', port, 1000);
|
|
const client = new DoorbirdClient({ host: '127.0.0.1', port, username: 'user', password: 'secret', timeoutMs: 1000, snapshotTimeoutMs: 1000, name: 'Front Door' });
|
|
const snapshot = await client.getSnapshot();
|
|
const image = await client.getSnapshotImage('live');
|
|
const relay = await client.execute({ action: 'energize_relay', relay: '2' });
|
|
const light = await client.execute({ action: 'turn_light_on' });
|
|
|
|
expect(supported).toBeTrue();
|
|
expect(snapshot.online).toBeTrue();
|
|
expect(snapshot.source).toEqual('http');
|
|
expect(snapshot.deviceInfo.macAddress).toEqual('1ccae3aabbcc');
|
|
expect(snapshot.status.doorbell).toBeTrue();
|
|
expect(snapshot.status.motion).toBeFalse();
|
|
expect(snapshot.favorites?.http?.['1'].title).toContain('Home Assistant');
|
|
expect(snapshot.schedule?.[0].input).toEqual('doorbell');
|
|
expect(image.contentType).toEqual('image/jpeg');
|
|
expect(Buffer.from(image.data).toString('hex')).toEqual('01020304');
|
|
expect(relay).toEqual({ ok: true, relay: '2' });
|
|
expect(light).toEqual({ ok: true });
|
|
expect(requests.some((requestArg) => requestArg.url === '/bha-api/monitor.cgi?check=doorbell' && !requestArg.authorization)).toBeTrue();
|
|
expect(requests.some((requestArg) => requestArg.url === '/bha-api/info.cgi' && requestArg.authorization?.startsWith('Basic '))).toBeTrue();
|
|
await client.destroy();
|
|
} finally {
|
|
await new Promise<void>((resolve, reject) => server.close((errorArg) => errorArg ? reject(errorArg) : resolve()));
|
|
}
|
|
});
|
|
|
|
tap.test('runtime status/snapshot services use snapshots without claiming command success', async () => {
|
|
const runtime = await new DoorbirdIntegration().setup({ rawInfo: { PRIMARY_MAC_ADDR: '1CCAE3AABBCC', RELAYS: ['1'] }, status: { doorbell: false, motion: false }, name: 'Manual DoorBird' }, {});
|
|
const status = await runtime.callService?.({ domain: 'doorbird', service: 'status', target: {} });
|
|
const command = await runtime.callService?.({ domain: 'doorbird', service: 'turn_light_on', target: {} });
|
|
|
|
expect(status?.success).toBeTrue();
|
|
expect((status?.data as { doorbell?: boolean }).doorbell).toBeFalse();
|
|
expect(command?.success).toBeFalse();
|
|
expect(command?.error).toContain('config.host');
|
|
await runtime.destroy();
|
|
});
|
|
|
|
export default tap.start();
|