Add native camera and media service integrations
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import { createAxisDiscoveryDescriptor } from '../../ts/integrations/axis/index.js';
|
||||
|
||||
tap.test('matches Axis mDNS records by service type and OUI', async () => {
|
||||
const descriptor = createAxisDiscoveryDescriptor();
|
||||
const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'axis-mdns-match');
|
||||
const result = await matcher!.matches({
|
||||
type: '_axis-video._tcp.local.',
|
||||
name: 'AXIS P3265._axis-video._tcp.local.',
|
||||
host: 'axis-p3265.local',
|
||||
port: 80,
|
||||
txt: {
|
||||
macaddress: 'ACCC8E123456',
|
||||
},
|
||||
}, {});
|
||||
expect(result.matched).toBeTrue();
|
||||
expect(result.normalizedDeviceId).toEqual('accc8e123456');
|
||||
expect(result.candidate?.integrationDomain).toEqual('axis');
|
||||
});
|
||||
|
||||
tap.test('matches Axis SSDP records by manufacturer', async () => {
|
||||
const descriptor = createAxisDiscoveryDescriptor();
|
||||
const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'axis-ssdp-match');
|
||||
const result = await matcher!.matches({
|
||||
manufacturer: 'AXIS',
|
||||
location: 'http://192.168.1.50:80/',
|
||||
upnp: {
|
||||
friendlyName: 'AXIS Door Station',
|
||||
serialNumber: '00408C654321',
|
||||
modelName: 'I8116-E',
|
||||
},
|
||||
}, {});
|
||||
expect(result.matched).toBeTrue();
|
||||
expect(result.candidate?.host).toEqual('192.168.1.50');
|
||||
expect(result.candidate?.model).toEqual('I8116-E');
|
||||
});
|
||||
|
||||
tap.test('validates manual Axis candidates', async () => {
|
||||
const descriptor = createAxisDiscoveryDescriptor();
|
||||
const matcher = descriptor.getMatchers().find((matcherArg) => matcherArg.id === 'axis-manual-match');
|
||||
const match = await matcher!.matches({ host: 'axis.local', protocol: 'http', model: 'AXIS P3265' }, {});
|
||||
expect(match.matched).toBeTrue();
|
||||
|
||||
const validator = descriptor.getValidators()[0];
|
||||
const validation = await validator.validate(match.candidate!, {});
|
||||
expect(validation.matched).toBeTrue();
|
||||
expect(validation.candidate?.manufacturer).toEqual('Axis Communications AB');
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
@@ -0,0 +1,68 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import { AxisMapper, type IAxisSnapshot } from '../../ts/integrations/axis/index.js';
|
||||
|
||||
const snapshot: IAxisSnapshot = {
|
||||
deviceInfo: {
|
||||
id: 'accc8e123456',
|
||||
serialNumber: 'ACCC8E123456',
|
||||
macAddress: 'accc8e123456',
|
||||
name: 'Front Door Axis',
|
||||
manufacturer: 'Axis Communications AB',
|
||||
model: 'I8116-E',
|
||||
firmwareVersion: '11.10.0',
|
||||
host: '192.168.1.50',
|
||||
port: 80,
|
||||
protocol: 'http',
|
||||
},
|
||||
cameras: [{
|
||||
id: '1',
|
||||
name: 'Front Door Camera',
|
||||
enabled: true,
|
||||
videoSource: 1,
|
||||
snapshotUrl: 'http://192.168.1.50/axis-cgi/jpg/image.cgi?camera=1',
|
||||
mjpegUrl: 'http://192.168.1.50/axis-cgi/mjpg/video.cgi?camera=1',
|
||||
rtspUrl: 'rtsp://192.168.1.50/axis-media/media.amp?videocodec=h264&camera=1',
|
||||
supportsPtz: true,
|
||||
}],
|
||||
sensors: [{ id: 'firmware_version', name: 'Firmware version', value: '11.10.0' }],
|
||||
binarySensors: [{ id: 'port_0', name: 'Call button', isOn: false, deviceClass: 'connectivity', source: '0' }],
|
||||
events: [{ id: 'doorbell', name: 'Doorbell', topicBase: 'tns1:Device/tnsaxis:IO/Port', isTripped: false, deviceClass: 'doorbell' }],
|
||||
ports: [{ id: '0', name: 'Call button', direction: 'input', state: 'open', normalState: 'open' }],
|
||||
relays: [{ id: '1', name: 'Door strike', direction: 'output', state: 'open', normalState: 'open' }],
|
||||
switches: [{ id: '1', name: 'Door strike', direction: 'output', state: 'open', normalState: 'open' }],
|
||||
lights: [],
|
||||
apiDiscovery: [{ id: 'io-port-management', version: '1.0' }, { id: 'ptz-control', version: '1.0' }],
|
||||
connected: true,
|
||||
updatedAt: '2026-01-01T00:00:00.000Z',
|
||||
};
|
||||
|
||||
tap.test('maps Axis cameras, sensors, events, and relays', async () => {
|
||||
const devices = AxisMapper.toDevices(snapshot);
|
||||
const entities = AxisMapper.toEntities(snapshot);
|
||||
expect(devices.length).toEqual(1);
|
||||
expect(devices[0].features.some((featureArg) => featureArg.capability === 'camera')).toBeTrue();
|
||||
expect(entities.some((entityArg) => entityArg.id === 'camera.front_door_camera')).toBeTrue();
|
||||
expect(entities.some((entityArg) => entityArg.id === 'switch.door_strike')).toBeTrue();
|
||||
expect(entities.some((entityArg) => entityArg.id === 'binary_sensor.call_button')).toBeTrue();
|
||||
expect(entities.some((entityArg) => entityArg.id === 'event.doorbell')).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('maps relay and PTZ service commands', async () => {
|
||||
const relayCommand = AxisMapper.relayCommandForService(snapshot, {
|
||||
domain: 'switch',
|
||||
service: 'turn_on',
|
||||
target: { entityId: 'switch.door_strike' },
|
||||
});
|
||||
expect(relayCommand).toEqual({ portId: '1', state: 'closed' });
|
||||
|
||||
const ptzCommand = AxisMapper.ptzCommandForService(snapshot, {
|
||||
domain: 'axis',
|
||||
service: 'ptz_control',
|
||||
target: {},
|
||||
data: { camera: '1', move: 'left', speed: 50 },
|
||||
});
|
||||
expect(ptzCommand?.move).toEqual('left');
|
||||
expect(ptzCommand?.speed).toEqual(50);
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
Reference in New Issue
Block a user