homematic-manager 3.0.0-beta.30 → 3.0.0-beta.31
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/node_modules/@homematic-manager/backend/package.json +1 -1
- package/node_modules/@homematic-manager/core/dist/easymodes/engine.d.ts +16 -3
- package/node_modules/@homematic-manager/core/dist/easymodes/engine.js +79 -27
- package/node_modules/@homematic-manager/core/package.json +1 -1
- package/package.json +3 -3
- package/ui/assets/{index-B7H_fshR.js → index-Co1znn_z.js} +13 -13
- package/ui/index.html +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homematic-manager/backend",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Homematic Manager backend: XML-RPC/BIN-RPC clients and callback servers, caches, optional ReGa",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -143,9 +143,22 @@ export declare class EasyModeEngine {
|
|
|
143
143
|
*/
|
|
144
144
|
applyProfile(profile: LinkProfile, current: Paramset, description: ParamsetDescription, options?: ApplyProfileOptions): AppliedProfile;
|
|
145
145
|
/**
|
|
146
|
-
* Which profile a link paramset currently follows
|
|
147
|
-
*
|
|
148
|
-
*
|
|
146
|
+
* Which profile a link paramset currently follows, the way the WebUI's `get_cur_profile2`
|
|
147
|
+
* decides it: a profile fits when every parameter it names holds on the link - a `fixed` value
|
|
148
|
+
* equal, a `list` value one of its values, a `range` value inside it - leaving out the
|
|
149
|
+
* parameters the link does not have (B-59, "Gibt es diesen Parameter im Aktor überhaupt?").
|
|
150
|
+
*
|
|
151
|
+
* B-77: only the `fixed` parameters used to count, and the profile with the most of them won.
|
|
152
|
+
* The profiles of one channel pair often differ only in their lists - an HmIP switch's
|
|
153
|
+
* "Schalter ein" and "Schalter ein / aus" differ in the jump table (`*_JT_ON` 1/3 or 4/6) -
|
|
154
|
+
* so a toggle link was shown as "switch on", which has more fixed parameters.
|
|
155
|
+
*
|
|
156
|
+
* Order: `UI_HINT` when its profile fits (`0`, the expert view, is taken as it is); otherwise
|
|
157
|
+
* the fitting profile the link has the most named parameters of, then the one with more fixed
|
|
158
|
+
* parameters (a staircase light is a "switch on" with a fixed time: the more specific one), then
|
|
159
|
+
* the one that lacks fewer, then the lower id - the WebUI takes the first fitting one by id, and
|
|
160
|
+
* where only one fits, as for B-77's pair, the two agree; last a `UI_HINT` whose profile no
|
|
161
|
+
* longer fits, as before.
|
|
149
162
|
*
|
|
150
163
|
* `undefined` means "none of them"; the caller then shows the expert view.
|
|
151
164
|
*/
|
|
@@ -153,43 +153,43 @@ export class EasyModeEngine {
|
|
|
153
153
|
return { values, problems };
|
|
154
154
|
}
|
|
155
155
|
/**
|
|
156
|
-
* Which profile a link paramset currently follows
|
|
157
|
-
*
|
|
158
|
-
*
|
|
156
|
+
* Which profile a link paramset currently follows, the way the WebUI's `get_cur_profile2`
|
|
157
|
+
* decides it: a profile fits when every parameter it names holds on the link - a `fixed` value
|
|
158
|
+
* equal, a `list` value one of its values, a `range` value inside it - leaving out the
|
|
159
|
+
* parameters the link does not have (B-59, "Gibt es diesen Parameter im Aktor überhaupt?").
|
|
160
|
+
*
|
|
161
|
+
* B-77: only the `fixed` parameters used to count, and the profile with the most of them won.
|
|
162
|
+
* The profiles of one channel pair often differ only in their lists - an HmIP switch's
|
|
163
|
+
* "Schalter ein" and "Schalter ein / aus" differ in the jump table (`*_JT_ON` 1/3 or 4/6) -
|
|
164
|
+
* so a toggle link was shown as "switch on", which has more fixed parameters.
|
|
165
|
+
*
|
|
166
|
+
* Order: `UI_HINT` when its profile fits (`0`, the expert view, is taken as it is); otherwise
|
|
167
|
+
* the fitting profile the link has the most named parameters of, then the one with more fixed
|
|
168
|
+
* parameters (a staircase light is a "switch on" with a fixed time: the more specific one), then
|
|
169
|
+
* the one that lacks fewer, then the lower id - the WebUI takes the first fitting one by id, and
|
|
170
|
+
* where only one fits, as for B-77's pair, the two agree; last a `UI_HINT` whose profile no
|
|
171
|
+
* longer fits, as before.
|
|
159
172
|
*
|
|
160
173
|
* `undefined` means "none of them"; the caller then shows the expert view.
|
|
161
174
|
*/
|
|
162
175
|
detectProfile(linkParamset, profiles) {
|
|
163
176
|
const hint = linkParamset[UI_HINT];
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
177
|
+
const byHint = hint === undefined || hint === ''
|
|
178
|
+
? undefined
|
|
179
|
+
: profiles.find((profile) => String(profile.id) === String(hint));
|
|
180
|
+
if (byHint && (byHint.id === EXPERT_PROFILE_ID || profileFits(byHint, linkParamset) !== undefined)) {
|
|
181
|
+
return byHint;
|
|
169
182
|
}
|
|
170
183
|
let best;
|
|
171
|
-
let
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// WebUI's get_cur_profile2 does ("Gibt es diesen Parameter im Aktor überhaupt?")
|
|
177
|
-
const fixed = all.filter(([param]) => param in linkParamset);
|
|
178
|
-
const missing = all.length - fixed.length;
|
|
179
|
-
if (fixed.length === 0) {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (!fixed.every(([param, constraint]) => sameProfileValue(linkParamset[param], constraint.value))) {
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
// the most fixed parameters matched; on a tie the profile that asks for less the link lacks
|
|
186
|
-
if (fixed.length > bestScore || (fixed.length === bestScore && missing < bestMissing)) {
|
|
187
|
-
bestScore = fixed.length;
|
|
188
|
-
bestMissing = missing;
|
|
184
|
+
let bestFit;
|
|
185
|
+
for (const profile of [...profiles].sort((a, b) => a.id - b.id)) {
|
|
186
|
+
const fit = profileFits(profile, linkParamset);
|
|
187
|
+
if (fit !== undefined && betterFit(fit, bestFit)) {
|
|
188
|
+
bestFit = fit;
|
|
189
189
|
best = profile;
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
-
return best;
|
|
192
|
+
return best ?? byHint;
|
|
193
193
|
}
|
|
194
194
|
/**
|
|
195
195
|
* The MASTER paramset of a channel type as the dialog should show it: ordered, with the
|
|
@@ -250,6 +250,58 @@ export function resolveAlias(receiverType, aliases) {
|
|
|
250
250
|
}
|
|
251
251
|
return current;
|
|
252
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Whether every parameter a profile names holds on the link, as `get_cur_profile2` checks it, and
|
|
255
|
+
* how many of them the link has and lacks. `undefined` when one does not hold, or when the link
|
|
256
|
+
* has none of them (the expert profile, or a profile of another firmware).
|
|
257
|
+
*/
|
|
258
|
+
function profileFits(profile, linkParamset) {
|
|
259
|
+
let present = 0;
|
|
260
|
+
let fixed = 0;
|
|
261
|
+
let missing = 0;
|
|
262
|
+
for (const [param, constraint] of Object.entries(profile.params)) {
|
|
263
|
+
if (!(param in linkParamset)) {
|
|
264
|
+
missing += 1;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (!constraintHolds(constraint, linkParamset[param])) {
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
present += 1;
|
|
271
|
+
if (constraint.kind === 'fixed') {
|
|
272
|
+
fixed += 1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return present === 0 ? undefined : { present, fixed, missing };
|
|
276
|
+
}
|
|
277
|
+
/** Whether a fit is the better one: more named parameters, then more fixed ones, then fewer lacking. */
|
|
278
|
+
function betterFit(fit, best) {
|
|
279
|
+
if (best === undefined)
|
|
280
|
+
return true;
|
|
281
|
+
if (fit.present !== best.present)
|
|
282
|
+
return fit.present > best.present;
|
|
283
|
+
if (fit.fixed !== best.fixed)
|
|
284
|
+
return fit.fixed > best.fixed;
|
|
285
|
+
return fit.missing < best.missing;
|
|
286
|
+
}
|
|
287
|
+
function constraintHolds(constraint, value) {
|
|
288
|
+
switch (constraint.kind) {
|
|
289
|
+
case 'fixed':
|
|
290
|
+
return sameProfileValue(value, constraint.value);
|
|
291
|
+
case 'list':
|
|
292
|
+
return constraint.values.some((entry) => sameProfileValue(value, entry));
|
|
293
|
+
case 'range': {
|
|
294
|
+
const number = Number(value);
|
|
295
|
+
// an enum name where the range counts indices cannot be judged here; it rules nothing out
|
|
296
|
+
if (value === '' || Number.isNaN(number)) {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
// the WebUI also accepts the range's default outside it: `{31 range 0 - 7}` of an HmIP
|
|
300
|
+
// "Schalter aus" holds for 31, because its last check compares against the first element
|
|
301
|
+
return (number >= constraint.min && number <= constraint.max) || number === constraint.default;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
253
305
|
function applyConstraint(constraint, current) {
|
|
254
306
|
switch (constraint.kind) {
|
|
255
307
|
case 'fixed':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homematic-manager",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Configure and administer Homematic and HomematicIP devices - devices and channels, direct links, paramsets, RSSI, service messages, events and an RPC console - from a local HTTP/WebSocket server that also serves the web UI",
|
|
6
6
|
"keywords": [
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"hm-simulator": "^1.1.0"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@homematic-manager/backend": "3.0.0-beta.
|
|
71
|
-
"@homematic-manager/core": "3.0.0-beta.
|
|
70
|
+
"@homematic-manager/backend": "3.0.0-beta.31",
|
|
71
|
+
"@homematic-manager/core": "3.0.0-beta.31",
|
|
72
72
|
"binrpc": "^4.2.0",
|
|
73
73
|
"homematic-rega": "^2.0.0",
|
|
74
74
|
"homematic-xmlrpc": "^2.0.0",
|