Add native local bus integrations
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import { createRflinkDiscoveryDescriptor } from '../../ts/integrations/rflink/index.js';
|
||||
|
||||
tap.test('matches manual RFLink serial gateway entries', async () => {
|
||||
const descriptor = createRflinkDiscoveryDescriptor();
|
||||
const matcher = descriptor.getMatchers()[0];
|
||||
const result = await matcher.matches({
|
||||
connectionType: 'serial',
|
||||
port: '/dev/serial/by-id/usb-Nodo_RFLink_Gateway',
|
||||
baudRate: 57600,
|
||||
name: 'RFLink Gateway',
|
||||
}, {});
|
||||
expect(result.matched).toBeTrue();
|
||||
expect(result.candidate?.integrationDomain).toEqual('rflink');
|
||||
expect(result.candidate?.metadata?.connectionType).toEqual('serial');
|
||||
expect(result.candidate?.metadata?.serialPort).toEqual('/dev/serial/by-id/usb-Nodo_RFLink_Gateway');
|
||||
});
|
||||
|
||||
tap.test('matches manual RFLink TCP bridge entries', async () => {
|
||||
const descriptor = createRflinkDiscoveryDescriptor();
|
||||
const matcher = descriptor.getMatchers()[0];
|
||||
const result = await matcher.matches({
|
||||
connectionType: 'tcp',
|
||||
host: '192.168.1.34',
|
||||
port: 1234,
|
||||
metadata: { rflink: true },
|
||||
}, {});
|
||||
expect(result.matched).toBeTrue();
|
||||
expect(result.candidate?.host).toEqual('192.168.1.34');
|
||||
expect(result.candidate?.port).toEqual(1234);
|
||||
expect(result.candidate?.metadata?.connectionType).toEqual('tcp');
|
||||
});
|
||||
|
||||
tap.test('validates RFLink gateway candidates', async () => {
|
||||
const descriptor = createRflinkDiscoveryDescriptor();
|
||||
const validator = descriptor.getValidators()[0];
|
||||
const result = await validator.validate({
|
||||
source: 'manual',
|
||||
integrationDomain: 'rflink',
|
||||
name: 'RFLink over TCP',
|
||||
host: '192.168.1.35',
|
||||
port: 1234,
|
||||
metadata: { connectionType: 'tcp' },
|
||||
}, {});
|
||||
expect(result.matched).toBeTrue();
|
||||
expect(result.normalizedDeviceId).toEqual('192.168.1.35');
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
@@ -0,0 +1,76 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import { RflinkClient, RflinkMapper } from '../../ts/integrations/rflink/index.js';
|
||||
import type { IRflinkSnapshot } from '../../ts/integrations/rflink/index.js';
|
||||
|
||||
const snapshot: IRflinkSnapshot = {
|
||||
gateway: {
|
||||
id: 'rflink-gateway',
|
||||
name: 'Nodo RFLink',
|
||||
connectionType: 'tcp',
|
||||
host: '192.168.1.34',
|
||||
port: 1234,
|
||||
firmware: 'RFLink Gateway',
|
||||
version: '1.1',
|
||||
revision: '46',
|
||||
online: true,
|
||||
},
|
||||
connected: true,
|
||||
updatedAt: '2026-05-05T00:00:00.000Z',
|
||||
devices: [],
|
||||
events: [],
|
||||
entities: [
|
||||
{ id: 'newkaku_0000c6c2_1', platform: 'light', name: 'Kitchen Lamp', type: 'hybrid', state: 'on', brightness: 170, aliases: ['kaku_000001_a'] },
|
||||
{ id: 'conrad_00785c_0a', platform: 'switch', name: 'Ceiling Fan', state: 'off' },
|
||||
{ id: 'alectov1_0334_temp', platform: 'sensor', name: 'Outdoor Temperature', sensorType: 'temperature', value: 7.4, unitOfMeasurement: '°C' },
|
||||
{ id: 'pt2262_00174754_0', platform: 'binary_sensor', name: 'PIR Entrance', deviceClass: 'motion', state: 'on' },
|
||||
{ id: 'newkaku_0a8720_0', platform: 'cover', name: 'Office Blind', type: 'inverted', state: 'closed' },
|
||||
],
|
||||
};
|
||||
|
||||
tap.test('maps RFLink snapshots to gateway and RF device definitions', async () => {
|
||||
const devices = RflinkMapper.toDevices(snapshot);
|
||||
expect(devices.length).toEqual(6);
|
||||
expect(devices[0].id).toEqual('rflink.gateway.rflink_gateway');
|
||||
expect(devices.find((deviceArg) => deviceArg.id === 'rflink.light.newkaku_0000c6c2_1')?.features.some((featureArg) => featureArg.id === 'brightness')).toBeTrue();
|
||||
expect(devices.find((deviceArg) => deviceArg.id === 'rflink.sensor.alectov1_0334_temp')?.state[0].value).toEqual(7.4);
|
||||
});
|
||||
|
||||
tap.test('maps RFLink entities to Home Assistant-style platforms', async () => {
|
||||
const entities = RflinkMapper.toEntities(snapshot);
|
||||
expect(entities.map((entityArg) => entityArg.platform)).toContain('light');
|
||||
expect(entities.map((entityArg) => entityArg.platform)).toContain('switch');
|
||||
expect(entities.map((entityArg) => entityArg.platform)).toContain('sensor');
|
||||
expect(entities.map((entityArg) => entityArg.platform)).toContain('binary_sensor');
|
||||
expect(entities.map((entityArg) => entityArg.platform)).toContain('cover');
|
||||
expect(entities.find((entityArg) => entityArg.id === 'sensor.outdoor_temperature')?.attributes?.unitOfMeasurement).toEqual('°C');
|
||||
expect(entities.find((entityArg) => entityArg.id === 'cover.office_blind')?.state).toEqual('closed');
|
||||
});
|
||||
|
||||
tap.test('maps RFLink services to line protocol commands', async () => {
|
||||
const closeCommand = RflinkMapper.commandForService(snapshot, {
|
||||
domain: 'cover',
|
||||
service: 'close_cover',
|
||||
target: { entityId: 'cover.office_blind' },
|
||||
data: {},
|
||||
});
|
||||
expect(closeCommand?.rflinkCommand).toEqual('UP');
|
||||
expect(RflinkClient.commandShape(closeCommand?.deviceId || '', closeCommand?.rflinkCommand || '').line).toEqual('10;newkaku;0a8720;0;UP;');
|
||||
|
||||
const dimCommand = RflinkMapper.commandForService(snapshot, {
|
||||
domain: 'light',
|
||||
service: 'turn_on',
|
||||
target: { entityId: 'light.kitchen_lamp' },
|
||||
data: { brightness: 128 },
|
||||
});
|
||||
expect(dimCommand?.type).toEqual('set_value');
|
||||
expect(dimCommand?.rflinkCommand).toEqual('7');
|
||||
});
|
||||
|
||||
tap.test('decodes RFLink sensor line packets into events', async () => {
|
||||
const packet = RflinkClient.decodeLine('20;00;Alecto V1;ID=0334;TEMP=004a;HUM=26;BAT=OK;');
|
||||
const events = packet ? RflinkClient.eventsFromPacket(packet) : [];
|
||||
expect(events.find((eventArg) => eventArg.id === 'alectov1_0334_temp')?.value).toEqual(7.4);
|
||||
expect(events.find((eventArg) => eventArg.id === 'alectov1_0334_hum')?.value).toEqual(26);
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
Reference in New Issue
Block a user