mirta 0.1.2 → 0.2.1
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/dist/index.d.mts +58 -6
- package/dist/index.mjs +68 -19
- package/package.json +4 -4
package/dist/index.d.mts
CHANGED
|
@@ -65,9 +65,9 @@ type StrictWhenSpecified<TObject, TKey extends keyof TObject, TReturn> = IsSpeci
|
|
|
65
65
|
* Устанавливает указанное свойство в readonly при выполнении указанного условия.
|
|
66
66
|
* @since 0.1.0
|
|
67
67
|
**/
|
|
68
|
-
type ReadonlyPropWhen<TObject, K extends keyof TObject, TCondition extends boolean | undefined> = TCondition extends true ? Omit<TObject, K> & {
|
|
68
|
+
type ReadonlyPropWhen<TObject, K extends keyof TObject, TCondition extends boolean | undefined> = TCondition extends true ? Expand<Omit<TObject, K> & {
|
|
69
69
|
+readonly [P in K]-?: TObject[P];
|
|
70
|
-
} : TObject;
|
|
70
|
+
}> : TObject;
|
|
71
71
|
/**
|
|
72
72
|
* Проверяет, что указанный тип объекта имеет хотя бы одно свойство заданного типа.
|
|
73
73
|
* Применяется для работы с дженериками.
|
|
@@ -86,6 +86,58 @@ type HasPropertyOfType<TObject extends object, TProperty> = {
|
|
|
86
86
|
[K in keyof TObject as TObject[K] extends TProperty ? K : never]: unknown;
|
|
87
87
|
} extends infer R ? {} extends R ? false : true : never;
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Набор политик, управляющих доступностью смены значения контрола.
|
|
91
|
+
*
|
|
92
|
+
* @since 0.2.0
|
|
93
|
+
*
|
|
94
|
+
**/
|
|
95
|
+
declare enum ChangePolicies {
|
|
96
|
+
/**
|
|
97
|
+
* Политика по умолчанию.
|
|
98
|
+
*
|
|
99
|
+
* - Применяет {@link Public} к контролам типа `switch`, `pushbutton`, `range` и `rgb`;
|
|
100
|
+
*
|
|
101
|
+
* - Применяет {@link Script} к остальным типам контролов.
|
|
102
|
+
*
|
|
103
|
+
**/
|
|
104
|
+
Default = "default",
|
|
105
|
+
/**
|
|
106
|
+
* Установка значения возможна как скриптами wb-rules, так и сторонними службами.
|
|
107
|
+
*
|
|
108
|
+
* Применимо к контролам виртуальных устройств.
|
|
109
|
+
*
|
|
110
|
+
**/
|
|
111
|
+
Public = "public",
|
|
112
|
+
/**
|
|
113
|
+
* Установка значения возможна только скриптами wb-rules.
|
|
114
|
+
*
|
|
115
|
+
* Применимо к контролам виртуальных устройств.
|
|
116
|
+
*
|
|
117
|
+
**/
|
|
118
|
+
Script = "script",
|
|
119
|
+
/**
|
|
120
|
+
* Полный запрет записи, значение доступно только для чтения.
|
|
121
|
+
*
|
|
122
|
+
* Применимо к контролам реальных устройств.
|
|
123
|
+
*
|
|
124
|
+
**/
|
|
125
|
+
ReadOnly = "read-only"
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Определяет политику установки значений реальных устройств.
|
|
129
|
+
*
|
|
130
|
+
* @since 0.2.0
|
|
131
|
+
*
|
|
132
|
+
**/
|
|
133
|
+
type ChangePolicy = `${Exclude<ChangePolicies, ChangePolicies.Public | ChangePolicies.Script>}`;
|
|
134
|
+
/**
|
|
135
|
+
* Определяет политику установки значений контролов виртуальных устройств.
|
|
136
|
+
*
|
|
137
|
+
* @since 0.2.0
|
|
138
|
+
*
|
|
139
|
+
**/
|
|
140
|
+
type VirtualChangePolicy = `${Exclude<ChangePolicies, ChangePolicies.ReadOnly>}`;
|
|
89
141
|
type ValueEventHandler<TValue> = (newValue: TValue, oldValue: TValue) => void;
|
|
90
142
|
interface Control<TValue> {
|
|
91
143
|
/** Актуальное значение контрола. */
|
|
@@ -117,8 +169,8 @@ type VirtualTypeMapper<K extends keyof WbRules.TypeMappings = keyof WbRules.Type
|
|
|
117
169
|
interface BaseControlDef {
|
|
118
170
|
/** Идентификатор контрола. Если не указан, используется название свойства. */
|
|
119
171
|
controlId?: string;
|
|
120
|
-
/**
|
|
121
|
-
|
|
172
|
+
/** Политика доступа на запись значения. */
|
|
173
|
+
changePolicy?: VirtualChangePolicy | ChangePolicy;
|
|
122
174
|
}
|
|
123
175
|
/**
|
|
124
176
|
* Определения основных контролов.
|
|
@@ -166,7 +218,7 @@ type StrictVirtualControls<TControls extends VirtualControls> = TControls & Virt
|
|
|
166
218
|
* при описании устройства в `defineDevice()`.
|
|
167
219
|
**/
|
|
168
220
|
type CreatedControls<TControls extends Controls | VirtualControls> = {
|
|
169
|
-
[K in keyof TControls]: Expand<MaybeReadonlyControl<StrictWhenSpecified<TControls[K], 'defaultValue', WbRules.TypeMappings[TControls[K]['type']]>, TControls[K]['
|
|
221
|
+
[K in keyof TControls]: Expand<MaybeReadonlyControl<StrictWhenSpecified<TControls[K], 'defaultValue', WbRules.TypeMappings[TControls[K]['type']]>, TControls[K]['changePolicy'] extends ChangePolicies.ReadOnly ? true : false>>;
|
|
170
222
|
};
|
|
171
223
|
/** Используется для извлечения типа значения из значения по умолчанию. */
|
|
172
224
|
type InferDefaultType<TProp> = [TProp] extends [{
|
|
@@ -431,5 +483,5 @@ declare function getControlSafe(context: DeviceContext, controlId: string): Cont
|
|
|
431
483
|
**/
|
|
432
484
|
declare function getControlSafe(deviceId: string, controlId: string, isReadyFunc: () => boolean): ControlSafe;
|
|
433
485
|
|
|
434
|
-
export { defineVirtualDevice, defineWiredDevice, defineZigbeeDevice, getControlSafe, getDeviceSafe };
|
|
486
|
+
export { ChangePolicies, defineVirtualDevice, defineWiredDevice, defineZigbeeDevice, getControlSafe, getDeviceSafe };
|
|
435
487
|
export type { ControlSafe, DeviceContext, DeviceType, PropType, TrackCallback };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEvent, isFunction } from '@mirta/basics';
|
|
1
|
+
import { useEvent, mqttToBoolean, isFunction } from '@mirta/basics';
|
|
2
2
|
export * from '@mirta/basics';
|
|
3
3
|
import '@mirta/polyfills';
|
|
4
4
|
|
|
@@ -23,6 +23,45 @@ function getControlSafe(context, controlId, isReadyFunc = () => true) {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Набор политик, управляющих доступностью смены значения контрола.
|
|
28
|
+
*
|
|
29
|
+
* @since 0.2.0
|
|
30
|
+
*
|
|
31
|
+
**/
|
|
32
|
+
var ChangePolicies;
|
|
33
|
+
(function (ChangePolicies) {
|
|
34
|
+
/**
|
|
35
|
+
* Политика по умолчанию.
|
|
36
|
+
*
|
|
37
|
+
* - Применяет {@link Public} к контролам типа `switch`, `pushbutton`, `range` и `rgb`;
|
|
38
|
+
*
|
|
39
|
+
* - Применяет {@link Script} к остальным типам контролов.
|
|
40
|
+
*
|
|
41
|
+
**/
|
|
42
|
+
ChangePolicies["Default"] = "default";
|
|
43
|
+
/**
|
|
44
|
+
* Установка значения возможна как скриптами wb-rules, так и сторонними службами.
|
|
45
|
+
*
|
|
46
|
+
* Применимо к контролам виртуальных устройств.
|
|
47
|
+
*
|
|
48
|
+
**/
|
|
49
|
+
ChangePolicies["Public"] = "public";
|
|
50
|
+
/**
|
|
51
|
+
* Установка значения возможна только скриптами wb-rules.
|
|
52
|
+
*
|
|
53
|
+
* Применимо к контролам виртуальных устройств.
|
|
54
|
+
*
|
|
55
|
+
**/
|
|
56
|
+
ChangePolicies["Script"] = "script";
|
|
57
|
+
/**
|
|
58
|
+
* Полный запрет записи, значение доступно только для чтения.
|
|
59
|
+
*
|
|
60
|
+
* Применимо к контролам реальных устройств.
|
|
61
|
+
*
|
|
62
|
+
**/
|
|
63
|
+
ChangePolicies["ReadOnly"] = "read-only";
|
|
64
|
+
})(ChangePolicies || (ChangePolicies = {}));
|
|
26
65
|
const typeMappings = {
|
|
27
66
|
'text': 'string',
|
|
28
67
|
'value': 'number',
|
|
@@ -35,10 +74,11 @@ const typeMappings = {
|
|
|
35
74
|
/**
|
|
36
75
|
* Создаёт контрол устройства.
|
|
37
76
|
* @since 0.1.0
|
|
77
|
+
*
|
|
38
78
|
**/
|
|
39
79
|
function createControl(context, controlId, options) {
|
|
40
80
|
const { deviceType, deviceId } = context;
|
|
41
|
-
const { type,
|
|
81
|
+
const { type, changePolicy = ChangePolicies.Default } = options;
|
|
42
82
|
const defaultValue = 'defaultValue' in options
|
|
43
83
|
? options['defaultValue']
|
|
44
84
|
: void 0;
|
|
@@ -61,6 +101,7 @@ function createControl(context, controlId, options) {
|
|
|
61
101
|
* Устанавливает новое значение, если оно отличается от существующего.
|
|
62
102
|
* @param newValue Устанавливаемое значение.
|
|
63
103
|
* @param preventEmit Предотвращает отправку значения в устройство.
|
|
104
|
+
*
|
|
64
105
|
**/
|
|
65
106
|
function setValue(newValue, preventEmit = false) {
|
|
66
107
|
const oldValue = localValue;
|
|
@@ -75,21 +116,26 @@ function createControl(context, controlId, options) {
|
|
|
75
116
|
trackMqtt(`/devices/${deviceId}/controls/${controlId}`, (payload) => {
|
|
76
117
|
const incomingType = typeof payload.value;
|
|
77
118
|
const expectingType = typeMappings[type];
|
|
119
|
+
let value;
|
|
78
120
|
if (incomingType === 'string' && expectingType === 'number') {
|
|
79
|
-
const
|
|
80
|
-
if (isNaN(
|
|
121
|
+
const parsedValue = Number(payload.value);
|
|
122
|
+
if (isNaN(parsedValue)) {
|
|
81
123
|
log.error(`Value ignored: control '${deviceId}/${controlId}' expects number, but Number(value) is NaN.`);
|
|
82
124
|
return;
|
|
83
125
|
}
|
|
84
|
-
|
|
126
|
+
value = parsedValue;
|
|
127
|
+
}
|
|
128
|
+
else if (expectingType === 'boolean') {
|
|
129
|
+
value = mqttToBoolean(payload.value);
|
|
130
|
+
}
|
|
131
|
+
else if (incomingType !== expectingType) {
|
|
132
|
+
log.error(`Value ignored: control '${deviceId}/${controlId}' expects type '${expectingType}', but received '${incomingType}'`);
|
|
133
|
+
return;
|
|
85
134
|
}
|
|
86
135
|
else {
|
|
87
|
-
|
|
88
|
-
log.error(`Value ignored: control '${deviceId}/${controlId}' expects type '${expectingType}', but received '${incomingType}'`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
setValue(payload.value, true);
|
|
136
|
+
value = payload.value;
|
|
92
137
|
}
|
|
138
|
+
setValue(value, true);
|
|
93
139
|
});
|
|
94
140
|
return {
|
|
95
141
|
get value() {
|
|
@@ -102,8 +148,8 @@ function createControl(context, controlId, options) {
|
|
|
102
148
|
return localValue;
|
|
103
149
|
},
|
|
104
150
|
set value(newValue) {
|
|
105
|
-
if (
|
|
106
|
-
log.warning(`
|
|
151
|
+
if (changePolicy === ChangePolicies.ReadOnly) {
|
|
152
|
+
log.warning(`A new value of '${deviceId}/${controlId}' has been rejected: control is readonly.`);
|
|
107
153
|
return;
|
|
108
154
|
}
|
|
109
155
|
setValue(newValue);
|
|
@@ -139,6 +185,9 @@ function createContext(deviceType, deviceId, state) {
|
|
|
139
185
|
};
|
|
140
186
|
return context;
|
|
141
187
|
}
|
|
188
|
+
const extendWithReadonly = (changePolicy) => changePolicy && changePolicy !== ChangePolicies.Default
|
|
189
|
+
? { readonly: changePolicy === ChangePolicies.Script || changePolicy === ChangePolicies.ReadOnly }
|
|
190
|
+
: {};
|
|
142
191
|
function configureControls(deviceId, controls) {
|
|
143
192
|
const device = getDevice(deviceId);
|
|
144
193
|
if (!device)
|
|
@@ -152,8 +201,7 @@ function configureControls(deviceId, controls) {
|
|
|
152
201
|
device.removeControl(controlId);
|
|
153
202
|
device.addControl(controlId, assign({}, control, {
|
|
154
203
|
value: control.defaultValue,
|
|
155
|
-
|
|
156
|
-
}));
|
|
204
|
+
}, extendWithReadonly(control.changePolicy)));
|
|
157
205
|
});
|
|
158
206
|
}
|
|
159
207
|
function configureContext(type, deviceId, controls, title) {
|
|
@@ -180,9 +228,10 @@ function configureContext(type, deviceId, controls, title) {
|
|
|
180
228
|
if (type === 'virtual') {
|
|
181
229
|
const cells = {};
|
|
182
230
|
Object.keys(controls).forEach((key) => {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
231
|
+
const control = controls[key];
|
|
232
|
+
const cell = assign({}, control, {
|
|
233
|
+
value: control['defaultValue'],
|
|
234
|
+
}, extendWithReadonly(control['changePolicy']));
|
|
186
235
|
cells[key] = cell;
|
|
187
236
|
});
|
|
188
237
|
global.defineVirtualDevice(deviceId, {
|
|
@@ -217,8 +266,8 @@ function createDevice(deviceType, deviceId, propDefs, props, options) {
|
|
|
217
266
|
const controlId = controlDef.controlId ?? key;
|
|
218
267
|
const control = createControl({ deviceType, deviceId, isReady: true }, controlId, {
|
|
219
268
|
type: controlDef.type,
|
|
269
|
+
changePolicy: controlDef.changePolicy ?? ChangePolicies.Default,
|
|
220
270
|
defaultValue: controlDef.defaultValue,
|
|
221
|
-
isReadonly: controlDef.isReadonly,
|
|
222
271
|
forceDefault: controlDef.forceDefault,
|
|
223
272
|
lazyInit: controlDef.lazyInit,
|
|
224
273
|
});
|
|
@@ -320,4 +369,4 @@ function getDeviceSafe(deviceId, isReadyFunc = () => true) {
|
|
|
320
369
|
};
|
|
321
370
|
}
|
|
322
371
|
|
|
323
|
-
export { defineVirtualDevice, defineWiredDevice, defineZigbeeDevice, getControlSafe, getDeviceSafe };
|
|
372
|
+
export { ChangePolicies, defineVirtualDevice, defineWiredDevice, defineZigbeeDevice, getControlSafe, getDeviceSafe };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mirta",
|
|
3
3
|
"description": "The powerful framework to write smart home automation scripts.",
|
|
4
|
-
"version": "0.1
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"license": "Unlicense",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mirta",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"url": "https://pay.cloudtips.ru/p/58512cca"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@mirta/globals": "0.1
|
|
40
|
-
"@mirta/
|
|
41
|
-
"@mirta/
|
|
39
|
+
"@mirta/globals": "0.2.1",
|
|
40
|
+
"@mirta/basics": "0.2.1",
|
|
41
|
+
"@mirta/polyfills": "0.2.1"
|
|
42
42
|
},
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|