@mp-consulting/homebridge-daikin-cloud 1.5.1 → 1.6.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/README.md +40 -27
- package/config.schema.json +66 -12
- package/dist/src/accessories/air-conditioning-accessory.js +1 -1
- package/dist/src/accessories/air-conditioning-accessory.js.map +1 -1
- package/dist/src/accessories/altherma-accessory.js +1 -1
- package/dist/src/accessories/altherma-accessory.js.map +1 -1
- package/dist/src/accessories/base-accessory.d.ts +5 -4
- package/dist/src/accessories/base-accessory.d.ts.map +1 -1
- package/dist/src/accessories/base-accessory.js +9 -10
- package/dist/src/accessories/base-accessory.js.map +1 -1
- package/dist/src/api/daikin-api.d.ts +22 -6
- package/dist/src/api/daikin-api.d.ts.map +1 -1
- package/dist/src/api/daikin-api.js +49 -23
- package/dist/src/api/daikin-api.js.map +1 -1
- package/dist/src/api/daikin-controller.d.ts +4 -48
- package/dist/src/api/daikin-controller.d.ts.map +1 -1
- package/dist/src/api/daikin-controller.js +14 -84
- package/dist/src/api/daikin-controller.js.map +1 -1
- package/dist/src/api/daikin-mobile-oauth.d.ts +11 -40
- package/dist/src/api/daikin-mobile-oauth.d.ts.map +1 -1
- package/dist/src/api/daikin-mobile-oauth.js +36 -160
- package/dist/src/api/daikin-mobile-oauth.js.map +1 -1
- package/dist/src/api/daikin-oauth.d.ts +10 -37
- package/dist/src/api/daikin-oauth.d.ts.map +1 -1
- package/dist/src/api/daikin-oauth.js +36 -166
- package/dist/src/api/daikin-oauth.js.map +1 -1
- package/dist/src/api/daikin-schemas.d.ts +20 -22
- package/dist/src/api/daikin-schemas.d.ts.map +1 -1
- package/dist/src/api/daikin-schemas.js +17 -49
- package/dist/src/api/daikin-schemas.js.map +1 -1
- package/dist/src/api/daikin-types.d.ts +11 -23
- 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 +4 -5
- package/dist/src/api/daikin-websocket.d.ts.map +1 -1
- package/dist/src/api/daikin-websocket.js +62 -34
- package/dist/src/api/daikin-websocket.js.map +1 -1
- package/dist/src/api/http-defaults.d.ts +3 -2
- package/dist/src/api/http-defaults.d.ts.map +1 -1
- package/dist/src/api/http-defaults.js.map +1 -1
- package/dist/src/api/http-transport.d.ts +14 -2
- package/dist/src/api/http-transport.d.ts.map +1 -1
- package/dist/src/api/http-transport.js +32 -9
- package/dist/src/api/http-transport.js.map +1 -1
- package/dist/src/api/token-client.d.ts +82 -0
- package/dist/src/api/token-client.d.ts.map +1 -0
- package/dist/src/api/token-client.js +188 -0
- package/dist/src/api/token-client.js.map +1 -0
- package/dist/src/api/token-storage.d.ts +6 -0
- package/dist/src/api/token-storage.d.ts.map +1 -1
- package/dist/src/api/token-storage.js +27 -1
- package/dist/src/api/token-storage.js.map +1 -1
- package/dist/src/config/config-manager.d.ts +69 -125
- package/dist/src/config/config-manager.d.ts.map +1 -1
- package/dist/src/config/config-manager.js +288 -225
- package/dist/src/config/config-manager.js.map +1 -1
- package/dist/src/constants/api.constants.d.ts +6 -0
- package/dist/src/constants/api.constants.d.ts.map +1 -1
- package/dist/src/constants/api.constants.js +7 -1
- package/dist/src/constants/api.constants.js.map +1 -1
- package/dist/src/constants/auth.constants.d.ts +11 -0
- package/dist/src/constants/auth.constants.d.ts.map +1 -1
- package/dist/src/constants/auth.constants.js +12 -1
- package/dist/src/constants/auth.constants.js.map +1 -1
- package/dist/src/constants/device.constants.d.ts +1 -7
- package/dist/src/constants/device.constants.d.ts.map +1 -1
- package/dist/src/constants/device.constants.js +2 -11
- package/dist/src/constants/device.constants.js.map +1 -1
- package/dist/src/constants/time.constants.d.ts +0 -2
- package/dist/src/constants/time.constants.d.ts.map +1 -1
- package/dist/src/constants/time.constants.js +1 -3
- package/dist/src/constants/time.constants.js.map +1 -1
- package/dist/src/device/accessory-factory.d.ts +0 -5
- package/dist/src/device/accessory-factory.d.ts.map +1 -1
- package/dist/src/device/accessory-factory.js +0 -7
- package/dist/src/device/accessory-factory.js.map +1 -1
- package/dist/src/device/capability-detector.d.ts +9 -1
- package/dist/src/device/capability-detector.d.ts.map +1 -1
- package/dist/src/device/capability-detector.js +32 -28
- package/dist/src/device/capability-detector.js.map +1 -1
- package/dist/src/device/capability-docs.d.ts.map +1 -1
- package/dist/src/device/capability-docs.js +3 -0
- package/dist/src/device/capability-docs.js.map +1 -1
- package/dist/src/device/data-paths.d.ts +18 -0
- package/dist/src/device/data-paths.d.ts.map +1 -0
- package/dist/src/device/data-paths.js +20 -0
- package/dist/src/device/data-paths.js.map +1 -0
- package/dist/src/device/profiles/device-profile.d.ts +0 -5
- package/dist/src/device/profiles/device-profile.d.ts.map +1 -1
- package/dist/src/device/profiles/device-profile.js +0 -21
- package/dist/src/device/profiles/device-profile.js.map +1 -1
- package/dist/src/features/base-feature.d.ts +53 -9
- package/dist/src/features/base-feature.d.ts.map +1 -1
- package/dist/src/features/base-feature.js +93 -40
- package/dist/src/features/base-feature.js.map +1 -1
- package/dist/src/features/feature-manager.d.ts +27 -15
- package/dist/src/features/feature-manager.d.ts.map +1 -1
- package/dist/src/features/feature-manager.js +27 -24
- package/dist/src/features/feature-manager.js.map +1 -1
- package/dist/src/features/index.d.ts +1 -0
- package/dist/src/features/index.d.ts.map +1 -1
- package/dist/src/features/index.js +1 -0
- package/dist/src/features/index.js.map +1 -1
- package/dist/src/features/modes/auto-fan-mode.feature.d.ts +16 -10
- package/dist/src/features/modes/auto-fan-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/auto-fan-mode.feature.js +16 -40
- package/dist/src/features/modes/auto-fan-mode.feature.js.map +1 -1
- package/dist/src/features/modes/dry-operation-mode.feature.d.ts +12 -9
- package/dist/src/features/modes/dry-operation-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/dry-operation-mode.feature.js +11 -30
- package/dist/src/features/modes/dry-operation-mode.feature.js.map +1 -1
- package/dist/src/features/modes/econo-mode.feature.d.ts +12 -9
- package/dist/src/features/modes/econo-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/econo-mode.feature.js +11 -31
- package/dist/src/features/modes/econo-mode.feature.js.map +1 -1
- package/dist/src/features/modes/fan-only-operation-mode.feature.d.ts +12 -9
- 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 +11 -30
- package/dist/src/features/modes/fan-only-operation-mode.feature.js.map +1 -1
- package/dist/src/features/modes/firmware-update.feature.d.ts +7 -6
- package/dist/src/features/modes/firmware-update.feature.d.ts.map +1 -1
- package/dist/src/features/modes/firmware-update.feature.js +15 -22
- package/dist/src/features/modes/firmware-update.feature.js.map +1 -1
- package/dist/src/features/modes/holiday-mode.feature.d.ts +2 -1
- package/dist/src/features/modes/holiday-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/holiday-mode.feature.js +5 -15
- package/dist/src/features/modes/holiday-mode.feature.js.map +1 -1
- package/dist/src/features/modes/indoor-silent-mode.feature.d.ts +15 -12
- package/dist/src/features/modes/indoor-silent-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/indoor-silent-mode.feature.js +15 -39
- package/dist/src/features/modes/indoor-silent-mode.feature.js.map +1 -1
- package/dist/src/features/modes/oscillation-mode.feature.d.ts +5 -6
- package/dist/src/features/modes/oscillation-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/oscillation-mode.feature.js +14 -36
- package/dist/src/features/modes/oscillation-mode.feature.js.map +1 -1
- package/dist/src/features/modes/outdoor-silent-mode.feature.d.ts +12 -9
- package/dist/src/features/modes/outdoor-silent-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/outdoor-silent-mode.feature.js +11 -31
- package/dist/src/features/modes/outdoor-silent-mode.feature.js.map +1 -1
- package/dist/src/features/modes/powerful-mode.feature.d.ts +14 -10
- package/dist/src/features/modes/powerful-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/powerful-mode.feature.js +13 -32
- package/dist/src/features/modes/powerful-mode.feature.js.map +1 -1
- package/dist/src/features/modes/streamer-mode.feature.d.ts +12 -9
- package/dist/src/features/modes/streamer-mode.feature.d.ts.map +1 -1
- package/dist/src/features/modes/streamer-mode.feature.js +11 -31
- package/dist/src/features/modes/streamer-mode.feature.js.map +1 -1
- package/dist/src/features/on-off-data-point-feature.d.ts +43 -0
- package/dist/src/features/on-off-data-point-feature.d.ts.map +1 -0
- package/dist/src/features/on-off-data-point-feature.js +49 -0
- package/dist/src/features/on-off-data-point-feature.js.map +1 -0
- package/dist/src/platform.d.ts +56 -9
- package/dist/src/platform.d.ts.map +1 -1
- package/dist/src/platform.js +248 -128
- package/dist/src/platform.js.map +1 -1
- package/dist/src/services/climate-control/fan-speed.d.ts +45 -0
- package/dist/src/services/climate-control/fan-speed.d.ts.map +1 -0
- package/dist/src/services/climate-control/fan-speed.js +104 -0
- package/dist/src/services/climate-control/fan-speed.js.map +1 -0
- package/dist/src/services/climate-control/separate-fan.service.d.ts +45 -0
- package/dist/src/services/climate-control/separate-fan.service.d.ts.map +1 -0
- package/dist/src/services/climate-control/separate-fan.service.js +82 -0
- package/dist/src/services/climate-control/separate-fan.service.js.map +1 -0
- package/dist/src/services/climate-control/setpoint-resolver.d.ts +68 -0
- package/dist/src/services/climate-control/setpoint-resolver.d.ts.map +1 -0
- package/dist/src/services/climate-control/setpoint-resolver.js +170 -0
- package/dist/src/services/climate-control/setpoint-resolver.js.map +1 -0
- package/dist/src/services/climate-control/swing-controller.d.ts +28 -0
- package/dist/src/services/climate-control/swing-controller.d.ts.map +1 -0
- package/dist/src/services/climate-control/swing-controller.js +65 -0
- package/dist/src/services/climate-control/swing-controller.js.map +1 -0
- package/dist/src/services/climate-control.service.d.ts +39 -48
- package/dist/src/services/climate-control.service.d.ts.map +1 -1
- package/dist/src/services/climate-control.service.js +223 -554
- package/dist/src/services/climate-control.service.js.map +1 -1
- package/dist/src/services/hot-water-tank.service.d.ts +17 -8
- package/dist/src/services/hot-water-tank.service.d.ts.map +1 -1
- package/dist/src/services/hot-water-tank.service.js +102 -157
- package/dist/src/services/hot-water-tank.service.js.map +1 -1
- package/dist/src/types/daikin-enums.d.ts +0 -5
- package/dist/src/types/daikin-enums.d.ts.map +1 -1
- package/dist/src/types/daikin-enums.js +1 -7
- package/dist/src/types/daikin-enums.js.map +1 -1
- package/dist/src/types/device-capabilities.d.ts +1 -0
- package/dist/src/types/device-capabilities.d.ts.map +1 -1
- package/dist/src/utils/errors.d.ts +9 -0
- package/dist/src/utils/errors.d.ts.map +1 -0
- package/dist/src/utils/errors.js +25 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/hap-write.d.ts +22 -0
- package/dist/src/utils/hap-write.d.ts.map +1 -0
- package/dist/src/utils/hap-write.js +33 -0
- package/dist/src/utils/hap-write.js.map +1 -0
- package/dist/src/utils/sleep.d.ts +5 -0
- package/dist/src/utils/sleep.d.ts.map +1 -0
- package/dist/src/utils/sleep.js +10 -0
- package/dist/src/utils/sleep.js.map +1 -0
- package/homebridge-ui/README.md +25 -2
- package/homebridge-ui/public/index.html +19 -17
- package/homebridge-ui/public/lib/kit.css +1 -1
- package/homebridge-ui/public/script.js +95 -32
- package/homebridge-ui/public/styles.css +109 -0
- package/homebridge-ui/server.js +321 -125
- package/package.json +14 -15
- package/dist/src/utils/log-context.d.ts +0 -105
- package/dist/src/utils/log-context.d.ts.map +0 -1
- package/dist/src/utils/log-context.js +0 -206
- package/dist/src/utils/log-context.js.map +0 -1
- package/dist/src/utils/update-mapper.d.ts +0 -59
- package/dist/src/utils/update-mapper.d.ts.map +0 -1
- package/dist/src/utils/update-mapper.js +0 -192
- package/dist/src/utils/update-mapper.js.map +0 -1
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Token Client
|
|
4
|
+
*
|
|
5
|
+
* Token lifecycle shared by both OAuth flows (Developer Portal and Mobile
|
|
6
|
+
* App): persistence to the token file, expiry checks with a refresh buffer,
|
|
7
|
+
* single-flight refresh, and strict parsing of token endpoint responses.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.TokenClient = void 0;
|
|
11
|
+
exports.withExpiresAt = withExpiresAt;
|
|
12
|
+
exports.isTokenExpiring = isTokenExpiring;
|
|
13
|
+
exports.parseJsonBody = parseJsonBody;
|
|
14
|
+
exports.parseTokenResponse = parseTokenResponse;
|
|
15
|
+
const daikin_schemas_1 = require("./daikin-schemas");
|
|
16
|
+
const token_storage_1 = require("./token-storage");
|
|
17
|
+
const constants_1 = require("../constants");
|
|
18
|
+
const errors_1 = require("../utils/errors");
|
|
19
|
+
function nowSeconds() {
|
|
20
|
+
return Math.floor(Date.now() / 1000);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Return a copy of the token set with `expires_at` derived from `expires_in`
|
|
24
|
+
* when the server only sent the relative lifetime.
|
|
25
|
+
*/
|
|
26
|
+
function withExpiresAt(tokenSet) {
|
|
27
|
+
if (tokenSet.expires_in && !tokenSet.expires_at) {
|
|
28
|
+
return { ...tokenSet, expires_at: nowSeconds() + tokenSet.expires_in };
|
|
29
|
+
}
|
|
30
|
+
return tokenSet;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Whether the token is expired or expires within TOKEN_EXPIRY_BUFFER_SECONDS.
|
|
34
|
+
* A token without `expires_at` is treated as expired.
|
|
35
|
+
*/
|
|
36
|
+
function isTokenExpiring(tokenSet) {
|
|
37
|
+
return (tokenSet.expires_at ?? 0) < nowSeconds() + constants_1.TOKEN_EXPIRY_BUFFER_SECONDS;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse a JSON response body, reporting the endpoint and status instead of a
|
|
41
|
+
* bare "Unexpected token <" when a proxy/WAF answers with HTML or nothing.
|
|
42
|
+
*/
|
|
43
|
+
function parseJsonBody(response, url, context) {
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(response.body);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
const { hostname } = new URL(url);
|
|
49
|
+
const snippet = response.body.trim().replace(/\s+/g, ' ').slice(0, 200);
|
|
50
|
+
throw new Error(`${context}: ${hostname} returned a non-JSON response (HTTP ${response.statusCode})`
|
|
51
|
+
+ (snippet ? `: ${snippet}` : ' with an empty body'));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate a token endpoint response: OAuth error bodies, non-2xx statuses and
|
|
56
|
+
* malformed token sets all become errors with a readable message. The message
|
|
57
|
+
* keeps the OAuth error code (e.g. `invalid_grant`), which callers match on.
|
|
58
|
+
* Token values are never included.
|
|
59
|
+
*/
|
|
60
|
+
function parseTokenResponse(response, url, context) {
|
|
61
|
+
const body = parseJsonBody(response, url, context);
|
|
62
|
+
const oauthError = daikin_schemas_1.OAuthErrorSchema.safeParse(body);
|
|
63
|
+
if (oauthError.success) {
|
|
64
|
+
const { error, error_description: description } = oauthError.data;
|
|
65
|
+
throw new Error(`${context}: ${error}${description ? ` - ${description}` : ''} (HTTP ${response.statusCode})`);
|
|
66
|
+
}
|
|
67
|
+
const { hostname } = new URL(url);
|
|
68
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
69
|
+
throw new Error(`${context}: ${hostname} returned HTTP ${response.statusCode}`);
|
|
70
|
+
}
|
|
71
|
+
const result = daikin_schemas_1.TokenSetSchema.safeParse(body);
|
|
72
|
+
if (!result.success) {
|
|
73
|
+
const fields = [...new Set(result.error.issues.map((issue) => issue.path.join('.') || '(body)'))];
|
|
74
|
+
throw new Error(`${context}: ${hostname} returned an invalid token response (missing or malformed: ${fields.join(', ')})`);
|
|
75
|
+
}
|
|
76
|
+
return result.data;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Base class holding the current token set and its refresh logic. Subclasses
|
|
80
|
+
* implement only the grant-specific refresh request.
|
|
81
|
+
*/
|
|
82
|
+
class TokenClient {
|
|
83
|
+
tokenFilePath;
|
|
84
|
+
onTokenUpdate;
|
|
85
|
+
onError;
|
|
86
|
+
tokenSet = null;
|
|
87
|
+
refreshPromise = null;
|
|
88
|
+
constructor(tokenFilePath, onTokenUpdate, onError) {
|
|
89
|
+
this.tokenFilePath = tokenFilePath;
|
|
90
|
+
this.onTokenUpdate = onTokenUpdate;
|
|
91
|
+
this.onError = onError;
|
|
92
|
+
this.loadFromFile();
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Refresh the access token. Concurrent callers share one in-flight request;
|
|
96
|
+
* a failed refresh is not cached, so the next call retries.
|
|
97
|
+
*/
|
|
98
|
+
refreshToken() {
|
|
99
|
+
const refreshToken = this.tokenSet?.refresh_token;
|
|
100
|
+
if (!refreshToken) {
|
|
101
|
+
return Promise.reject(new Error('No refresh token available'));
|
|
102
|
+
}
|
|
103
|
+
if (!this.refreshPromise) {
|
|
104
|
+
this.refreshPromise = this.requestTokenRefresh(refreshToken)
|
|
105
|
+
// Providers may omit refresh_token when they do not rotate it; the
|
|
106
|
+
// previous one stays valid then (RFC 6749 section 6).
|
|
107
|
+
.then((tokenSet) => this.setTokenSet({ ...tokenSet, refresh_token: tokenSet.refresh_token ?? refreshToken }))
|
|
108
|
+
.finally(() => {
|
|
109
|
+
this.refreshPromise = null;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return this.refreshPromise;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get a valid access token, refreshing if necessary
|
|
116
|
+
*/
|
|
117
|
+
async getAccessToken() {
|
|
118
|
+
const current = this.tokenSet;
|
|
119
|
+
if (!current) {
|
|
120
|
+
throw new Error('Not authenticated. Please authenticate first.');
|
|
121
|
+
}
|
|
122
|
+
if (!isTokenExpiring(current)) {
|
|
123
|
+
return current.access_token;
|
|
124
|
+
}
|
|
125
|
+
if (!current.refresh_token) {
|
|
126
|
+
throw new Error('Token expired and no refresh token available. Please re-authenticate.');
|
|
127
|
+
}
|
|
128
|
+
const refreshed = await this.refreshToken();
|
|
129
|
+
return refreshed.access_token;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if we have a token (it may still need a refresh)
|
|
133
|
+
*/
|
|
134
|
+
isAuthenticated() {
|
|
135
|
+
return !!this.tokenSet?.access_token;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get token expiration date
|
|
139
|
+
*/
|
|
140
|
+
getTokenExpiration() {
|
|
141
|
+
if (!this.tokenSet?.expires_at) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
return new Date(this.tokenSet.expires_at * 1000);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get current token set (for status display)
|
|
148
|
+
*/
|
|
149
|
+
getTokenSet() {
|
|
150
|
+
return this.tokenSet;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Forget the current tokens and delete the token file
|
|
154
|
+
*/
|
|
155
|
+
clearTokens() {
|
|
156
|
+
(0, token_storage_1.deleteTokenFile)(this.tokenFilePath);
|
|
157
|
+
this.tokenSet = null;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Store a new token set (in memory and on disk) and notify listeners.
|
|
161
|
+
* Returns the stored set, with `expires_at` filled in.
|
|
162
|
+
*/
|
|
163
|
+
setTokenSet(tokenSet) {
|
|
164
|
+
const stored = withExpiresAt(tokenSet);
|
|
165
|
+
this.tokenSet = stored;
|
|
166
|
+
this.saveToFile(stored);
|
|
167
|
+
this.onTokenUpdate?.(stored);
|
|
168
|
+
return stored;
|
|
169
|
+
}
|
|
170
|
+
loadFromFile() {
|
|
171
|
+
try {
|
|
172
|
+
this.tokenSet = (0, token_storage_1.loadTokenFromFile)(this.tokenFilePath);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
this.onError?.(new Error(`Failed to load token file: ${(0, errors_1.toMessage)(error)}`));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
saveToFile(tokenSet) {
|
|
179
|
+
try {
|
|
180
|
+
(0, token_storage_1.saveTokenToFile)(this.tokenFilePath, tokenSet);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
this.onError?.(new Error(`Failed to save token file: ${(0, errors_1.toMessage)(error)}`));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.TokenClient = TokenClient;
|
|
188
|
+
//# sourceMappingURL=token-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-client.js","sourceRoot":"","sources":["../../../src/api/token-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,12 @@ import type { TokenSet } from './daikin-types';
|
|
|
12
12
|
export declare function loadTokenFromFile(filePath: string): TokenSet | null;
|
|
13
13
|
/**
|
|
14
14
|
* Save a token set to a file with restricted permissions.
|
|
15
|
+
*
|
|
16
|
+
* Writes atomically (temp file + rename) so a crash mid-write never leaves a
|
|
17
|
+
* truncated token file behind. `mode` on writeFileSync only applies when a
|
|
18
|
+
* file is created, so a pre-existing world-readable token file would keep its
|
|
19
|
+
* permissions: the temp file is always freshly created with 0600 and replaces
|
|
20
|
+
* the old inode, and the final file is chmod'ed again for good measure.
|
|
15
21
|
*/
|
|
16
22
|
export declare function saveTokenToFile(filePath: string, tokenSet: TokenSet): void;
|
|
17
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-storage.d.ts","sourceRoot":"","sources":["../../../src/api/token-storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"token-storage.d.ts","sourceRoot":"","sources":["../../../src/api/token-storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM/C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAgBnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAsB1E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQtD"}
|
|
@@ -43,6 +43,7 @@ exports.loadTokenFromFile = loadTokenFromFile;
|
|
|
43
43
|
exports.saveTokenToFile = saveTokenToFile;
|
|
44
44
|
exports.deleteTokenFile = deleteTokenFile;
|
|
45
45
|
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const crypto = __importStar(require("node:crypto"));
|
|
46
47
|
const daikin_schemas_1 = require("./daikin-schemas");
|
|
47
48
|
/** File permissions: owner read/write only */
|
|
48
49
|
const TOKEN_FILE_MODE = 0o600;
|
|
@@ -70,9 +71,34 @@ function loadTokenFromFile(filePath) {
|
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
72
73
|
* Save a token set to a file with restricted permissions.
|
|
74
|
+
*
|
|
75
|
+
* Writes atomically (temp file + rename) so a crash mid-write never leaves a
|
|
76
|
+
* truncated token file behind. `mode` on writeFileSync only applies when a
|
|
77
|
+
* file is created, so a pre-existing world-readable token file would keep its
|
|
78
|
+
* permissions: the temp file is always freshly created with 0600 and replaces
|
|
79
|
+
* the old inode, and the final file is chmod'ed again for good measure.
|
|
73
80
|
*/
|
|
74
81
|
function saveTokenToFile(filePath, tokenSet) {
|
|
75
|
-
|
|
82
|
+
const tempPath = `${filePath}.tmp-${crypto.randomBytes(8).toString('hex')}`;
|
|
83
|
+
try {
|
|
84
|
+
fs.writeFileSync(tempPath, JSON.stringify(tokenSet, null, 2), { encoding: 'utf8', mode: TOKEN_FILE_MODE, flag: 'wx' });
|
|
85
|
+
fs.renameSync(tempPath, filePath);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
try {
|
|
89
|
+
fs.rmSync(tempPath, { force: true });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Best effort cleanup
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
fs.chmodSync(filePath, TOKEN_FILE_MODE);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Platforms without POSIX permissions (Windows)
|
|
101
|
+
}
|
|
76
102
|
}
|
|
77
103
|
/**
|
|
78
104
|
* Delete a token file if it exists.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-storage.js","sourceRoot":"","sources":["../../../src/api/token-storage.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"token-storage.js","sourceRoot":"","sources":["../../../src/api/token-storage.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcH,8CAgBC;AAWD,0CAsBC;AAKD,0CAQC;AA1ED,4CAA8B;AAC9B,oDAAsC;AAEtC,qDAAkD;AAElD,8CAA8C;AAC9C,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,+BAAc,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,MAAM,CAAC,IAAI,CAAC;YACrB,CAAC;YACD,sDAAsD;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAAC,QAAgB,EAAE,QAAkB;IAClE,MAAM,QAAQ,GAAG,GAAG,QAAQ,QAAQ,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAC5E,IAAI,CAAC;QACH,EAAE,CAAC,aAAa,CACd,QAAQ,EACR,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EACjC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,EAAE,CACxD,CAAC;QACF,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC;YACH,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe,CAAC,QAAgB;IAC9C,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;AACH,CAAC"}
|
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration Manager
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Single source of truth for the plugin configuration: the raw Homebridge
|
|
5
|
+
* platform config is normalized once (type coercion, schema bounds, defaults)
|
|
6
|
+
* and every consumer reads it through the typed getters below.
|
|
6
7
|
*/
|
|
7
|
-
import type { AuthMode } from '../api/daikin-types';
|
|
8
|
-
|
|
8
|
+
import type { AuthMode, DaikinControllerConfig } from '../api/daikin-types';
|
|
9
|
+
import type { HttpTransportMode } from '../api/http-transport';
|
|
10
|
+
/**
|
|
11
|
+
* Per-feature config toggles (the `show*` keys of config.schema.json).
|
|
12
|
+
*
|
|
13
|
+
* - `extra`: switches that follow the legacy `showExtraFeatures` catch-all when
|
|
14
|
+
* their own key is not set.
|
|
15
|
+
* - `standalone`: default off and never follow `showExtraFeatures` (a separate
|
|
16
|
+
* Fan tile, and a switch that installs firmware).
|
|
17
|
+
*
|
|
18
|
+
* Kept in sync with config.schema.json and homebridge-ui/public/script.js by
|
|
19
|
+
* test/unit/config/feature-config-keys.test.ts.
|
|
20
|
+
*/
|
|
21
|
+
export declare const FEATURE_CONFIG_KEYS: {
|
|
22
|
+
readonly extra: readonly ["showPowerfulMode", "showEconoMode", "showStreamerMode", "showOutdoorSilentMode", "showIndoorSilentMode", "showAutoFanMode", "showOscillationSwitch", "showDryMode", "showFanOnlyMode", "showHolidayMode"];
|
|
23
|
+
readonly standalone: readonly ["showSeparateFanControl", "showFirmwareUpdateSwitch"];
|
|
24
|
+
};
|
|
25
|
+
export type ExtraFeatureConfigKey = typeof FEATURE_CONFIG_KEYS.extra[number];
|
|
26
|
+
export type StandaloneFeatureConfigKey = typeof FEATURE_CONFIG_KEYS.standalone[number];
|
|
27
|
+
export type FeatureConfigKey = ExtraFeatureConfigKey | StandaloneFeatureConfigKey;
|
|
28
|
+
/**
|
|
29
|
+
* Raw plugin config as written by Homebridge. Values come from a user-edited
|
|
30
|
+
* JSON file, so the declared types are what the schema intends, not a guarantee.
|
|
31
|
+
*/
|
|
32
|
+
export interface PluginConfig extends Partial<Record<FeatureConfigKey, boolean | null>> {
|
|
9
33
|
platform: string;
|
|
10
34
|
name?: string;
|
|
11
35
|
authMode?: AuthMode;
|
|
12
36
|
clientId?: string;
|
|
13
37
|
clientSecret?: string;
|
|
14
38
|
callbackServerExternalAddress?: string;
|
|
15
|
-
callbackServerPort?: number;
|
|
39
|
+
callbackServerPort?: number | string;
|
|
16
40
|
oidcCallbackServerBindAddr?: string;
|
|
17
41
|
daikinEmail?: string;
|
|
18
42
|
daikinPassword?: string;
|
|
@@ -24,139 +48,59 @@ export interface PluginConfig {
|
|
|
24
48
|
*/
|
|
25
49
|
email?: string;
|
|
26
50
|
password?: string;
|
|
27
|
-
updateIntervalInMinutes?: number;
|
|
28
|
-
forceUpdateDelay?: number;
|
|
51
|
+
updateIntervalInMinutes?: number | string;
|
|
52
|
+
forceUpdateDelay?: number | string;
|
|
29
53
|
excludedDevicesByDeviceId?: string[];
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
showStreamerMode?: boolean;
|
|
33
|
-
showOutdoorSilentMode?: boolean;
|
|
34
|
-
showIndoorSilentMode?: boolean;
|
|
35
|
-
showAutoFanMode?: boolean;
|
|
36
|
-
showOscillationSwitch?: boolean;
|
|
37
|
-
showDryMode?: boolean;
|
|
38
|
-
showFanOnlyMode?: boolean;
|
|
39
|
-
showHolidayMode?: boolean;
|
|
40
|
-
showExtraFeatures?: boolean;
|
|
41
|
-
showSeparateFanControl?: boolean;
|
|
42
|
-
showFirmwareUpdateSwitch?: boolean;
|
|
43
|
-
httpTransport?: 'node' | 'curl';
|
|
54
|
+
showExtraFeatures?: boolean | null;
|
|
55
|
+
httpTransport?: HttpTransportMode;
|
|
44
56
|
enableWebSocket?: boolean;
|
|
45
57
|
}
|
|
46
|
-
export interface
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
excludedDeviceIds: Set<string>;
|
|
51
|
-
features: {
|
|
52
|
-
powerfulMode: boolean;
|
|
53
|
-
econoMode: boolean;
|
|
54
|
-
streamerMode: boolean;
|
|
55
|
-
outdoorSilentMode: boolean;
|
|
56
|
-
indoorSilentMode: boolean;
|
|
57
|
-
autoFanMode: boolean;
|
|
58
|
-
oscillationMode: boolean;
|
|
59
|
-
dryMode: boolean;
|
|
60
|
-
fanOnlyMode: boolean;
|
|
61
|
-
holidayMode: boolean;
|
|
62
|
-
};
|
|
63
|
-
websocketEnabled: boolean;
|
|
58
|
+
export interface ConfigValidationResult {
|
|
59
|
+
valid: boolean;
|
|
60
|
+
errors: string[];
|
|
61
|
+
warnings: string[];
|
|
64
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate a raw plugin config. Pure: also used by the custom UI server
|
|
65
|
+
* (homebridge-ui/server.js) for its /config/validate endpoint.
|
|
66
|
+
*
|
|
67
|
+
* Errors are configurations the plugin cannot work with; out-of-range numeric
|
|
68
|
+
* settings are only warnings, because normalization clamps them.
|
|
69
|
+
*/
|
|
70
|
+
export declare function validateConfig(config: PluginConfig): ConfigValidationResult;
|
|
65
71
|
export declare class ConfigManager {
|
|
66
72
|
private readonly config;
|
|
67
|
-
private normalized;
|
|
73
|
+
private readonly normalized;
|
|
68
74
|
constructor(config: PluginConfig);
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
/** Platform name as configured in Homebridge */
|
|
76
|
+
getName(): string | undefined;
|
|
77
|
+
/** HTTP transport from the config (the DAIKIN_HTTP_TRANSPORT env var still wins, see configureHttpTransport) */
|
|
78
|
+
getHttpTransport(): HttpTransportMode | undefined;
|
|
79
|
+
/** Polling interval in milliseconds, clamped to 1-60 minutes */
|
|
80
|
+
getUpdateIntervalMs(): number;
|
|
81
|
+
/** Delay before refreshing after a change, in milliseconds, clamped to 5-300 seconds */
|
|
82
|
+
getForceUpdateDelayMs(): number;
|
|
83
|
+
/** Whether the device (raw Daikin device ID) is listed in excludedDevicesByDeviceId */
|
|
84
|
+
isDeviceExcluded(deviceId: string): boolean;
|
|
85
|
+
/** WebSocket push updates are on unless explicitly disabled */
|
|
86
|
+
isWebSocketEnabled(): boolean;
|
|
76
87
|
getAuthMode(): AuthMode;
|
|
77
|
-
/**
|
|
78
|
-
* Check if mobile app mode is enabled
|
|
79
|
-
*/
|
|
80
88
|
isMobileAppMode(): boolean;
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
isDeveloperPortalMode(): boolean;
|
|
85
|
-
/**
|
|
86
|
-
* Get developer portal credentials
|
|
87
|
-
*/
|
|
88
|
-
getDeveloperCredentials(): {
|
|
89
|
-
clientId: string;
|
|
90
|
-
clientSecret: string;
|
|
91
|
-
callbackServerExternalAddress: string;
|
|
92
|
-
callbackServerPort: number;
|
|
93
|
-
oidcCallbackServerBindAddr?: string;
|
|
94
|
-
} | null;
|
|
95
|
-
/**
|
|
96
|
-
* Get mobile app credentials
|
|
97
|
-
*/
|
|
89
|
+
/** Client ID and secret are present (the minimum to start the Developer Portal controller) */
|
|
90
|
+
hasDeveloperCredentials(): boolean;
|
|
91
|
+
/** Mobile app email and password (falling back to the legacy keys), or null when either is missing */
|
|
98
92
|
getMobileCredentials(): {
|
|
99
93
|
email: string;
|
|
100
94
|
password: string;
|
|
101
95
|
} | null;
|
|
102
|
-
/**
|
|
103
|
-
* Mobile app email, falling back to the legacy `email` key
|
|
104
|
-
*/
|
|
105
|
-
getMobileEmail(): string | undefined;
|
|
106
|
-
/**
|
|
107
|
-
* Mobile app password, falling back to the legacy `password` key
|
|
108
|
-
*/
|
|
109
|
-
getMobilePassword(): string | undefined;
|
|
110
|
-
/**
|
|
111
|
-
* Legacy credential keys that are in use because the canonical key is absent
|
|
112
|
-
*/
|
|
96
|
+
/** Legacy credential keys that are in use because the canonical key is absent */
|
|
113
97
|
getLegacyCredentialKeysInUse(): string[];
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
getForceUpdateDelayMs(): number;
|
|
122
|
-
/**
|
|
123
|
-
* Get excluded device IDs as a Set
|
|
124
|
-
*/
|
|
125
|
-
getExcludedDeviceIds(): Set<string>;
|
|
126
|
-
/**
|
|
127
|
-
* Check if a device is excluded
|
|
128
|
-
*/
|
|
129
|
-
isDeviceExcluded(deviceId: string): boolean;
|
|
130
|
-
/**
|
|
131
|
-
* Get feature configuration
|
|
132
|
-
*/
|
|
133
|
-
getFeatures(): NormalizedConfig['features'];
|
|
134
|
-
/**
|
|
135
|
-
* Check if WebSocket is enabled
|
|
136
|
-
*/
|
|
137
|
-
isWebSocketEnabled(): boolean;
|
|
138
|
-
/**
|
|
139
|
-
* Validate configuration using Zod schemas
|
|
140
|
-
*/
|
|
141
|
-
validateWithZod(): {
|
|
142
|
-
valid: boolean;
|
|
143
|
-
errors: string[];
|
|
144
|
-
};
|
|
145
|
-
/**
|
|
146
|
-
* Validate configuration
|
|
147
|
-
*/
|
|
148
|
-
validate(): {
|
|
149
|
-
valid: boolean;
|
|
150
|
-
errors: string[];
|
|
151
|
-
warnings: string[];
|
|
152
|
-
};
|
|
153
|
-
/**
|
|
154
|
-
* Normalize the configuration with all defaults applied
|
|
155
|
-
*/
|
|
156
|
-
private normalize;
|
|
157
|
-
/**
|
|
158
|
-
* Get raw config value (escape hatch for migration)
|
|
159
|
-
*/
|
|
160
|
-
getRawConfig(): PluginConfig;
|
|
98
|
+
/** Configuration for the DaikinCloudController, with defaults applied */
|
|
99
|
+
getControllerConfig(tokenFilePath: string): DaikinControllerConfig;
|
|
100
|
+
/** Whether a feature switch / service is enabled (see resolveFeatures for the semantics) */
|
|
101
|
+
isFeatureEnabled(key: FeatureConfigKey): boolean;
|
|
102
|
+
validate(): ConfigValidationResult;
|
|
103
|
+
/** The raw config with credentials and device IDs masked, for debug logging */
|
|
104
|
+
getRedactedConfig(): object;
|
|
161
105
|
}
|
|
162
106
|
//# sourceMappingURL=config-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../../src/config/config-manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../../src/config/config-manager.ts"],"names":[],"mappings":"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"}
|