@mp-consulting/homebridge-daikin-cloud 1.3.5 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +39 -1
- package/README.md +5 -3
- package/dist/src/accessories/air-conditioning-accessory.d.ts +2 -2
- package/dist/src/accessories/air-conditioning-accessory.d.ts.map +1 -1
- package/dist/src/accessories/air-conditioning-accessory.js.map +1 -1
- package/dist/src/accessories/altherma-accessory.d.ts +2 -2
- package/dist/src/accessories/altherma-accessory.d.ts.map +1 -1
- package/dist/src/accessories/altherma-accessory.js.map +1 -1
- package/dist/src/accessories/base-accessory.d.ts +6 -6
- package/dist/src/accessories/base-accessory.d.ts.map +1 -1
- package/dist/src/accessories/base-accessory.js +15 -15
- package/dist/src/accessories/base-accessory.js.map +1 -1
- package/dist/src/api/daikin-api.d.ts +26 -26
- package/dist/src/api/daikin-api.d.ts.map +1 -1
- package/dist/src/api/daikin-api.js +68 -42
- package/dist/src/api/daikin-api.js.map +1 -1
- package/dist/src/api/daikin-cloud.repository.d.ts.map +1 -1
- package/dist/src/api/daikin-cloud.repository.js +22 -14
- package/dist/src/api/daikin-cloud.repository.js.map +1 -1
- package/dist/src/api/daikin-controller.d.ts +41 -47
- package/dist/src/api/daikin-controller.d.ts.map +1 -1
- package/dist/src/api/daikin-controller.js +40 -64
- package/dist/src/api/daikin-controller.js.map +1 -1
- package/dist/src/api/daikin-device.d.ts +36 -31
- package/dist/src/api/daikin-device.d.ts.map +1 -1
- package/dist/src/api/daikin-device.js +45 -31
- package/dist/src/api/daikin-device.js.map +1 -1
- package/dist/src/api/daikin-mobile-oauth.d.ts +20 -20
- package/dist/src/api/daikin-mobile-oauth.d.ts.map +1 -1
- package/dist/src/api/daikin-mobile-oauth.js +49 -44
- package/dist/src/api/daikin-mobile-oauth.js.map +1 -1
- package/dist/src/api/daikin-oauth.d.ts +32 -32
- package/dist/src/api/daikin-oauth.d.ts.map +1 -1
- package/dist/src/api/daikin-oauth.js +64 -56
- package/dist/src/api/daikin-oauth.js.map +1 -1
- package/dist/src/api/daikin-schemas.d.ts +476 -351
- package/dist/src/api/daikin-schemas.d.ts.map +1 -1
- package/dist/src/api/daikin-schemas.js +11 -42
- package/dist/src/api/daikin-schemas.js.map +1 -1
- package/dist/src/api/daikin-types.d.ts +5 -1
- package/dist/src/api/daikin-types.d.ts.map +1 -1
- package/dist/src/api/daikin-types.js.map +1 -1
- package/dist/src/api/daikin-websocket.d.ts +31 -32
- package/dist/src/api/daikin-websocket.d.ts.map +1 -1
- package/dist/src/api/daikin-websocket.js +55 -35
- package/dist/src/api/daikin-websocket.js.map +1 -1
- package/dist/src/api/index.d.ts +2 -1
- package/dist/src/api/index.d.ts.map +1 -1
- package/dist/src/api/index.js +3 -1
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/api/token-storage.d.ts +21 -0
- package/dist/src/api/token-storage.d.ts.map +1 -0
- package/dist/src/api/token-storage.js +90 -0
- package/dist/src/api/token-storage.js.map +1 -0
- package/dist/src/config/config-manager.d.ts +33 -33
- package/dist/src/config/config-manager.d.ts.map +1 -1
- package/dist/src/config/config-manager.js +33 -33
- package/dist/src/config/config-manager.js.map +1 -1
- package/dist/src/constants/api.constants.d.ts +4 -0
- package/dist/src/constants/api.constants.d.ts.map +1 -1
- package/dist/src/constants/api.constants.js +5 -1
- package/dist/src/constants/api.constants.js.map +1 -1
- package/dist/src/constants/device.constants.d.ts +4 -0
- package/dist/src/constants/device.constants.d.ts.map +1 -1
- package/dist/src/constants/device.constants.js +5 -1
- package/dist/src/constants/device.constants.js.map +1 -1
- package/dist/src/device/accessory-factory.d.ts +10 -10
- package/dist/src/device/accessory-factory.d.ts.map +1 -1
- package/dist/src/device/accessory-factory.js +7 -7
- package/dist/src/device/accessory-factory.js.map +1 -1
- package/dist/src/device/capability-detector.d.ts +8 -8
- package/dist/src/device/capability-detector.d.ts.map +1 -1
- package/dist/src/device/capability-detector.js +6 -6
- package/dist/src/device/capability-detector.js.map +1 -1
- package/dist/src/device/capability-docs.d.ts +1 -9
- package/dist/src/device/capability-docs.d.ts.map +1 -1
- package/dist/src/device/capability-docs.js +19 -73
- package/dist/src/device/capability-docs.js.map +1 -1
- package/dist/src/device/profiles/device-profile.d.ts +1 -1
- package/dist/src/device/profiles/device-profile.d.ts.map +1 -1
- package/dist/src/device/profiles/device-profile.js +4 -4
- package/dist/src/device/profiles/device-profile.js.map +1 -1
- package/dist/src/features/base-feature.d.ts +2 -2
- package/dist/src/features/base-feature.d.ts.map +1 -1
- package/dist/src/features/base-feature.js +2 -3
- package/dist/src/features/base-feature.js.map +1 -1
- package/dist/src/features/feature-manager.d.ts +8 -16
- package/dist/src/features/feature-manager.d.ts.map +1 -1
- package/dist/src/features/feature-manager.js +5 -17
- package/dist/src/features/feature-manager.js.map +1 -1
- package/dist/src/features/modes/dry-operation-mode.feature.d.ts +1 -1
- package/dist/src/features/modes/dry-operation-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/dry-operation-mode.feature.js.map +1 -1
- package/dist/src/features/modes/econo-mode.feature.d.ts +1 -1
- package/dist/src/features/modes/econo-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/econo-mode.feature.js.map +1 -1
- package/dist/src/features/modes/fan-only-operation-mode.feature.d.ts +1 -1
- package/dist/src/features/modes/fan-only-operation-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/fan-only-operation-mode.feature.js.map +1 -1
- package/dist/src/features/modes/indoor-silent-mode.feature.d.ts +1 -1
- package/dist/src/features/modes/indoor-silent-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/indoor-silent-mode.feature.js.map +1 -1
- package/dist/src/features/modes/outdoor-silent-mode.feature.d.ts +1 -1
- package/dist/src/features/modes/outdoor-silent-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/outdoor-silent-mode.feature.js.map +1 -1
- package/dist/src/features/modes/powerful-mode.feature.d.ts +1 -1
- package/dist/src/features/modes/powerful-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/powerful-mode.feature.js.map +1 -1
- package/dist/src/features/modes/streamer-mode.feature.d.ts +1 -1
- package/dist/src/features/modes/streamer-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/streamer-mode.feature.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/platform.d.ts +11 -8
- package/dist/src/platform.d.ts.map +1 -1
- package/dist/src/platform.js +64 -15
- package/dist/src/platform.js.map +1 -1
- package/dist/src/services/climate-control.service.d.ts +8 -2
- package/dist/src/services/climate-control.service.d.ts.map +1 -1
- package/dist/src/services/climate-control.service.js +59 -58
- package/dist/src/services/climate-control.service.js.map +1 -1
- package/dist/src/services/hot-water-tank.service.d.ts +6 -2
- package/dist/src/services/hot-water-tank.service.d.ts.map +1 -1
- package/dist/src/services/hot-water-tank.service.js +33 -31
- package/dist/src/services/hot-water-tank.service.js.map +1 -1
- package/dist/src/types/daikin-enums.js +12 -12
- package/dist/src/types/daikin-enums.js.map +1 -1
- package/dist/src/types/device-capabilities.d.ts +1 -1
- package/dist/src/types/device-capabilities.d.ts.map +1 -1
- package/dist/src/utils/log-context.d.ts +23 -23
- package/dist/src/utils/log-context.d.ts.map +1 -1
- package/dist/src/utils/log-context.js +28 -28
- package/dist/src/utils/log-context.js.map +1 -1
- package/dist/src/utils/strings.d.ts.map +1 -1
- package/dist/src/utils/strings.js.map +1 -1
- package/dist/src/utils/update-mapper.d.ts +16 -16
- package/dist/src/utils/update-mapper.d.ts.map +1 -1
- package/dist/src/utils/update-mapper.js +14 -14
- package/dist/src/utils/update-mapper.js.map +1 -1
- package/homebridge-ui/public/index.html +2 -2
- package/homebridge-ui/public/script.js +957 -898
- package/homebridge-ui/server.js +746 -678
- package/package.json +29 -27
- package/.claude/settings.json +0 -3
- package/.claude/settings.local.json +0 -29
- package/CHANGELOG.md +0 -103
- package/CLAUDE.md +0 -269
- package/config.md +0 -2
- package/dist/src/api/daikin-device-tracker.d.ts +0 -97
- package/dist/src/api/daikin-device-tracker.d.ts.map +0 -1
- package/dist/src/api/daikin-device-tracker.js +0 -136
- package/dist/src/api/daikin-device-tracker.js.map +0 -1
- package/dist/src/api/http-interceptor.d.ts +0 -99
- package/dist/src/api/http-interceptor.d.ts.map +0 -1
- package/dist/src/api/http-interceptor.js +0 -177
- package/dist/src/api/http-interceptor.js.map +0 -1
- package/dist/src/di/service-container.d.ts +0 -92
- package/dist/src/di/service-container.d.ts.map +0 -1
- package/dist/src/di/service-container.js +0 -156
- package/dist/src/di/service-container.js.map +0 -1
- package/dist/src/features/feature-registry.d.ts +0 -100
- package/dist/src/features/feature-registry.d.ts.map +0 -1
- package/dist/src/features/feature-registry.js +0 -142
- package/dist/src/features/feature-registry.js.map +0 -1
- package/dist/src/services/service-factory.d.ts +0 -46
- package/dist/src/services/service-factory.d.ts.map +0 -1
- package/dist/src/services/service-factory.js +0 -72
- package/dist/src/services/service-factory.js.map +0 -1
- package/dist/src/utils/error-handler.d.ts +0 -101
- package/dist/src/utils/error-handler.d.ts.map +0 -1
- package/dist/src/utils/error-handler.js +0 -251
- package/dist/src/utils/error-handler.js.map +0 -1
- package/dist/src/utils/retry.d.ts +0 -42
- package/dist/src/utils/retry.d.ts.map +0 -1
- package/dist/src/utils/retry.js +0 -70
- package/dist/src/utils/retry.js.map +0 -1
- package/docs/ARCHITECTURE.md +0 -645
- package/docs/IMPLEMENTATION_GUIDE.md +0 -899
- package/docs/IMPROVEMENTS_SUMMARY.md +0 -415
- package/docs/NEXT_STEPS.md +0 -368
- package/docs/Screenshot 2024-07-04 at 18.41.28.png +0 -0
- package/docs/TROUBLESHOOTING.md +0 -475
- package/docs/api-response-for-BRP069A8x.json +0 -520
- package/docs/api-response-for-BRP069C4x-2.json +0 -881
- package/docs/api-response-for-BRP069C4x.json +0 -916
- package/docs/api-response-for-altherma.json +0 -759
- package/docs/api-response-for-altherma2.json +0 -2735
- package/docs/api-response-with-multiple-devices-incl-heatpump.json +0 -2544
- package/docs/cr-insance-altherma-id-0.json +0 -834
- package/docs/mock-air-to-air-dx23.json +0 -759
- package/docs/mock-air-to-air-dx4.json +0 -1134
- package/docs/mock-airpurifier-with-humidifier.json +0 -732
- package/docs/mock-airpurifier.json +0 -450
- package/docs/mock-altherma-air-to-water-lan.json +0 -845
- package/docs/mock-altherma-air-to-water-wlan.json +0 -845
- package/docs/mock-d2cnd-gas-boiler.json +0 -649
- package/docs/setpointmode-vs-controlmode-vs-setpoints-vs-sensorydata.txt +0 -6
- package/images/fan-speed.jpeg +0 -0
- package/images/homekit-controls.jpeg +0 -0
- package/images/homekit-settings.jpeg +0 -0
- package/images/swing-mode.png +0 -0
- package/jest.config.ts +0 -13
- package/test/fixtures/altherma-crSense-2.ts +0 -834
- package/test/fixtures/altherma-fraction.ts +0 -718
- package/test/fixtures/altherma-heat-pump-2.ts +0 -479
- package/test/fixtures/altherma-heat-pump.ts +0 -757
- package/test/fixtures/altherma-miladcerkic-off.ts +0 -524
- package/test/fixtures/altherma-miladcerkic.ts +0 -524
- package/test/fixtures/altherma-v1ckoeln.ts +0 -644
- package/test/fixtures/altherma-with-embedded-id-zero.ts +0 -834
- package/test/fixtures/dx23-airco-2.ts +0 -343
- package/test/fixtures/dx23-airco.ts +0 -518
- package/test/fixtures/dx4-airco.ts +0 -914
- package/test/fixtures/unknown-jan.ts +0 -488
- package/test/fixtures/unknown-kitchen-guests.ts +0 -488
- package/test/helpers/test-isolation.ts +0 -228
- package/test/integration/air-conditioning.test.ts +0 -410
- package/test/integration/altherma.test.ts +0 -289
- package/test/integration/platform.test.ts +0 -118
- package/test/mocks/index.ts +0 -27
- package/test/test-gigya-auth.js +0 -443
- package/test/test-mobile-oauth.js +0 -175
- package/test/test-websocket-mobile.js +0 -123
- package/test/test-websocket.js +0 -116
- package/test/unit/api/__snapshots__/daikinCloud.test.ts.snap +0 -1320
- package/test/unit/api/daikin-api.test.ts +0 -384
- package/test/unit/api/daikin-oauth.test.ts +0 -214
- package/test/unit/api/daikinCloud.test.ts +0 -12
- package/test/unit/config/config-manager.test.ts +0 -271
- package/test/unit/device/daikin-device.test.ts +0 -79
- package/test/unit/services/hot-water-tank.service.test.ts +0 -123
- package/test/unit/utils/error-handler.test.ts +0 -274
- package/test/unit/utils/log-context.test.ts +0 -271
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ConfigManager Tests
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {ConfigManager, PluginConfig} from '../../../src/config/config-manager';
|
|
6
|
-
|
|
7
|
-
describe('ConfigManager', () => {
|
|
8
|
-
describe('getAuthMode', () => {
|
|
9
|
-
it('should return mobile_app when configured', () => {
|
|
10
|
-
const config: PluginConfig = {
|
|
11
|
-
platform: 'DaikinCloud',
|
|
12
|
-
authMode: 'mobile_app',
|
|
13
|
-
};
|
|
14
|
-
const manager = new ConfigManager(config);
|
|
15
|
-
expect(manager.getAuthMode()).toBe('mobile_app');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('should default to developer_portal when not specified', () => {
|
|
19
|
-
const config: PluginConfig = {
|
|
20
|
-
platform: 'DaikinCloud',
|
|
21
|
-
};
|
|
22
|
-
const manager = new ConfigManager(config);
|
|
23
|
-
expect(manager.getAuthMode()).toBe('developer_portal');
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('getDeveloperCredentials', () => {
|
|
28
|
-
it('should return credentials when all fields present', () => {
|
|
29
|
-
const config: PluginConfig = {
|
|
30
|
-
platform: 'DaikinCloud',
|
|
31
|
-
clientId: 'test-id',
|
|
32
|
-
clientSecret: 'test-secret',
|
|
33
|
-
callbackServerExternalAddress: 'example.com',
|
|
34
|
-
callbackServerPort: 8582,
|
|
35
|
-
};
|
|
36
|
-
const manager = new ConfigManager(config);
|
|
37
|
-
const creds = manager.getDeveloperCredentials();
|
|
38
|
-
|
|
39
|
-
expect(creds).not.toBeNull();
|
|
40
|
-
expect(creds?.clientId).toBe('test-id');
|
|
41
|
-
expect(creds?.clientSecret).toBe('test-secret');
|
|
42
|
-
expect(creds?.callbackServerExternalAddress).toBe('example.com');
|
|
43
|
-
expect(creds?.callbackServerPort).toBe(8582);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should return null when fields missing', () => {
|
|
47
|
-
const config: PluginConfig = {
|
|
48
|
-
platform: 'DaikinCloud',
|
|
49
|
-
clientId: 'test-id',
|
|
50
|
-
};
|
|
51
|
-
const manager = new ConfigManager(config);
|
|
52
|
-
expect(manager.getDeveloperCredentials()).toBeNull();
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('getMobileCredentials', () => {
|
|
57
|
-
it('should return credentials when present', () => {
|
|
58
|
-
const config: PluginConfig = {
|
|
59
|
-
platform: 'DaikinCloud',
|
|
60
|
-
daikinEmail: 'test@example.com',
|
|
61
|
-
daikinPassword: 'password123',
|
|
62
|
-
};
|
|
63
|
-
const manager = new ConfigManager(config);
|
|
64
|
-
const creds = manager.getMobileCredentials();
|
|
65
|
-
|
|
66
|
-
expect(creds).not.toBeNull();
|
|
67
|
-
expect(creds?.email).toBe('test@example.com');
|
|
68
|
-
expect(creds?.password).toBe('password123');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should return null when fields missing', () => {
|
|
72
|
-
const config: PluginConfig = {
|
|
73
|
-
platform: 'DaikinCloud',
|
|
74
|
-
daikinEmail: 'test@example.com',
|
|
75
|
-
};
|
|
76
|
-
const manager = new ConfigManager(config);
|
|
77
|
-
expect(manager.getMobileCredentials()).toBeNull();
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('validate', () => {
|
|
82
|
-
it('should pass validation for valid developer portal config', () => {
|
|
83
|
-
const config: PluginConfig = {
|
|
84
|
-
platform: 'DaikinCloud',
|
|
85
|
-
authMode: 'developer_portal',
|
|
86
|
-
clientId: 'test-id',
|
|
87
|
-
clientSecret: 'test-secret',
|
|
88
|
-
callbackServerExternalAddress: '192.168.1.100',
|
|
89
|
-
callbackServerPort: 8582,
|
|
90
|
-
};
|
|
91
|
-
const manager = new ConfigManager(config);
|
|
92
|
-
const result = manager.validate();
|
|
93
|
-
|
|
94
|
-
expect(result.valid).toBe(true);
|
|
95
|
-
expect(result.errors).toHaveLength(0);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should fail validation for developer portal without clientId', () => {
|
|
99
|
-
const config: PluginConfig = {
|
|
100
|
-
platform: 'DaikinCloud',
|
|
101
|
-
authMode: 'developer_portal',
|
|
102
|
-
clientSecret: 'test-secret',
|
|
103
|
-
callbackServerExternalAddress: '192.168.1.100',
|
|
104
|
-
};
|
|
105
|
-
const manager = new ConfigManager(config);
|
|
106
|
-
const result = manager.validate();
|
|
107
|
-
|
|
108
|
-
expect(result.valid).toBe(false);
|
|
109
|
-
expect(result.errors).toContain('Client ID is required for Developer Portal mode');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should fail validation for localhost callback address', () => {
|
|
113
|
-
const config: PluginConfig = {
|
|
114
|
-
platform: 'DaikinCloud',
|
|
115
|
-
authMode: 'developer_portal',
|
|
116
|
-
clientId: 'test-id',
|
|
117
|
-
clientSecret: 'test-secret',
|
|
118
|
-
callbackServerExternalAddress: 'localhost',
|
|
119
|
-
};
|
|
120
|
-
const manager = new ConfigManager(config);
|
|
121
|
-
const result = manager.validate();
|
|
122
|
-
|
|
123
|
-
expect(result.valid).toBe(false);
|
|
124
|
-
expect(result.errors).toContain('Callback address cannot be localhost. Use your external IP or domain.');
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should fail validation for invalid port', () => {
|
|
128
|
-
const config: PluginConfig = {
|
|
129
|
-
platform: 'DaikinCloud',
|
|
130
|
-
authMode: 'developer_portal',
|
|
131
|
-
clientId: 'test-id',
|
|
132
|
-
clientSecret: 'test-secret',
|
|
133
|
-
callbackServerExternalAddress: '192.168.1.100',
|
|
134
|
-
callbackServerPort: 99999,
|
|
135
|
-
};
|
|
136
|
-
const manager = new ConfigManager(config);
|
|
137
|
-
const result = manager.validate();
|
|
138
|
-
|
|
139
|
-
expect(result.valid).toBe(false);
|
|
140
|
-
expect(result.errors.length).toBeGreaterThan(0);
|
|
141
|
-
expect(result.errors.some(e => e.includes('port'))).toBe(true);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should warn about privileged port', () => {
|
|
145
|
-
const config: PluginConfig = {
|
|
146
|
-
platform: 'DaikinCloud',
|
|
147
|
-
authMode: 'developer_portal',
|
|
148
|
-
clientId: 'test-id',
|
|
149
|
-
clientSecret: 'test-secret',
|
|
150
|
-
callbackServerExternalAddress: '192.168.1.100',
|
|
151
|
-
callbackServerPort: 443,
|
|
152
|
-
};
|
|
153
|
-
const manager = new ConfigManager(config);
|
|
154
|
-
const result = manager.validate();
|
|
155
|
-
|
|
156
|
-
expect(result.valid).toBe(true);
|
|
157
|
-
expect(result.warnings).toContain('Port 443 is privileged (< 1024) and may require root permissions.');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should pass validation for valid mobile app config', () => {
|
|
161
|
-
const config: PluginConfig = {
|
|
162
|
-
platform: 'DaikinCloud',
|
|
163
|
-
authMode: 'mobile_app',
|
|
164
|
-
daikinEmail: 'test@example.com',
|
|
165
|
-
daikinPassword: 'password123',
|
|
166
|
-
};
|
|
167
|
-
const manager = new ConfigManager(config);
|
|
168
|
-
const result = manager.validate();
|
|
169
|
-
|
|
170
|
-
expect(result.valid).toBe(true);
|
|
171
|
-
expect(result.errors).toHaveLength(0);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('should fail validation for mobile app without email', () => {
|
|
175
|
-
const config: PluginConfig = {
|
|
176
|
-
platform: 'DaikinCloud',
|
|
177
|
-
authMode: 'mobile_app',
|
|
178
|
-
daikinPassword: 'password123',
|
|
179
|
-
};
|
|
180
|
-
const manager = new ConfigManager(config);
|
|
181
|
-
const result = manager.validate();
|
|
182
|
-
|
|
183
|
-
expect(result.valid).toBe(false);
|
|
184
|
-
expect(result.errors).toContain('Email is required for Mobile App mode');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('should warn about low update interval for developer portal', () => {
|
|
188
|
-
const config: PluginConfig = {
|
|
189
|
-
platform: 'DaikinCloud',
|
|
190
|
-
authMode: 'developer_portal',
|
|
191
|
-
clientId: 'test-id',
|
|
192
|
-
clientSecret: 'test-secret',
|
|
193
|
-
callbackServerExternalAddress: '192.168.1.100',
|
|
194
|
-
updateIntervalInMinutes: 5,
|
|
195
|
-
};
|
|
196
|
-
const manager = new ConfigManager(config);
|
|
197
|
-
const result = manager.validate();
|
|
198
|
-
|
|
199
|
-
expect(result.valid).toBe(true);
|
|
200
|
-
expect(result.warnings.some(w => w.includes('rate limit'))).toBe(true);
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
describe('getUpdateIntervalMs', () => {
|
|
205
|
-
it('should return configured interval in milliseconds', () => {
|
|
206
|
-
const config: PluginConfig = {
|
|
207
|
-
platform: 'DaikinCloud',
|
|
208
|
-
updateIntervalInMinutes: 30,
|
|
209
|
-
};
|
|
210
|
-
const manager = new ConfigManager(config);
|
|
211
|
-
expect(manager.getUpdateIntervalMs()).toBe(30 * 60 * 1000);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('should return default interval when not configured', () => {
|
|
215
|
-
const config: PluginConfig = {
|
|
216
|
-
platform: 'DaikinCloud',
|
|
217
|
-
};
|
|
218
|
-
const manager = new ConfigManager(config);
|
|
219
|
-
expect(manager.getUpdateIntervalMs()).toBe(15 * 60 * 1000);
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
describe('isDeviceExcluded', () => {
|
|
224
|
-
it('should return true for excluded device', () => {
|
|
225
|
-
const config: PluginConfig = {
|
|
226
|
-
platform: 'DaikinCloud',
|
|
227
|
-
excludedDevicesByDeviceId: ['device1', 'device2'],
|
|
228
|
-
};
|
|
229
|
-
const manager = new ConfigManager(config);
|
|
230
|
-
expect(manager.isDeviceExcluded('device1')).toBe(true);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('should return false for non-excluded device', () => {
|
|
234
|
-
const config: PluginConfig = {
|
|
235
|
-
platform: 'DaikinCloud',
|
|
236
|
-
excludedDevicesByDeviceId: ['device1', 'device2'],
|
|
237
|
-
};
|
|
238
|
-
const manager = new ConfigManager(config);
|
|
239
|
-
expect(manager.isDeviceExcluded('device3')).toBe(false);
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
describe('getFeatures', () => {
|
|
244
|
-
it('should return feature configuration', () => {
|
|
245
|
-
const config: PluginConfig = {
|
|
246
|
-
platform: 'DaikinCloud',
|
|
247
|
-
showPowerfulMode: true,
|
|
248
|
-
showEconoMode: false,
|
|
249
|
-
};
|
|
250
|
-
const manager = new ConfigManager(config);
|
|
251
|
-
const features = manager.getFeatures();
|
|
252
|
-
|
|
253
|
-
expect(features.powerfulMode).toBe(true);
|
|
254
|
-
expect(features.econoMode).toBe(false);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('should use legacy showExtraFeatures when present', () => {
|
|
258
|
-
const config: PluginConfig = {
|
|
259
|
-
platform: 'DaikinCloud',
|
|
260
|
-
showExtraFeatures: true,
|
|
261
|
-
};
|
|
262
|
-
const manager = new ConfigManager(config);
|
|
263
|
-
const features = manager.getFeatures();
|
|
264
|
-
|
|
265
|
-
// All features should be true when legacy flag is set
|
|
266
|
-
expect(features.powerfulMode).toBe(true);
|
|
267
|
-
expect(features.econoMode).toBe(true);
|
|
268
|
-
expect(features.streamerMode).toBe(true);
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import {PlatformAccessory} from 'homebridge/lib/platformAccessory';
|
|
2
|
-
import {DaikinCloudDevice, DaikinApi} from '../../../src/api';
|
|
3
|
-
import {dx4Airco} from '../../fixtures/dx4-airco';
|
|
4
|
-
import {althermaHeatPump} from '../../fixtures/altherma-heat-pump';
|
|
5
|
-
|
|
6
|
-
const mockApi = { updateDevice: jest.fn().mockResolvedValue(undefined) } as unknown as DaikinApi;
|
|
7
|
-
|
|
8
|
-
it('Get deviceModel from device %s', async () => {
|
|
9
|
-
const accessory = new PlatformAccessory('NAME', 'efd08509-2edb-41d0-a9ab-ce913323d811');
|
|
10
|
-
accessory.context.device = new DaikinCloudDevice(dx4Airco as any, mockApi);
|
|
11
|
-
expect(accessory.context.device.getDescription().deviceModel).toEqual('dx4');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('Get name from device %s', async () => {
|
|
15
|
-
const accessory = new PlatformAccessory('NAME', 'efd08509-2edb-41d0-a9ab-ce913323d811');
|
|
16
|
-
accessory.context.device = new DaikinCloudDevice(dx4Airco as any, mockApi);
|
|
17
|
-
expect(accessory.context.device.getData('climateControl', 'name', undefined).value).toEqual('Zolder');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('Get name from device %s', async () => {
|
|
21
|
-
const accessory = new PlatformAccessory('NAME', 'efd08509-2edb-41d0-a9ab-ce913323d811');
|
|
22
|
-
accessory.context.device = new DaikinCloudDevice(althermaHeatPump as any, mockApi);
|
|
23
|
-
expect(accessory.context.device.getData('climateControlMainZone', 'name', undefined).value).toEqual('Altherma');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('Get tankTemperature from device domesticHotWaterTank', async () => {
|
|
27
|
-
const accessory = new PlatformAccessory('NAME', 'efd08509-2edb-41d0-a9ab-ce913323d811');
|
|
28
|
-
accessory.context.device = new DaikinCloudDevice(althermaHeatPump as any, mockApi);
|
|
29
|
-
expect(accessory.context.device.getData('domesticHotWaterTank', 'sensoryData', '/tankTemperature').value).toEqual(48);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('Get roomTemperature from climateControl sensoryData', async () => {
|
|
33
|
-
const device = new DaikinCloudDevice(dx4Airco as any, mockApi);
|
|
34
|
-
const result = device.getData('climateControl', 'sensoryData', '/roomTemperature');
|
|
35
|
-
expect(result.value).toEqual(25);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('Debug: trace getData path for roomTemperature', () => {
|
|
39
|
-
const device = new DaikinCloudDevice(dx4Airco as any, mockApi);
|
|
40
|
-
|
|
41
|
-
// Check that management point is found
|
|
42
|
-
const desc = device.desc;
|
|
43
|
-
const mp = desc.managementPoints?.find((m: any) => m.embeddedId === 'climateControl');
|
|
44
|
-
expect(mp).toBeDefined();
|
|
45
|
-
expect(mp?.sensoryData).toBeDefined();
|
|
46
|
-
|
|
47
|
-
// Check sensoryData structure
|
|
48
|
-
const sensoryData = mp?.sensoryData;
|
|
49
|
-
expect(sensoryData?.value?.roomTemperature?.value).toEqual(25);
|
|
50
|
-
|
|
51
|
-
// Now test through getData
|
|
52
|
-
const result = device.getData('climateControl', 'sensoryData', '/roomTemperature');
|
|
53
|
-
console.log('getData result:', JSON.stringify(result, null, 2));
|
|
54
|
-
expect(result.value).toEqual(25);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('Integration: simulate full AC accessory temperature get', () => {
|
|
58
|
-
const device = new DaikinCloudDevice(dx4Airco as any, mockApi);
|
|
59
|
-
|
|
60
|
-
// Test the path that ClimateControlService uses
|
|
61
|
-
const managementPointId = 'climateControl';
|
|
62
|
-
|
|
63
|
-
// 1. getCurrentControlMode() checks controlMode first
|
|
64
|
-
const controlModeResult = device.getData(managementPointId, 'controlMode', undefined);
|
|
65
|
-
console.log('controlMode result:', JSON.stringify(controlModeResult, null, 2));
|
|
66
|
-
|
|
67
|
-
// For AC devices, controlMode should not exist, so it defaults to 'roomTemperature'
|
|
68
|
-
const controlMode = controlModeResult.value ? controlModeResult.value : 'roomTemperature';
|
|
69
|
-
console.log('controlMode:', controlMode);
|
|
70
|
-
|
|
71
|
-
// 2. handleCurrentTemperatureGet uses this path
|
|
72
|
-
const path = '/' + controlMode;
|
|
73
|
-
console.log('path:', path);
|
|
74
|
-
|
|
75
|
-
const temperature = device.getData(managementPointId, 'sensoryData', path);
|
|
76
|
-
console.log('temperature result:', JSON.stringify(temperature, null, 2));
|
|
77
|
-
|
|
78
|
-
expect(temperature.value).toEqual(25);
|
|
79
|
-
});
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import {HotWaterTankService} from '../../../src/services';
|
|
2
|
-
import {DaikinCloudDevice, DaikinApi} from '../../../src/api';
|
|
3
|
-
import {MockPlatformConfig} from '../../mocks';
|
|
4
|
-
import {DaikinCloudAccessoryContext, DaikinCloudPlatform} from '../../../src/platform';
|
|
5
|
-
import {PlatformAccessory} from 'homebridge/lib/platformAccessory';
|
|
6
|
-
import {Characteristic, uuid} from 'hap-nodejs';
|
|
7
|
-
import {althermaHeatPump} from '../../fixtures/altherma-heat-pump';
|
|
8
|
-
|
|
9
|
-
import {HomebridgeAPI} from 'homebridge/lib/api.js';
|
|
10
|
-
import {Logger} from 'homebridge/lib/logger.js';
|
|
11
|
-
|
|
12
|
-
// Helper to get management point data from the device
|
|
13
|
-
const getManagementPoint = (device: DaikinCloudDevice, embeddedId: string): any => {
|
|
14
|
-
return (device as any).rawData.managementPoints.find((mp: any) => mp.embeddedId === embeddedId);
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// Use fake timers to prevent tests from hanging due to setInterval/setTimeout in platform
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
jest.useFakeTimers();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
jest.clearAllTimers();
|
|
24
|
-
jest.useRealTimers();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('HotWaterTankService', () => {
|
|
28
|
-
let accessory: PlatformAccessory<DaikinCloudAccessoryContext>;
|
|
29
|
-
let service: HotWaterTankService;
|
|
30
|
-
|
|
31
|
-
const EMBEDDED_ID = 'domesticHotWaterTank';
|
|
32
|
-
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
const mockApi = {} as DaikinApi;
|
|
35
|
-
accessory = new PlatformAccessory<DaikinCloudAccessoryContext>('ACCESSORY_NAME', uuid.generate('ACCESSORY_UUID'));
|
|
36
|
-
// Use a deep copy of the fixture to isolate test mutations
|
|
37
|
-
accessory.context['device'] = new DaikinCloudDevice(JSON.parse(JSON.stringify(althermaHeatPump)) as any, mockApi);
|
|
38
|
-
accessory.context.device.getLastUpdated = jest.fn().mockReturnValue(new Date(1987, 0, 19, 0, 0, 0, 0));
|
|
39
|
-
|
|
40
|
-
const platform = new DaikinCloudPlatform(new Logger(), new MockPlatformConfig(true), new HomebridgeAPI());
|
|
41
|
-
|
|
42
|
-
service = new HotWaterTankService(platform, accessory, EMBEDDED_ID);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should get the current heating cooling state', async () => {
|
|
46
|
-
expect(await service.handleHotWaterTankCurrentHeatingCoolingStateGet()).toBe(Characteristic.CurrentHeatingCoolingState.HEAT);
|
|
47
|
-
|
|
48
|
-
getManagementPoint(accessory.context.device, EMBEDDED_ID)['onOffMode'] = { value: 'off' };
|
|
49
|
-
|
|
50
|
-
expect(await service.handleHotWaterTankTargetHeatingCoolingStateGet()).toBe(Characteristic.CurrentHeatingCoolingState.OFF);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should get the current temperature', async () => {
|
|
54
|
-
expect(await service.handleHotWaterTankCurrentTemperatureGet()).toBe(48);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should get the target heating cooling temperature', async () => {
|
|
58
|
-
expect(await service.handleHotWaterTankHeatingTargetTemperatureGet()).toBe(48);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should get the target heating cooling state', async () => {
|
|
62
|
-
expect(await service.handleHotWaterTankTargetHeatingCoolingStateGet()).toBe(Characteristic.TargetHeatingCoolingState.HEAT);
|
|
63
|
-
|
|
64
|
-
getManagementPoint(accessory.context.device, EMBEDDED_ID)['operationMode'] = {
|
|
65
|
-
'settable': true,
|
|
66
|
-
'values': [
|
|
67
|
-
'auto',
|
|
68
|
-
'dry',
|
|
69
|
-
'cooling',
|
|
70
|
-
'heating',
|
|
71
|
-
'fanOnly',
|
|
72
|
-
],
|
|
73
|
-
'value': 'cooling',
|
|
74
|
-
};
|
|
75
|
-
expect(await service.handleHotWaterTankTargetHeatingCoolingStateGet()).toBe(Characteristic.TargetHeatingCoolingState.COOL);
|
|
76
|
-
|
|
77
|
-
getManagementPoint(accessory.context.device, EMBEDDED_ID)['operationMode'] = {
|
|
78
|
-
'settable': true,
|
|
79
|
-
'values': [
|
|
80
|
-
'auto',
|
|
81
|
-
'dry',
|
|
82
|
-
'cooling',
|
|
83
|
-
'heating',
|
|
84
|
-
'fanOnly',
|
|
85
|
-
],
|
|
86
|
-
'value': 'auto',
|
|
87
|
-
};
|
|
88
|
-
expect(await service.handleHotWaterTankTargetHeatingCoolingStateGet()).toBe(Characteristic.TargetHeatingCoolingState.AUTO);
|
|
89
|
-
|
|
90
|
-
getManagementPoint(accessory.context.device, EMBEDDED_ID)['operationMode'] = {
|
|
91
|
-
'settable': true,
|
|
92
|
-
'values': [
|
|
93
|
-
'auto',
|
|
94
|
-
'dry',
|
|
95
|
-
'cooling',
|
|
96
|
-
'heating',
|
|
97
|
-
'fanOnly',
|
|
98
|
-
],
|
|
99
|
-
'value': 'fanOnly',
|
|
100
|
-
};
|
|
101
|
-
expect(await service.handleHotWaterTankTargetHeatingCoolingStateGet()).toBe(Characteristic.TargetHeatingCoolingState.AUTO);
|
|
102
|
-
|
|
103
|
-
getManagementPoint(accessory.context.device, EMBEDDED_ID)['operationMode'] = {
|
|
104
|
-
'settable': true,
|
|
105
|
-
'values': [
|
|
106
|
-
'auto',
|
|
107
|
-
'dry',
|
|
108
|
-
'cooling',
|
|
109
|
-
'heating',
|
|
110
|
-
'fanOnly',
|
|
111
|
-
],
|
|
112
|
-
'value': 'dry',
|
|
113
|
-
};
|
|
114
|
-
expect(await service.handleHotWaterTankTargetHeatingCoolingStateGet()).toBe(Characteristic.TargetHeatingCoolingState.AUTO);
|
|
115
|
-
|
|
116
|
-
getManagementPoint(accessory.context.device, EMBEDDED_ID)['onOffMode'] = { value: 'off' };
|
|
117
|
-
expect(await service.handleHotWaterTankTargetHeatingCoolingStateGet()).toBe(Characteristic.TargetHeatingCoolingState.OFF);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should get the powerful mode', async () => {
|
|
121
|
-
expect(await service.handlePowerfulModeGet()).toBe(false);
|
|
122
|
-
});
|
|
123
|
-
});
|