homebridge-tsvesync
Version:
Homebridge plugin for VeSync devices including Levoit air purifiers, humidifiers, and Etekcity smart outlets
178 lines (155 loc) • 5.64 kB
text/typescript
import { API } from 'homebridge';
import { Logger } from 'homebridge';
import { CharacteristicValue, CharacteristicSetCallback } from 'homebridge';
import { TSVESyncPlatform } from '../../platform';
import { VeSync } from 'tsvesync';
import { VeSyncFan } from '../../types/device.types';
import { RetryManager } from '../../utils/retry';
import { FanAccessory } from '../../accessories/fan.accessory';
import { createMockFan } from '../utils/test-helpers';
describe('Fan Device Tests', () => {
let mockAPI: jest.Mocked<API>;
let platform: TSVESyncPlatform;
let logger: jest.Mocked<Logger>;
let api: jest.Mocked<VeSync>;
beforeEach(() => {
mockAPI = {
hap: {
Characteristic: {
Active: jest.fn(),
RotationSpeed: jest.fn(),
RotationDirection: jest.fn(),
SwingMode: jest.fn(),
LockPhysicalControls: jest.fn(),
On: 'On',
Name: 'Name',
Manufacturer: jest.fn(),
Model: jest.fn(),
SerialNumber: jest.fn(),
},
Service: {
Fanv2: jest.fn(),
AccessoryInformation: jest.fn(),
},
uuid: {
generate: jest.fn(),
},
},
platformAccessory: jest.fn(),
} as any;
logger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
} as any;
api = {
login: jest.fn(),
getDevices: jest.fn(),
} as any;
platform = new TSVESyncPlatform(logger, {} as any, mockAPI);
(platform as any).api = api;
});
describe('fan state management', () => {
let fan: FanAccessory;
let mockFan: jest.Mocked<VeSyncFan>;
let accessory: any;
let handlers: { [key: string]: (value: CharacteristicValue) => Promise<void> } = {};
let infoService: any;
let fanService: any;
beforeEach(() => {
mockFan = createMockFan({
deviceName: 'Test Fan',
deviceType: 'LTF-F422',
cid: 'test-cid',
uuid: 'test-uuid',
});
const modeCharacteristic = {
onGet: jest.fn().mockReturnThis(),
onSet: jest.fn().mockReturnThis(),
};
const modeService = {
getCharacteristic: jest.fn().mockReturnValue(modeCharacteristic),
};
// Create mock services
infoService = {
setCharacteristic: jest.fn().mockReturnThis(),
};
// Create mock fan service with handler capture
fanService = {
getCharacteristic: jest.fn().mockImplementation((char) => ({
onSet: jest.fn((fn) => {
if (char === platform.Characteristic.Active) {
handlers.setActive = fn;
} else if (char === platform.Characteristic.RotationSpeed) {
handlers.setRotationSpeed = fn;
} else if (char === platform.Characteristic.RotationDirection) {
handlers.setRotationDirection = fn;
} else if (char === platform.Characteristic.SwingMode) {
handlers.setSwingMode = fn;
} else if (char === platform.Characteristic.LockPhysicalControls) {
handlers.setLockPhysicalControls = fn;
}
return { onGet: jest.fn() };
}),
onGet: jest.fn(),
})),
};
// Create mock accessory
accessory = {
getService: jest.fn((service) => {
if (service === 'Fan Mode') {
return modeService;
}
if (service === platform.Service.AccessoryInformation) {
return infoService;
}
if (service === platform.Service.Fanv2) {
return fanService;
}
return null;
}),
addService: jest.fn((service) => {
if (service === platform.Service.Fanv2) {
return fanService;
}
return null;
}),
};
fan = new FanAccessory(platform, accessory, mockFan);
});
it('should handle power state changes', async () => {
expect(handlers.setActive).toBeDefined();
await handlers.setActive(1);
expect(mockFan.turnOn).toHaveBeenCalled();
await handlers.setActive(0);
expect(mockFan.turnOff).toHaveBeenCalled();
});
it('should handle fan speed changes', async () => {
expect(handlers.setRotationSpeed).toBeDefined();
await handlers.setRotationSpeed(50);
expect(mockFan.changeFanSpeed).toHaveBeenCalledWith(3); // 50% maps to speed 3
});
it('should handle rotation direction changes', async () => {
expect(handlers.setRotationDirection).toBeDefined();
await handlers.setRotationDirection(1); // CLOCKWISE
expect(mockFan.setOscillation).toHaveBeenCalledWith(true);
await handlers.setRotationDirection(0); // COUNTER_CLOCKWISE
expect(mockFan.setOscillation).toHaveBeenCalledWith(false);
});
it('should handle swing mode changes', async () => {
expect(handlers.setSwingMode).toBeDefined();
await handlers.setSwingMode(1); // SWING_ENABLED
expect(mockFan.setOscillation).toHaveBeenCalledWith(true);
await handlers.setSwingMode(0); // SWING_DISABLED
expect(mockFan.setOscillation).toHaveBeenCalledWith(false);
});
it('should handle child lock changes', async () => {
expect(handlers.setLockPhysicalControls).toBeDefined();
await handlers.setLockPhysicalControls(1); // CONTROL_LOCK_ENABLED
expect(mockFan.setChildLock).toHaveBeenCalledWith(true);
await handlers.setLockPhysicalControls(0); // CONTROL_LOCK_DISABLED
expect(mockFan.setChildLock).toHaveBeenCalledWith(false);
});
});
});