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,1035 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { ThinqSnapshot } from '../../../../core/domain/value-objects/ThinqSnapshot.js';
|
|
4
|
+
|
|
5
|
+
describe('ThinqSnapshot', () => {
|
|
6
|
+
describe('raw getter', () => {
|
|
7
|
+
it('should return the constructor input as-is', () => {
|
|
8
|
+
// Arrange
|
|
9
|
+
const data = {
|
|
10
|
+
'airState.operation': 1,
|
|
11
|
+
'airState.tempState.current': 22,
|
|
12
|
+
foo: 'bar',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Act
|
|
16
|
+
const snapshot = new ThinqSnapshot(data);
|
|
17
|
+
|
|
18
|
+
// Assert
|
|
19
|
+
expect(snapshot.raw).toBe(data);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('isPowerOn', () => {
|
|
24
|
+
it('should return true when airState.operation is 1', () => {
|
|
25
|
+
// Arrange
|
|
26
|
+
const snapshot = new ThinqSnapshot({ 'airState.operation': 1 });
|
|
27
|
+
|
|
28
|
+
// Assert
|
|
29
|
+
expect(snapshot.isPowerOn).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should return false when airState.operation is 0', () => {
|
|
33
|
+
// Arrange
|
|
34
|
+
const snapshot = new ThinqSnapshot({ 'airState.operation': 0 });
|
|
35
|
+
|
|
36
|
+
// Assert
|
|
37
|
+
expect(snapshot.isPowerOn).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return false when airState.operation is absent', () => {
|
|
41
|
+
// Arrange
|
|
42
|
+
const snapshot = new ThinqSnapshot({});
|
|
43
|
+
|
|
44
|
+
// Assert
|
|
45
|
+
expect(snapshot.isPowerOn).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should return false when airState.operation is a non-numeric type', () => {
|
|
49
|
+
// Arrange
|
|
50
|
+
const snapshot = new ThinqSnapshot({ 'airState.operation': 'on' });
|
|
51
|
+
|
|
52
|
+
// Assert
|
|
53
|
+
expect(snapshot.isPowerOn).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('currentTemperatureCelsius', () => {
|
|
58
|
+
it('should return the number value when present', () => {
|
|
59
|
+
// Arrange
|
|
60
|
+
const snapshot = new ThinqSnapshot({ 'airState.tempState.current': 22 });
|
|
61
|
+
|
|
62
|
+
// Assert
|
|
63
|
+
expect(snapshot.currentTemperatureCelsius).toBe(22);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should return undefined when key is absent', () => {
|
|
67
|
+
// Arrange
|
|
68
|
+
const snapshot = new ThinqSnapshot({});
|
|
69
|
+
|
|
70
|
+
// Assert
|
|
71
|
+
expect(snapshot.currentTemperatureCelsius).toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should return undefined when value is not a number', () => {
|
|
75
|
+
// Arrange
|
|
76
|
+
const snapshot = new ThinqSnapshot({ 'airState.tempState.current': '22' });
|
|
77
|
+
|
|
78
|
+
// Assert
|
|
79
|
+
expect(snapshot.currentTemperatureCelsius).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('targetTemperatureCelsius', () => {
|
|
84
|
+
it('should return the number value when present', () => {
|
|
85
|
+
// Arrange
|
|
86
|
+
const snapshot = new ThinqSnapshot({ 'airState.tempState.target': 24 });
|
|
87
|
+
|
|
88
|
+
// Assert
|
|
89
|
+
expect(snapshot.targetTemperatureCelsius).toBe(24);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should return undefined when key is absent', () => {
|
|
93
|
+
// Arrange
|
|
94
|
+
const snapshot = new ThinqSnapshot({});
|
|
95
|
+
|
|
96
|
+
// Assert
|
|
97
|
+
expect(snapshot.targetTemperatureCelsius).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return undefined when value is not a number', () => {
|
|
101
|
+
// Arrange
|
|
102
|
+
const snapshot = new ThinqSnapshot({ 'airState.tempState.target': '24' });
|
|
103
|
+
|
|
104
|
+
// Assert
|
|
105
|
+
expect(snapshot.targetTemperatureCelsius).toBeUndefined();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('operationMode', () => {
|
|
110
|
+
it('should return the number value when present', () => {
|
|
111
|
+
// Arrange
|
|
112
|
+
const snapshot = new ThinqSnapshot({ 'airState.opMode': 0 });
|
|
113
|
+
|
|
114
|
+
// Assert
|
|
115
|
+
expect(snapshot.operationMode).toBe(0);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should return undefined when key is absent', () => {
|
|
119
|
+
// Arrange
|
|
120
|
+
const snapshot = new ThinqSnapshot({});
|
|
121
|
+
|
|
122
|
+
// Assert
|
|
123
|
+
expect(snapshot.operationMode).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should return undefined when value is not a number', () => {
|
|
127
|
+
// Arrange
|
|
128
|
+
const snapshot = new ThinqSnapshot({ 'airState.opMode': 'cool' });
|
|
129
|
+
|
|
130
|
+
// Assert
|
|
131
|
+
expect(snapshot.operationMode).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('windStrength', () => {
|
|
136
|
+
it('should return the number value when present', () => {
|
|
137
|
+
// Arrange
|
|
138
|
+
const snapshot = new ThinqSnapshot({ 'airState.windStrength': 2 });
|
|
139
|
+
|
|
140
|
+
// Assert
|
|
141
|
+
expect(snapshot.windStrength).toBe(2);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should return undefined when key is absent', () => {
|
|
145
|
+
// Arrange
|
|
146
|
+
const snapshot = new ThinqSnapshot({});
|
|
147
|
+
|
|
148
|
+
// Assert
|
|
149
|
+
expect(snapshot.windStrength).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should return undefined when value is not a number', () => {
|
|
153
|
+
// Arrange
|
|
154
|
+
const snapshot = new ThinqSnapshot({ 'airState.windStrength': 'low' });
|
|
155
|
+
|
|
156
|
+
// Assert
|
|
157
|
+
expect(snapshot.windStrength).toBeUndefined();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('online', () => {
|
|
162
|
+
it('should return true when online is boolean true', () => {
|
|
163
|
+
// Arrange
|
|
164
|
+
const snapshot = new ThinqSnapshot({ online: true });
|
|
165
|
+
|
|
166
|
+
// Assert
|
|
167
|
+
expect(snapshot.online).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should return false when online is boolean false', () => {
|
|
171
|
+
// Arrange
|
|
172
|
+
const snapshot = new ThinqSnapshot({ online: false });
|
|
173
|
+
|
|
174
|
+
// Assert
|
|
175
|
+
expect(snapshot.online).toBe(false);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should return undefined when online is absent', () => {
|
|
179
|
+
// Arrange
|
|
180
|
+
const snapshot = new ThinqSnapshot({});
|
|
181
|
+
|
|
182
|
+
// Assert
|
|
183
|
+
expect(snapshot.online).toBeUndefined();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should return undefined when online is not a boolean', () => {
|
|
187
|
+
// Arrange
|
|
188
|
+
const snapshot = new ThinqSnapshot({ online: 'yes' });
|
|
189
|
+
|
|
190
|
+
// Assert
|
|
191
|
+
expect(snapshot.online).toBeUndefined();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('isJetModeOn', () => {
|
|
196
|
+
it('should return true when airState.wMode.jet is 1', () => {
|
|
197
|
+
// Arrange
|
|
198
|
+
const snapshot = new ThinqSnapshot({ 'airState.wMode.jet': 1 });
|
|
199
|
+
|
|
200
|
+
// Assert
|
|
201
|
+
expect(snapshot.isJetModeOn).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should return false when airState.wMode.jet is 0', () => {
|
|
205
|
+
// Arrange
|
|
206
|
+
const snapshot = new ThinqSnapshot({ 'airState.wMode.jet': 0 });
|
|
207
|
+
|
|
208
|
+
// Assert
|
|
209
|
+
expect(snapshot.isJetModeOn).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should return false when airState.wMode.jet is absent', () => {
|
|
213
|
+
// Arrange
|
|
214
|
+
const snapshot = new ThinqSnapshot({});
|
|
215
|
+
|
|
216
|
+
// Assert
|
|
217
|
+
expect(snapshot.isJetModeOn).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe('isQuietModeOn', () => {
|
|
222
|
+
it('should return true when airState.miscFuncState.silentAWHP is 1', () => {
|
|
223
|
+
// Arrange
|
|
224
|
+
const snapshot = new ThinqSnapshot({ 'airState.miscFuncState.silentAWHP': 1 });
|
|
225
|
+
|
|
226
|
+
// Assert
|
|
227
|
+
expect(snapshot.isQuietModeOn).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should return false when airState.miscFuncState.silentAWHP is 0', () => {
|
|
231
|
+
// Arrange
|
|
232
|
+
const snapshot = new ThinqSnapshot({ 'airState.miscFuncState.silentAWHP': 0 });
|
|
233
|
+
|
|
234
|
+
// Assert
|
|
235
|
+
expect(snapshot.isQuietModeOn).toBe(false);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should return false when airState.miscFuncState.silentAWHP is absent', () => {
|
|
239
|
+
// Arrange
|
|
240
|
+
const snapshot = new ThinqSnapshot({});
|
|
241
|
+
|
|
242
|
+
// Assert
|
|
243
|
+
expect(snapshot.isQuietModeOn).toBe(false);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('isEnergySaveModeOn', () => {
|
|
248
|
+
it('should return true when airState.powerSave.basic is 1', () => {
|
|
249
|
+
// Arrange
|
|
250
|
+
const snapshot = new ThinqSnapshot({ 'airState.powerSave.basic': 1 });
|
|
251
|
+
|
|
252
|
+
// Assert
|
|
253
|
+
expect(snapshot.isEnergySaveModeOn).toBe(true);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should return false when airState.powerSave.basic is 0', () => {
|
|
257
|
+
// Arrange
|
|
258
|
+
const snapshot = new ThinqSnapshot({ 'airState.powerSave.basic': 0 });
|
|
259
|
+
|
|
260
|
+
// Assert
|
|
261
|
+
expect(snapshot.isEnergySaveModeOn).toBe(false);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should return false when airState.powerSave.basic is absent', () => {
|
|
265
|
+
// Arrange
|
|
266
|
+
const snapshot = new ThinqSnapshot({});
|
|
267
|
+
|
|
268
|
+
// Assert
|
|
269
|
+
expect(snapshot.isEnergySaveModeOn).toBe(false);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('isAirCleanModeOn', () => {
|
|
274
|
+
it('should return true when airState.wMode.airClean is 1', () => {
|
|
275
|
+
// Arrange
|
|
276
|
+
const snapshot = new ThinqSnapshot({ 'airState.wMode.airClean': 1 });
|
|
277
|
+
|
|
278
|
+
// Assert
|
|
279
|
+
expect(snapshot.isAirCleanModeOn).toBe(true);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should return false when airState.wMode.airClean is 0', () => {
|
|
283
|
+
// Arrange
|
|
284
|
+
const snapshot = new ThinqSnapshot({ 'airState.wMode.airClean': 0 });
|
|
285
|
+
|
|
286
|
+
// Assert
|
|
287
|
+
expect(snapshot.isAirCleanModeOn).toBe(false);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should return false when airState.wMode.airClean is absent', () => {
|
|
291
|
+
// Arrange
|
|
292
|
+
const snapshot = new ThinqSnapshot({});
|
|
293
|
+
|
|
294
|
+
// Assert
|
|
295
|
+
expect(snapshot.isAirCleanModeOn).toBe(false);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe('isLedOn', () => {
|
|
300
|
+
it('should return true when airState.lightingState.displayControl is 1', () => {
|
|
301
|
+
// Arrange
|
|
302
|
+
const snapshot = new ThinqSnapshot({ 'airState.lightingState.displayControl': 1 });
|
|
303
|
+
|
|
304
|
+
// Assert
|
|
305
|
+
expect(snapshot.isLedOn).toBe(true);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should return false when airState.lightingState.displayControl is 0', () => {
|
|
309
|
+
// Arrange
|
|
310
|
+
const snapshot = new ThinqSnapshot({ 'airState.lightingState.displayControl': 0 });
|
|
311
|
+
|
|
312
|
+
// Assert
|
|
313
|
+
expect(snapshot.isLedOn).toBe(false);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should return false when airState.lightingState.displayControl is absent', () => {
|
|
317
|
+
// Arrange
|
|
318
|
+
const snapshot = new ThinqSnapshot({});
|
|
319
|
+
|
|
320
|
+
// Assert
|
|
321
|
+
expect(snapshot.isLedOn).toBe(false);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe('isVerticalSwingOn (Phase B)', () => {
|
|
326
|
+
it('should return true when airState.wDir.vStep is 100 (numeric)', () => {
|
|
327
|
+
// Arrange
|
|
328
|
+
const snapshot = new ThinqSnapshot({ 'airState.wDir.vStep': 100 });
|
|
329
|
+
|
|
330
|
+
// Assert
|
|
331
|
+
expect(snapshot.isVerticalSwingOn).toBe(true);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('should return true when airState.wDir.vStep is "100" (string)', () => {
|
|
335
|
+
// Arrange
|
|
336
|
+
const snapshot = new ThinqSnapshot({ 'airState.wDir.vStep': '100' });
|
|
337
|
+
|
|
338
|
+
// Assert
|
|
339
|
+
expect(snapshot.isVerticalSwingOn).toBe(true);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should return false when airState.wDir.vStep is 0', () => {
|
|
343
|
+
// Arrange
|
|
344
|
+
const snapshot = new ThinqSnapshot({ 'airState.wDir.vStep': 0 });
|
|
345
|
+
|
|
346
|
+
// Assert
|
|
347
|
+
expect(snapshot.isVerticalSwingOn).toBe(false);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should return false when airState.wDir.vStep is absent', () => {
|
|
351
|
+
// Arrange
|
|
352
|
+
const snapshot = new ThinqSnapshot({});
|
|
353
|
+
|
|
354
|
+
// Assert
|
|
355
|
+
expect(snapshot.isVerticalSwingOn).toBe(false);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should return false when airState.wDir.vStep is any other number', () => {
|
|
359
|
+
// Arrange
|
|
360
|
+
const snapshot = new ThinqSnapshot({ 'airState.wDir.vStep': 50 });
|
|
361
|
+
|
|
362
|
+
// Assert
|
|
363
|
+
expect(snapshot.isVerticalSwingOn).toBe(false);
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
describe('isHorizontalSwingOn (Phase B)', () => {
|
|
368
|
+
it('should return true when airState.wDir.hStep is 100 (numeric)', () => {
|
|
369
|
+
// Arrange
|
|
370
|
+
const snapshot = new ThinqSnapshot({ 'airState.wDir.hStep': 100 });
|
|
371
|
+
|
|
372
|
+
// Assert
|
|
373
|
+
expect(snapshot.isHorizontalSwingOn).toBe(true);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should return true when airState.wDir.hStep is "100" (string)', () => {
|
|
377
|
+
// Arrange
|
|
378
|
+
const snapshot = new ThinqSnapshot({ 'airState.wDir.hStep': '100' });
|
|
379
|
+
|
|
380
|
+
// Assert
|
|
381
|
+
expect(snapshot.isHorizontalSwingOn).toBe(true);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should return false when airState.wDir.hStep is 0', () => {
|
|
385
|
+
// Arrange
|
|
386
|
+
const snapshot = new ThinqSnapshot({ 'airState.wDir.hStep': 0 });
|
|
387
|
+
|
|
388
|
+
// Assert
|
|
389
|
+
expect(snapshot.isHorizontalSwingOn).toBe(false);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('should return false when airState.wDir.hStep is absent', () => {
|
|
393
|
+
// Arrange
|
|
394
|
+
const snapshot = new ThinqSnapshot({});
|
|
395
|
+
|
|
396
|
+
// Assert
|
|
397
|
+
expect(snapshot.isHorizontalSwingOn).toBe(false);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('should return false when airState.wDir.hStep is any other number', () => {
|
|
401
|
+
// Arrange
|
|
402
|
+
const snapshot = new ThinqSnapshot({ 'airState.wDir.hStep': 50 });
|
|
403
|
+
|
|
404
|
+
// Assert
|
|
405
|
+
expect(snapshot.isHorizontalSwingOn).toBe(false);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
describe('humidityPercent (Phase C)', () => {
|
|
410
|
+
it('should return the value directly when <= 100', () => {
|
|
411
|
+
// Arrange
|
|
412
|
+
const snapshot = new ThinqSnapshot({ 'airState.humidity.current': 55 });
|
|
413
|
+
|
|
414
|
+
// Assert
|
|
415
|
+
expect(snapshot.humidityPercent).toBe(55);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('should return the value divided by 10 when > 100', () => {
|
|
419
|
+
// Arrange
|
|
420
|
+
const snapshot = new ThinqSnapshot({ 'airState.humidity.current': 550 });
|
|
421
|
+
|
|
422
|
+
// Assert
|
|
423
|
+
expect(snapshot.humidityPercent).toBe(55);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('should return exactly 100 when value is 100', () => {
|
|
427
|
+
// Arrange
|
|
428
|
+
const snapshot = new ThinqSnapshot({ 'airState.humidity.current': 100 });
|
|
429
|
+
|
|
430
|
+
// Assert
|
|
431
|
+
expect(snapshot.humidityPercent).toBe(100);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('should return undefined when key is absent', () => {
|
|
435
|
+
// Arrange
|
|
436
|
+
const snapshot = new ThinqSnapshot({});
|
|
437
|
+
|
|
438
|
+
// Assert
|
|
439
|
+
expect(snapshot.humidityPercent).toBeUndefined();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('should return undefined when value is not a number', () => {
|
|
443
|
+
// Arrange
|
|
444
|
+
const snapshot = new ThinqSnapshot({ 'airState.humidity.current': '55' });
|
|
445
|
+
|
|
446
|
+
// Assert
|
|
447
|
+
expect(snapshot.humidityPercent).toBeUndefined();
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('should apply heuristic to large values (1000 → 100)', () => {
|
|
451
|
+
// Arrange
|
|
452
|
+
const snapshot = new ThinqSnapshot({ 'airState.humidity.current': 1000 });
|
|
453
|
+
|
|
454
|
+
// Assert
|
|
455
|
+
expect(snapshot.humidityPercent).toBe(100);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('should apply heuristic to small > 100 values (101 → 10.1)', () => {
|
|
459
|
+
// Arrange
|
|
460
|
+
const snapshot = new ThinqSnapshot({ 'airState.humidity.current': 101 });
|
|
461
|
+
|
|
462
|
+
// Assert
|
|
463
|
+
expect(snapshot.humidityPercent).toBe(10.1);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('should apply heuristic to common > 100 value (650 → 65)', () => {
|
|
467
|
+
// Arrange
|
|
468
|
+
const snapshot = new ThinqSnapshot({ 'airState.humidity.current': 650 });
|
|
469
|
+
|
|
470
|
+
// Assert
|
|
471
|
+
expect(snapshot.humidityPercent).toBe(65);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
describe('airQualityOverall (Phase C)', () => {
|
|
476
|
+
it('should return the number value when present', () => {
|
|
477
|
+
// Arrange
|
|
478
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.overall': 3 });
|
|
479
|
+
|
|
480
|
+
// Assert
|
|
481
|
+
expect(snapshot.airQualityOverall).toBe(3);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('should return undefined when key is absent', () => {
|
|
485
|
+
// Arrange
|
|
486
|
+
const snapshot = new ThinqSnapshot({});
|
|
487
|
+
|
|
488
|
+
// Assert
|
|
489
|
+
expect(snapshot.airQualityOverall).toBeUndefined();
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('should return undefined when value is not a number', () => {
|
|
493
|
+
// Arrange
|
|
494
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.overall': 'Good' });
|
|
495
|
+
|
|
496
|
+
// Assert
|
|
497
|
+
expect(snapshot.airQualityOverall).toBeUndefined();
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('should return 0 for poor air quality', () => {
|
|
501
|
+
// Arrange
|
|
502
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.overall': 0 });
|
|
503
|
+
|
|
504
|
+
// Assert
|
|
505
|
+
expect(snapshot.airQualityOverall).toBe(0);
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
describe('pm25 (Phase C)', () => {
|
|
510
|
+
it('should return the number value when present', () => {
|
|
511
|
+
// Arrange
|
|
512
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.PM2': 25 });
|
|
513
|
+
|
|
514
|
+
// Assert
|
|
515
|
+
expect(snapshot.pm25).toBe(25);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('should return undefined when key is absent', () => {
|
|
519
|
+
// Arrange
|
|
520
|
+
const snapshot = new ThinqSnapshot({});
|
|
521
|
+
|
|
522
|
+
// Assert
|
|
523
|
+
expect(snapshot.pm25).toBeUndefined();
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it('should return undefined when value is not a number', () => {
|
|
527
|
+
// Arrange
|
|
528
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.PM2': '25' });
|
|
529
|
+
|
|
530
|
+
// Assert
|
|
531
|
+
expect(snapshot.pm25).toBeUndefined();
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it('should return 0 for zero PM2.5 reading', () => {
|
|
535
|
+
// Arrange
|
|
536
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.PM2': 0 });
|
|
537
|
+
|
|
538
|
+
// Assert
|
|
539
|
+
expect(snapshot.pm25).toBe(0);
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
describe('pm10 (Phase C)', () => {
|
|
544
|
+
it('should return the number value when present', () => {
|
|
545
|
+
// Arrange
|
|
546
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.PM10': 50 });
|
|
547
|
+
|
|
548
|
+
// Assert
|
|
549
|
+
expect(snapshot.pm10).toBe(50);
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('should return undefined when key is absent', () => {
|
|
553
|
+
// Arrange
|
|
554
|
+
const snapshot = new ThinqSnapshot({});
|
|
555
|
+
|
|
556
|
+
// Assert
|
|
557
|
+
expect(snapshot.pm10).toBeUndefined();
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should return undefined when value is not a number', () => {
|
|
561
|
+
// Arrange
|
|
562
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.PM10': '50' });
|
|
563
|
+
|
|
564
|
+
// Assert
|
|
565
|
+
expect(snapshot.pm10).toBeUndefined();
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('should return 0 for zero PM10 reading', () => {
|
|
569
|
+
// Arrange
|
|
570
|
+
const snapshot = new ThinqSnapshot({ 'airState.quality.PM10': 0 });
|
|
571
|
+
|
|
572
|
+
// Assert
|
|
573
|
+
expect(snapshot.pm10).toBe(0);
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
describe('powerConsumptionWatts (Phase D)', () => {
|
|
578
|
+
it('should return the raw value when present (no division)', () => {
|
|
579
|
+
// Arrange
|
|
580
|
+
const snapshot = new ThinqSnapshot({ 'airState.energy.onCurrent': 500 });
|
|
581
|
+
|
|
582
|
+
// Assert
|
|
583
|
+
expect(snapshot.powerConsumptionWatts).toBe(500);
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it('should return 0 when airState.energy.onCurrent is 0', () => {
|
|
587
|
+
// Arrange
|
|
588
|
+
const snapshot = new ThinqSnapshot({ 'airState.energy.onCurrent': 0 });
|
|
589
|
+
|
|
590
|
+
// Assert
|
|
591
|
+
expect(snapshot.powerConsumptionWatts).toBe(0);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('should return undefined when key is absent', () => {
|
|
595
|
+
// Arrange
|
|
596
|
+
const snapshot = new ThinqSnapshot({});
|
|
597
|
+
|
|
598
|
+
// Assert
|
|
599
|
+
expect(snapshot.powerConsumptionWatts).toBeUndefined();
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('should return undefined when value is not a number', () => {
|
|
603
|
+
// Arrange
|
|
604
|
+
const snapshot = new ThinqSnapshot({ 'airState.energy.onCurrent': '500' });
|
|
605
|
+
|
|
606
|
+
// Assert
|
|
607
|
+
expect(snapshot.powerConsumptionWatts).toBeUndefined();
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it('should return undefined when the result is NaN', () => {
|
|
611
|
+
// Arrange
|
|
612
|
+
const snapshot = new ThinqSnapshot({ 'airState.energy.onCurrent': NaN });
|
|
613
|
+
|
|
614
|
+
// Assert
|
|
615
|
+
expect(snapshot.powerConsumptionWatts).toBeUndefined();
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
it('should return raw value for intermediate powers (999 → 999)', () => {
|
|
619
|
+
// Arrange
|
|
620
|
+
const snapshot = new ThinqSnapshot({ 'airState.energy.onCurrent': 999 });
|
|
621
|
+
|
|
622
|
+
// Assert
|
|
623
|
+
expect(snapshot.powerConsumptionWatts).toBe(999);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it('should handle large power values (5000 → 5000)', () => {
|
|
627
|
+
// Arrange
|
|
628
|
+
const snapshot = new ThinqSnapshot({ 'airState.energy.onCurrent': 5000 });
|
|
629
|
+
|
|
630
|
+
// Assert
|
|
631
|
+
expect(snapshot.powerConsumptionWatts).toBe(5000);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it('should handle fractional onCurrent values (1.5 → 1.5)', () => {
|
|
635
|
+
// Arrange
|
|
636
|
+
const snapshot = new ThinqSnapshot({ 'airState.energy.onCurrent': 1.5 });
|
|
637
|
+
|
|
638
|
+
// Assert
|
|
639
|
+
expect(snapshot.powerConsumptionWatts).toBe(1.5);
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
describe('has', () => {
|
|
644
|
+
it('should return true when key has a number value', () => {
|
|
645
|
+
// Arrange
|
|
646
|
+
const snapshot = new ThinqSnapshot({ 'airState.operation': 1 });
|
|
647
|
+
|
|
648
|
+
// Assert
|
|
649
|
+
expect(snapshot.has('airState.operation')).toBe(true);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('should return true when key has value 0 (falsy number)', () => {
|
|
653
|
+
// Arrange
|
|
654
|
+
const snapshot = new ThinqSnapshot({ 'airState.operation': 0 });
|
|
655
|
+
|
|
656
|
+
// Assert
|
|
657
|
+
expect(snapshot.has('airState.operation')).toBe(true);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it('should return true when key has a false boolean value', () => {
|
|
661
|
+
// Arrange
|
|
662
|
+
const snapshot = new ThinqSnapshot({ online: false });
|
|
663
|
+
|
|
664
|
+
// Assert
|
|
665
|
+
expect(snapshot.has('online')).toBe(true);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('should return true when key has an empty string value', () => {
|
|
669
|
+
// Arrange
|
|
670
|
+
const snapshot = new ThinqSnapshot({ foo: '' });
|
|
671
|
+
|
|
672
|
+
// Assert
|
|
673
|
+
expect(snapshot.has('foo')).toBe(true);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it('should return false when key is absent', () => {
|
|
677
|
+
// Arrange
|
|
678
|
+
const snapshot = new ThinqSnapshot({});
|
|
679
|
+
|
|
680
|
+
// Assert
|
|
681
|
+
expect(snapshot.has('airState.operation')).toBe(false);
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it('should return false when key value is undefined', () => {
|
|
685
|
+
// Arrange
|
|
686
|
+
const snapshot = new ThinqSnapshot({ 'airState.operation': undefined });
|
|
687
|
+
|
|
688
|
+
// Assert
|
|
689
|
+
expect(snapshot.has('airState.operation')).toBe(false);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('should return false when key value is null', () => {
|
|
693
|
+
// Arrange
|
|
694
|
+
const snapshot = new ThinqSnapshot({ 'airState.operation': null });
|
|
695
|
+
|
|
696
|
+
// Assert
|
|
697
|
+
expect(snapshot.has('airState.operation')).toBe(false);
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
it('regression: isPowerOn should remain false when key is absent (existing behavior)', () => {
|
|
701
|
+
// Arrange
|
|
702
|
+
const snapshot = new ThinqSnapshot({});
|
|
703
|
+
|
|
704
|
+
// Assert
|
|
705
|
+
expect(snapshot.isPowerOn).toBe(false);
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
describe('washerRawState', () => {
|
|
710
|
+
it('should return the washerDryer.state string when present', () => {
|
|
711
|
+
// Arrange
|
|
712
|
+
const snapshot = new ThinqSnapshot({
|
|
713
|
+
washerDryer: { state: 'RUNNING', remainTimeHour: 1 },
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
// Act & Assert
|
|
717
|
+
expect(snapshot.washerRawState).toBe('RUNNING');
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it('should return undefined when washerDryer is absent', () => {
|
|
721
|
+
// Arrange
|
|
722
|
+
const snapshot = new ThinqSnapshot({});
|
|
723
|
+
|
|
724
|
+
// Act & Assert
|
|
725
|
+
expect(snapshot.washerRawState).toBeUndefined();
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it('should return undefined when washerDryer.state is absent', () => {
|
|
729
|
+
// Arrange
|
|
730
|
+
const snapshot = new ThinqSnapshot({
|
|
731
|
+
washerDryer: { remainTimeHour: 1 },
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
// Act & Assert
|
|
735
|
+
expect(snapshot.washerRawState).toBeUndefined();
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
it('should return undefined when washerDryer.state is not a string', () => {
|
|
739
|
+
// Arrange
|
|
740
|
+
const snapshot = new ThinqSnapshot({
|
|
741
|
+
washerDryer: { state: 123 },
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
// Act & Assert
|
|
745
|
+
expect(snapshot.washerRawState).toBeUndefined();
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
describe('isWasherPowerOn', () => {
|
|
750
|
+
it('should return true when washerRawState is RUNNING', () => {
|
|
751
|
+
// Arrange
|
|
752
|
+
const snapshot = new ThinqSnapshot({
|
|
753
|
+
washerDryer: { state: 'RUNNING' },
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
// Act & Assert
|
|
757
|
+
expect(snapshot.isWasherPowerOn).toBe(true);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
it('should return true when washerRawState is PAUSE', () => {
|
|
761
|
+
// Arrange
|
|
762
|
+
const snapshot = new ThinqSnapshot({
|
|
763
|
+
washerDryer: { state: 'PAUSE' },
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
// Act & Assert
|
|
767
|
+
expect(snapshot.isWasherPowerOn).toBe(true);
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
it('should return true when washerRawState is END', () => {
|
|
771
|
+
// Arrange
|
|
772
|
+
const snapshot = new ThinqSnapshot({
|
|
773
|
+
washerDryer: { state: 'END' },
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// Act & Assert
|
|
777
|
+
expect(snapshot.isWasherPowerOn).toBe(true);
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('should return true when washerRawState is ERROR', () => {
|
|
781
|
+
// Arrange
|
|
782
|
+
const snapshot = new ThinqSnapshot({
|
|
783
|
+
washerDryer: { state: 'ERROR' },
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
// Act & Assert
|
|
787
|
+
expect(snapshot.isWasherPowerOn).toBe(true);
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
it('should return false when washerRawState is POWEROFF', () => {
|
|
791
|
+
// Arrange
|
|
792
|
+
const snapshot = new ThinqSnapshot({
|
|
793
|
+
washerDryer: { state: 'POWEROFF' },
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// Act & Assert
|
|
797
|
+
expect(snapshot.isWasherPowerOn).toBe(false);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('should return false when washerRawState is POWERFAIL', () => {
|
|
801
|
+
// Arrange
|
|
802
|
+
const snapshot = new ThinqSnapshot({
|
|
803
|
+
washerDryer: { state: 'POWERFAIL' },
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// Act & Assert
|
|
807
|
+
expect(snapshot.isWasherPowerOn).toBe(false);
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
it('should return false when washerDryer is absent', () => {
|
|
811
|
+
// Arrange
|
|
812
|
+
const snapshot = new ThinqSnapshot({});
|
|
813
|
+
|
|
814
|
+
// Act & Assert
|
|
815
|
+
expect(snapshot.isWasherPowerOn).toBe(false);
|
|
816
|
+
});
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
describe('isWasherRunning', () => {
|
|
820
|
+
it('should return true when washerRawState is RUNNING', () => {
|
|
821
|
+
// Arrange
|
|
822
|
+
const snapshot = new ThinqSnapshot({
|
|
823
|
+
washerDryer: { state: 'RUNNING' },
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
// Act & Assert
|
|
827
|
+
expect(snapshot.isWasherRunning).toBe(true);
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
it('should return false when washerRawState is PAUSE (not running, even if powered on)', () => {
|
|
831
|
+
// Arrange
|
|
832
|
+
const snapshot = new ThinqSnapshot({
|
|
833
|
+
washerDryer: { state: 'PAUSE' },
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
// Act & Assert
|
|
837
|
+
expect(snapshot.isWasherRunning).toBe(false);
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
it('should return false when washerRawState is END (finished, not running)', () => {
|
|
841
|
+
// Arrange
|
|
842
|
+
const snapshot = new ThinqSnapshot({
|
|
843
|
+
washerDryer: { state: 'END' },
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
// Act & Assert
|
|
847
|
+
expect(snapshot.isWasherRunning).toBe(false);
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
it('should return false when washerRawState is POWEROFF', () => {
|
|
851
|
+
// Arrange
|
|
852
|
+
const snapshot = new ThinqSnapshot({
|
|
853
|
+
washerDryer: { state: 'POWEROFF' },
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
// Act & Assert
|
|
857
|
+
expect(snapshot.isWasherRunning).toBe(false);
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
it('should return false when washerRawState is ERROR', () => {
|
|
861
|
+
// Arrange
|
|
862
|
+
const snapshot = new ThinqSnapshot({
|
|
863
|
+
washerDryer: { state: 'ERROR' },
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
// Act & Assert
|
|
867
|
+
expect(snapshot.isWasherRunning).toBe(false);
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
it('should return false when washerDryer is absent', () => {
|
|
871
|
+
// Arrange
|
|
872
|
+
const snapshot = new ThinqSnapshot({});
|
|
873
|
+
|
|
874
|
+
// Act & Assert
|
|
875
|
+
expect(snapshot.isWasherRunning).toBe(false);
|
|
876
|
+
});
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
describe('isWasherError', () => {
|
|
880
|
+
it('should return true when washerRawState is ERROR', () => {
|
|
881
|
+
// Arrange
|
|
882
|
+
const snapshot = new ThinqSnapshot({
|
|
883
|
+
washerDryer: { state: 'ERROR' },
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// Act & Assert
|
|
887
|
+
expect(snapshot.isWasherError).toBe(true);
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
it('should return false when washerRawState is RUNNING', () => {
|
|
891
|
+
// Arrange
|
|
892
|
+
const snapshot = new ThinqSnapshot({
|
|
893
|
+
washerDryer: { state: 'RUNNING' },
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
// Act & Assert
|
|
897
|
+
expect(snapshot.isWasherError).toBe(false);
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
it('should return false when washerRawState is POWEROFF', () => {
|
|
901
|
+
// Arrange
|
|
902
|
+
const snapshot = new ThinqSnapshot({
|
|
903
|
+
washerDryer: { state: 'POWEROFF' },
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
// Act & Assert
|
|
907
|
+
expect(snapshot.isWasherError).toBe(false);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
it('should return false when washerDryer is absent', () => {
|
|
911
|
+
// Arrange
|
|
912
|
+
const snapshot = new ThinqSnapshot({});
|
|
913
|
+
|
|
914
|
+
// Act & Assert
|
|
915
|
+
expect(snapshot.isWasherError).toBe(false);
|
|
916
|
+
});
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
describe('washerRemainingDurationSeconds', () => {
|
|
920
|
+
it('should return combined seconds when both hours and minutes present', () => {
|
|
921
|
+
// Arrange
|
|
922
|
+
const snapshot = new ThinqSnapshot({
|
|
923
|
+
washerDryer: { state: 'RUNNING', remainTimeHour: 1, remainTimeMinute: 30 },
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
// Act
|
|
927
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
928
|
+
|
|
929
|
+
// Assert
|
|
930
|
+
expect(result).toBe(5400); // 1*3600 + 30*60
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
it('should return seconds from hours only', () => {
|
|
934
|
+
// Arrange
|
|
935
|
+
const snapshot = new ThinqSnapshot({
|
|
936
|
+
washerDryer: { state: 'RUNNING', remainTimeHour: 2 },
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
// Act
|
|
940
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
941
|
+
|
|
942
|
+
// Assert
|
|
943
|
+
expect(result).toBe(7200); // 2*3600
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
it('should return seconds from minutes only', () => {
|
|
947
|
+
// Arrange
|
|
948
|
+
const snapshot = new ThinqSnapshot({
|
|
949
|
+
washerDryer: { state: 'RUNNING', remainTimeMinute: 45 },
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
// Act
|
|
953
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
954
|
+
|
|
955
|
+
// Assert
|
|
956
|
+
expect(result).toBe(2700); // 45*60
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
it('should return 0 when present but not running (PAUSE)', () => {
|
|
960
|
+
// Arrange
|
|
961
|
+
const snapshot = new ThinqSnapshot({
|
|
962
|
+
washerDryer: { state: 'PAUSE', remainTimeHour: 1, remainTimeMinute: 30 },
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
// Act
|
|
966
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
967
|
+
|
|
968
|
+
// Assert
|
|
969
|
+
expect(result).toBe(0); // Zeroed when not running
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
it('should return 0 when present but not running (END)', () => {
|
|
973
|
+
// Arrange
|
|
974
|
+
const snapshot = new ThinqSnapshot({
|
|
975
|
+
washerDryer: { state: 'END', remainTimeHour: 1 },
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
// Act
|
|
979
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
980
|
+
|
|
981
|
+
// Assert
|
|
982
|
+
expect(result).toBe(0); // Zeroed when not running
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
it('should return undefined when both remain times absent', () => {
|
|
986
|
+
// Arrange
|
|
987
|
+
const snapshot = new ThinqSnapshot({
|
|
988
|
+
washerDryer: { state: 'RUNNING' },
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
// Act
|
|
992
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
993
|
+
|
|
994
|
+
// Assert
|
|
995
|
+
expect(result).toBeUndefined();
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
it('should return undefined when washerDryer absent', () => {
|
|
999
|
+
// Arrange
|
|
1000
|
+
const snapshot = new ThinqSnapshot({});
|
|
1001
|
+
|
|
1002
|
+
// Act
|
|
1003
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
1004
|
+
|
|
1005
|
+
// Assert
|
|
1006
|
+
expect(result).toBeUndefined();
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
it('should handle zero hour/minute values', () => {
|
|
1010
|
+
// Arrange
|
|
1011
|
+
const snapshot = new ThinqSnapshot({
|
|
1012
|
+
washerDryer: { state: 'RUNNING', remainTimeHour: 0, remainTimeMinute: 0 },
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
// Act
|
|
1016
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
1017
|
+
|
|
1018
|
+
// Assert
|
|
1019
|
+
expect(result).toBe(0);
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
it('should add hours and minutes correctly (large values)', () => {
|
|
1023
|
+
// Arrange
|
|
1024
|
+
const snapshot = new ThinqSnapshot({
|
|
1025
|
+
washerDryer: { state: 'RUNNING', remainTimeHour: 10, remainTimeMinute: 59 },
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// Act
|
|
1029
|
+
const result = snapshot.washerRemainingDurationSeconds;
|
|
1030
|
+
|
|
1031
|
+
// Assert
|
|
1032
|
+
expect(result).toBe(39540); // 10*3600 + 59*60
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
});
|