matterbridge-lg-thinq-plugin 0.1.0
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/.claude/CLAUDE.md +43 -0
- package/.claude/agents/compiler.md +91 -0
- package/.claude/agents/direct-executor.md +98 -0
- package/.claude/agents/documenter.md +78 -0
- package/.claude/agents/finalizer.md +233 -0
- package/.claude/agents/implementer.md +112 -0
- package/.claude/agents/investigator.md +100 -0
- package/.claude/agents/release-manager.md +203 -0
- package/.claude/agents/reviewer.md +131 -0
- package/.claude/agents/technical-architect.md +403 -0
- package/.claude/agents/test-writer.md +159 -0
- package/.claude/agents/wiki-manager.md +147 -0
- package/.claude/instructions/agent-prompts.md +90 -0
- package/.claude/instructions/shared-rules.md +11 -0
- package/.claude/instructions/team-orchestrator-policy.md +165 -0
- package/.claude/memory.md +186 -0
- package/.claude/output-styles/engineer-manager.md +38 -0
- package/.claude/settings.json +40 -0
- package/.claude/settings.local.json +30 -0
- package/.claude/skills/load-policy/SKILL.md +13 -0
- package/.claude/skills/ref-idea/SKILL.md +44 -0
- package/.claude/skills/status-of/SKILL.md +38 -0
- package/.claude/skills/workspace-console/SKILL.md +49 -0
- package/.claude/statusline-command.sh +130 -0
- package/.claude/statusline.md +53 -0
- package/.claude/templates/approval-packet.template.html +171 -0
- package/.claude/templates/reference-workspaces.md +16 -0
- package/.claude/tools/cli-runner-server.cjs +220 -0
- package/.claude/tools/discord-send-server.cjs +188 -0
- package/.claude/tools/github-release-server.cjs +181 -0
- package/.claude/tools/glob-grep-server.cjs +267 -0
- package/.claude/tools/read-log-server.cjs +175 -0
- package/.github/codeql/codeql-config.yml +14 -0
- package/.github/workflows/build.yml +54 -0
- package/.github/workflows/codeql.yml +59 -0
- package/.github/workflows/coverage.yml +70 -0
- package/.github/workflows/publish.yml +103 -0
- package/.mcp.json +34 -0
- package/CHANGELOG.md +195 -0
- package/LICENSE +21 -0
- package/dist/cli/commands/devices.js +52 -0
- package/dist/cli/commands/energy.js +122 -0
- package/dist/cli/commands/login.js +54 -0
- package/dist/cli/help.js +50 -0
- package/dist/cli/main.js +56 -0
- package/dist/cli/session.js +13 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utils.js +29 -0
- package/dist/cli.js +2 -0
- package/dist/constants/index.js +1 -0
- package/dist/core/domain/entities/ThinqDevice.js +43 -0
- package/dist/core/domain/value-objects/AirConditionerCapabilities.js +22 -0
- package/dist/core/domain/value-objects/ThinqFilterState.js +35 -0
- package/dist/core/domain/value-objects/ThinqSnapshot.js +125 -0
- package/dist/errors/AuthenticationError.js +26 -0
- package/dist/errors/BaseError.js +23 -0
- package/dist/errors/index.js +2 -0
- package/dist/model/LgThinkqPluginPlatformConfig.js +23 -0
- package/dist/model/LgThinqPluginPlatformConfig.js +23 -0
- package/dist/module.js +212 -0
- package/dist/platform/deviceRegistry.js +26 -0
- package/dist/platform/platformConfigManager.js +89 -0
- package/dist/platform/platformState.js +9 -0
- package/dist/platform/thinq/thinqAirConditionerCommandHandlers.js +190 -0
- package/dist/platform/thinq/thinqAirConditionerEndpointFactory.js +53 -0
- package/dist/platform/thinq/thinqAirConditionerFilterResetCommandHandler.js +54 -0
- package/dist/platform/thinq/thinqAirConditionerFilterStateSync.js +19 -0
- package/dist/platform/thinq/thinqAirConditionerStateSync.js +205 -0
- package/dist/platform/thinq/thinqDeviceConfigReconciler.js +26 -0
- package/dist/platform/thinq/thinqDeviceConfigurator.js +124 -0
- package/dist/platform/thinq/thinqDeviceDiscovery.js +33 -0
- package/dist/platform/thinq/thinqWasherCommandHandlers.js +54 -0
- package/dist/platform/thinq/thinqWasherEndpointFactory.js +6 -0
- package/dist/platform/thinq/thinqWasherStateSync.js +31 -0
- package/dist/platform/thinq/thinqWasherStopCommandResolver.js +38 -0
- package/dist/services/authentication/AccountAuthStrategy.js +135 -0
- package/dist/services/authentication/AuthContext.js +1 -0
- package/dist/services/authentication/AuthenticationCoordinator.js +29 -0
- package/dist/services/authentication/IAuthStrategy.js +1 -0
- package/dist/services/authentication/TokenAuthStrategy.js +27 -0
- package/dist/services/authentication/UserDataRepository.js +32 -0
- package/dist/services/thinq/gateway.js +61 -0
- package/dist/services/thinq/mqtt/mqttCertificate.js +63 -0
- package/dist/services/thinq/mqtt/mqttConnectionEvents.js +37 -0
- package/dist/services/thinq/mqtt/mqttKeyRepository.js +33 -0
- package/dist/services/thinq/mqtt/mqttRetry.js +25 -0
- package/dist/services/thinq/mqtt/thinqMqttListener.js +97 -0
- package/dist/services/thinq/serviceContainer.js +73 -0
- package/dist/services/thinq/session.js +48 -0
- package/dist/services/thinq/thinqApiClient.js +258 -0
- package/dist/services/thinq/thinqDeviceService.js +75 -0
- package/dist/services/thinq/thinqFilterMonitoringService.js +40 -0
- package/dist/settings.js +9 -0
- package/dist/tests/helpers/testUtils.js +87 -0
- package/dist/utils/mask.js +39 -0
- package/docs/README_CLI.md +166 -0
- package/eslint.config.js +194 -0
- package/matterbridge-lg-thinq-plugin.config.json +27 -0
- package/matterbridge-lg-thinq-plugin.schema.json +520 -0
- package/package.json +107 -0
- package/prettier.config.js +49 -0
- package/scripts/parse-junit-failures.mjs +45 -0
- package/scripts/parse-tsc-failures.mjs +48 -0
- package/scripts/run-build-local-summary.mjs +29 -0
- package/scripts/run-format-summary.mjs +36 -0
- package/scripts/run-lint-fix-summary.mjs +38 -0
- package/scripts/run-precommit-summary.mjs +94 -0
- package/src/cli/commands/devices.ts +67 -0
- package/src/cli/commands/energy.ts +160 -0
- package/src/cli/commands/login.ts +74 -0
- package/src/cli/help.ts +53 -0
- package/src/cli/main.ts +66 -0
- package/src/cli/session.ts +15 -0
- package/src/cli/types.ts +10 -0
- package/src/cli/utils.ts +30 -0
- package/src/cli.ts +3 -0
- package/src/constants/index.ts +8 -0
- package/src/core/domain/entities/ThinqDevice.test.ts +224 -0
- package/src/core/domain/entities/ThinqDevice.ts +74 -0
- package/src/core/domain/value-objects/AirConditionerCapabilities.ts +50 -0
- package/src/core/domain/value-objects/ThinqFilterState.ts +50 -0
- package/src/core/domain/value-objects/ThinqSnapshot.ts +162 -0
- package/src/errors/AuthenticationError.ts +37 -0
- package/src/errors/BaseError.ts +38 -0
- package/src/errors/index.ts +24 -0
- package/src/model/LgThinqPluginPlatformConfig.ts +94 -0
- package/src/module.ts +298 -0
- package/src/platform/deviceRegistry.ts +58 -0
- package/src/platform/platformConfigManager.ts +133 -0
- package/src/platform/platformState.ts +16 -0
- package/src/platform/thinq/thinqAirConditionerCommandHandlers.ts +291 -0
- package/src/platform/thinq/thinqAirConditionerEndpointFactory.ts +145 -0
- package/src/platform/thinq/thinqAirConditionerFilterResetCommandHandler.ts +96 -0
- package/src/platform/thinq/thinqAirConditionerFilterStateSync.ts +42 -0
- package/src/platform/thinq/thinqAirConditionerStateSync.ts +343 -0
- package/src/platform/thinq/thinqDeviceConfigReconciler.ts +42 -0
- package/src/platform/thinq/thinqDeviceConfigurator.test.ts +90 -0
- package/src/platform/thinq/thinqDeviceConfigurator.ts +191 -0
- package/src/platform/thinq/thinqDeviceDiscovery.test.ts +173 -0
- package/src/platform/thinq/thinqDeviceDiscovery.ts +51 -0
- package/src/platform/thinq/thinqWasherCommandHandlers.ts +120 -0
- package/src/platform/thinq/thinqWasherEndpointFactory.ts +37 -0
- package/src/platform/thinq/thinqWasherStateSync.ts +60 -0
- package/src/platform/thinq/thinqWasherStopCommandResolver.ts +62 -0
- package/src/services/authentication/AccountAuthStrategy.test.ts +358 -0
- package/src/services/authentication/AccountAuthStrategy.ts +209 -0
- package/src/services/authentication/AuthContext.ts +19 -0
- package/src/services/authentication/AuthenticationCoordinator.test.ts +217 -0
- package/src/services/authentication/AuthenticationCoordinator.ts +59 -0
- package/src/services/authentication/IAuthStrategy.ts +10 -0
- package/src/services/authentication/TokenAuthStrategy.test.ts +167 -0
- package/src/services/authentication/TokenAuthStrategy.ts +40 -0
- package/src/services/authentication/UserDataRepository.test.ts +149 -0
- package/src/services/authentication/UserDataRepository.ts +46 -0
- package/src/services/thinq/gateway.ts +86 -0
- package/src/services/thinq/mqtt/mqttCertificate.test.ts +344 -0
- package/src/services/thinq/mqtt/mqttCertificate.ts +102 -0
- package/src/services/thinq/mqtt/mqttConnectionEvents.test.ts +276 -0
- package/src/services/thinq/mqtt/mqttConnectionEvents.ts +76 -0
- package/src/services/thinq/mqtt/mqttKeyRepository.test.ts +164 -0
- package/src/services/thinq/mqtt/mqttKeyRepository.ts +43 -0
- package/src/services/thinq/mqtt/mqttRetry.test.ts +239 -0
- package/src/services/thinq/mqtt/mqttRetry.ts +43 -0
- package/src/services/thinq/mqtt/thinqMqttListener.test.ts +349 -0
- package/src/services/thinq/mqtt/thinqMqttListener.ts +120 -0
- package/src/services/thinq/serviceContainer.test.ts +203 -0
- package/src/services/thinq/serviceContainer.ts +105 -0
- package/src/services/thinq/session.test.ts +287 -0
- package/src/services/thinq/session.ts +69 -0
- package/src/services/thinq/thinqApiClient.test.ts +702 -0
- package/src/services/thinq/thinqApiClient.ts +418 -0
- package/src/services/thinq/thinqDeviceService.test.ts +415 -0
- package/src/services/thinq/thinqDeviceService.ts +101 -0
- package/src/services/thinq/thinqFilterMonitoringService.test.ts +264 -0
- package/src/services/thinq/thinqFilterMonitoringService.ts +53 -0
- package/src/settings.ts +18 -0
- package/src/tests/cli/commands/devices.test.ts +293 -0
- package/src/tests/cli/commands/login.test.ts +240 -0
- package/src/tests/cli/help.test.ts +46 -0
- package/src/tests/cli/main.test.ts +214 -0
- package/src/tests/cli/session.test.ts +198 -0
- package/src/tests/cli/utils.test.ts +194 -0
- package/src/tests/core/domain/value-objects/AirConditionerCapabilities.test.ts +685 -0
- package/src/tests/core/domain/value-objects/ThinqFilterState.test.ts +210 -0
- package/src/tests/core/domain/value-objects/ThinqSnapshot.test.ts +1035 -0
- package/src/tests/helpers/testUtils.ts +118 -0
- package/src/tests/model/LgThinqPluginPlatformConfig.test.ts +81 -0
- package/src/tests/platform/deviceRegistry.test.ts +177 -0
- package/src/tests/platform/platformConfigManager.test.ts +790 -0
- package/src/tests/platform/platformState.test.ts +41 -0
- package/src/tests/platform/thinq/thinqAirConditionerCommandHandlers.test.ts +879 -0
- package/src/tests/platform/thinq/thinqAirConditionerEndpointFactory.test.ts +993 -0
- package/src/tests/platform/thinq/thinqAirConditionerFilterResetCommandHandler.test.ts +508 -0
- package/src/tests/platform/thinq/thinqAirConditionerFilterStateSync.test.ts +212 -0
- package/src/tests/platform/thinq/thinqAirConditionerStateSync.test.ts +1586 -0
- package/src/tests/platform/thinq/thinqDeviceConfigReconciler.test.ts +269 -0
- package/src/tests/platform/thinq/thinqDeviceConfigurator.test.ts +492 -0
- package/src/tests/platform/thinq/thinqWasherCommandHandlers.test.ts +594 -0
- package/src/tests/platform/thinq/thinqWasherEndpointFactory.test.ts +84 -0
- package/src/tests/platform/thinq/thinqWasherStateSync.test.ts +373 -0
- package/src/tests/platform/thinq/thinqWasherStopCommandResolver.test.ts +392 -0
- package/src/tests/settings.test.ts +33 -0
- package/src/utils/mask.ts +50 -0
- package/tsconfig.base.json +25 -0
- package/tsconfig.json +9 -0
- package/tsconfig.production.json +13 -0
- package/tsconfig.vitest.json +5 -0
- package/vite.config.ts +43 -0
- package/wiki/memory-archive.md +12 -0
- package/wiki/reference-workspaces.md +23 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { HepaFilterMonitoring, ResourceMonitoring } from 'matterbridge/matter/clusters';
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { ThinqFilterState } from '../../../core/domain/value-objects/ThinqFilterState.js';
|
|
5
|
+
import {
|
|
6
|
+
applyThinqFilterStateToAirConditioner,
|
|
7
|
+
resolveFilterChangeIndication,
|
|
8
|
+
} from '../../../platform/thinq/thinqAirConditionerFilterStateSync.js';
|
|
9
|
+
import { asPartial, createMockLogger } from '../../helpers/testUtils.js';
|
|
10
|
+
|
|
11
|
+
function createFilterState(filterData: Record<string, unknown>): ThinqFilterState {
|
|
12
|
+
const state = ThinqFilterState.fromRaw(filterData);
|
|
13
|
+
if (!state) {
|
|
14
|
+
throw new Error('Failed to create ThinqFilterState from test data');
|
|
15
|
+
}
|
|
16
|
+
return state;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('resolveFilterChangeIndication', () => {
|
|
20
|
+
it('should return Ok for remaining percent > 10', () => {
|
|
21
|
+
expect(resolveFilterChangeIndication(100)).toBe(ResourceMonitoring.ChangeIndication.Ok);
|
|
22
|
+
expect(resolveFilterChangeIndication(50)).toBe(ResourceMonitoring.ChangeIndication.Ok);
|
|
23
|
+
expect(resolveFilterChangeIndication(11)).toBe(ResourceMonitoring.ChangeIndication.Ok);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should return Warning for remaining percent 1-10', () => {
|
|
27
|
+
expect(resolveFilterChangeIndication(10)).toBe(ResourceMonitoring.ChangeIndication.Warning);
|
|
28
|
+
expect(resolveFilterChangeIndication(5)).toBe(ResourceMonitoring.ChangeIndication.Warning);
|
|
29
|
+
expect(resolveFilterChangeIndication(1)).toBe(ResourceMonitoring.ChangeIndication.Warning);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should return Critical for remaining percent <= 0', () => {
|
|
33
|
+
expect(resolveFilterChangeIndication(0)).toBe(ResourceMonitoring.ChangeIndication.Critical);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('applyThinqFilterStateToAirConditioner', () => {
|
|
38
|
+
let mockLogger: ReturnType<typeof createMockLogger>;
|
|
39
|
+
let airConditioner: any;
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
vi.clearAllMocks();
|
|
43
|
+
mockLogger = createMockLogger();
|
|
44
|
+
airConditioner = {
|
|
45
|
+
id: 0x01,
|
|
46
|
+
name: 'Living Room AC',
|
|
47
|
+
log: mockLogger,
|
|
48
|
+
updateAttribute: vi.fn().mockResolvedValue(false),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
vi.clearAllMocks();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should skip update when filterState is undefined', async () => {
|
|
57
|
+
const updateAttributeSpy = vi.spyOn(airConditioner, 'updateAttribute').mockResolvedValue(false);
|
|
58
|
+
|
|
59
|
+
await applyThinqFilterStateToAirConditioner(airConditioner, undefined, mockLogger);
|
|
60
|
+
|
|
61
|
+
expect(updateAttributeSpy).not.toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should log debug message when filterState is undefined', async () => {
|
|
65
|
+
await applyThinqFilterStateToAirConditioner(airConditioner, undefined, mockLogger);
|
|
66
|
+
|
|
67
|
+
expect(mockLogger.debug).toHaveBeenCalledWith(
|
|
68
|
+
expect.stringContaining('no data, skipping HepaFilterMonitoring update'),
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should update condition and changeIndication when filterState is defined', async () => {
|
|
73
|
+
const filterData = {
|
|
74
|
+
'airState.filterMngStates.useTime': 11,
|
|
75
|
+
'airState.filterMngStates.maxTime': 720,
|
|
76
|
+
};
|
|
77
|
+
const filterState = createFilterState(filterData);
|
|
78
|
+
const updateAttributeSpy = vi.spyOn(airConditioner, 'updateAttribute').mockResolvedValue(false);
|
|
79
|
+
|
|
80
|
+
await applyThinqFilterStateToAirConditioner(airConditioner, filterState, mockLogger);
|
|
81
|
+
|
|
82
|
+
expect(updateAttributeSpy).toHaveBeenCalledTimes(2);
|
|
83
|
+
expect(updateAttributeSpy).toHaveBeenCalledWith(HepaFilterMonitoring.id, 'condition', 98, mockLogger);
|
|
84
|
+
expect(updateAttributeSpy).toHaveBeenCalledWith(
|
|
85
|
+
HepaFilterMonitoring.id,
|
|
86
|
+
'changeIndication',
|
|
87
|
+
ResourceMonitoring.ChangeIndication.Ok,
|
|
88
|
+
mockLogger,
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should set changeIndication to Ok when remaining percent > 10', async () => {
|
|
93
|
+
const filterData = {
|
|
94
|
+
'airState.filterMngStates.useTime': 50,
|
|
95
|
+
'airState.filterMngStates.maxTime': 720,
|
|
96
|
+
};
|
|
97
|
+
const filterState = createFilterState(filterData);
|
|
98
|
+
const updateAttributeSpy = vi.spyOn(airConditioner, 'updateAttribute').mockResolvedValue(false);
|
|
99
|
+
|
|
100
|
+
await applyThinqFilterStateToAirConditioner(airConditioner, filterState, mockLogger);
|
|
101
|
+
|
|
102
|
+
const changeIndicationCall = updateAttributeSpy.mock.calls.find(
|
|
103
|
+
(call) => call[0] === HepaFilterMonitoring.id && call[1] === 'changeIndication',
|
|
104
|
+
);
|
|
105
|
+
expect(changeIndicationCall?.[2]).toBe(ResourceMonitoring.ChangeIndication.Ok);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should set changeIndication to Warning when remaining percent 1-10', async () => {
|
|
109
|
+
const filterData = {
|
|
110
|
+
'airState.filterMngStates.useTime': 648,
|
|
111
|
+
'airState.filterMngStates.maxTime': 720,
|
|
112
|
+
};
|
|
113
|
+
const filterState = createFilterState(filterData);
|
|
114
|
+
const updateAttributeSpy = vi.spyOn(airConditioner, 'updateAttribute').mockResolvedValue(false);
|
|
115
|
+
|
|
116
|
+
await applyThinqFilterStateToAirConditioner(airConditioner, filterState, mockLogger);
|
|
117
|
+
|
|
118
|
+
const changeIndicationCall = updateAttributeSpy.mock.calls.find(
|
|
119
|
+
(call) => call[0] === HepaFilterMonitoring.id && call[1] === 'changeIndication',
|
|
120
|
+
);
|
|
121
|
+
expect(changeIndicationCall?.[2]).toBe(ResourceMonitoring.ChangeIndication.Warning);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should set changeIndication to Critical when remaining percent <= 0', async () => {
|
|
125
|
+
const filterData = {
|
|
126
|
+
'airState.filterMngStates.useTime': 720,
|
|
127
|
+
'airState.filterMngStates.maxTime': 720,
|
|
128
|
+
};
|
|
129
|
+
const filterState = createFilterState(filterData);
|
|
130
|
+
const updateAttributeSpy = vi.spyOn(airConditioner, 'updateAttribute').mockResolvedValue(false);
|
|
131
|
+
|
|
132
|
+
await applyThinqFilterStateToAirConditioner(airConditioner, filterState, mockLogger);
|
|
133
|
+
|
|
134
|
+
const changeIndicationCall = updateAttributeSpy.mock.calls.find(
|
|
135
|
+
(call) => call[0] === HepaFilterMonitoring.id && call[1] === 'changeIndication',
|
|
136
|
+
);
|
|
137
|
+
expect(changeIndicationCall?.[2]).toBe(ResourceMonitoring.ChangeIndication.Critical);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should handle edge case with useTime > maxTime (clamped to 0)', async () => {
|
|
141
|
+
const filterData = {
|
|
142
|
+
'airState.filterMngStates.useTime': 800,
|
|
143
|
+
'airState.filterMngStates.maxTime': 720,
|
|
144
|
+
};
|
|
145
|
+
const filterState = createFilterState(filterData);
|
|
146
|
+
const updateAttributeSpy = vi.spyOn(airConditioner, 'updateAttribute').mockResolvedValue(false);
|
|
147
|
+
|
|
148
|
+
await applyThinqFilterStateToAirConditioner(airConditioner, filterState, mockLogger);
|
|
149
|
+
|
|
150
|
+
const conditionCall = updateAttributeSpy.mock.calls.find(
|
|
151
|
+
(call) => call[0] === HepaFilterMonitoring.id && call[1] === 'condition',
|
|
152
|
+
);
|
|
153
|
+
expect(conditionCall?.[2]).toBe(0);
|
|
154
|
+
|
|
155
|
+
const changeIndicationCall = updateAttributeSpy.mock.calls.find(
|
|
156
|
+
(call) => call[0] === HepaFilterMonitoring.id && call[1] === 'changeIndication',
|
|
157
|
+
);
|
|
158
|
+
expect(changeIndicationCall?.[2]).toBe(ResourceMonitoring.ChangeIndication.Critical);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should use correct cluster and attribute IDs', async () => {
|
|
162
|
+
const filterData = {
|
|
163
|
+
'airState.filterMngStates.useTime': 11,
|
|
164
|
+
'airState.filterMngStates.maxTime': 720,
|
|
165
|
+
};
|
|
166
|
+
const filterState = createFilterState(filterData);
|
|
167
|
+
const updateAttributeSpy = vi.spyOn(airConditioner, 'updateAttribute').mockResolvedValue(false);
|
|
168
|
+
|
|
169
|
+
await applyThinqFilterStateToAirConditioner(airConditioner, filterState, mockLogger);
|
|
170
|
+
|
|
171
|
+
// Verify the cluster ID (113 = 0x71 for HepaFilterMonitoring)
|
|
172
|
+
expect(updateAttributeSpy).toHaveBeenCalledWith(
|
|
173
|
+
expect.any(Number),
|
|
174
|
+
expect.any(String),
|
|
175
|
+
expect.any(Number),
|
|
176
|
+
mockLogger,
|
|
177
|
+
);
|
|
178
|
+
const calls = updateAttributeSpy.mock.calls;
|
|
179
|
+
expect(calls[0][0]).toBe(HepaFilterMonitoring.id);
|
|
180
|
+
expect(calls[1][0]).toBe(HepaFilterMonitoring.id);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should propagate updateAttribute errors', async () => {
|
|
184
|
+
const filterData = {
|
|
185
|
+
'airState.filterMngStates.useTime': 11,
|
|
186
|
+
'airState.filterMngStates.maxTime': 720,
|
|
187
|
+
};
|
|
188
|
+
const filterState = createFilterState(filterData);
|
|
189
|
+
const error = new Error('Update failed');
|
|
190
|
+
vi.spyOn(airConditioner, 'updateAttribute').mockRejectedValue(error);
|
|
191
|
+
|
|
192
|
+
await expect(applyThinqFilterStateToAirConditioner(airConditioner, filterState, mockLogger)).rejects.toThrow(
|
|
193
|
+
'Update failed',
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should work with minimal endpoint mock (matching test pattern)', async () => {
|
|
198
|
+
const filterData = {
|
|
199
|
+
'airState.filterMngStates.useTime': 11,
|
|
200
|
+
'airState.filterMngStates.maxTime': 720,
|
|
201
|
+
};
|
|
202
|
+
const filterState = createFilterState(filterData);
|
|
203
|
+
|
|
204
|
+
const minimalEndpoint: any = {
|
|
205
|
+
updateAttribute: vi.fn().mockResolvedValue(false),
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
await applyThinqFilterStateToAirConditioner(minimalEndpoint, filterState, mockLogger);
|
|
209
|
+
|
|
210
|
+
expect(minimalEndpoint.updateAttribute).toHaveBeenCalledTimes(2);
|
|
211
|
+
});
|
|
212
|
+
});
|