hoffmation-base 3.7.3 → 3.8.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/lib/interfaces/settings/iShutterSettings.d.ts +22 -1
- package/lib/services/weather/weather-service.d.ts +31 -0
- package/lib/services/weather/weather-service.js +88 -16
- package/lib/settingsObjects/deviceSettings/shutterSettings.d.ts +6 -0
- package/lib/settingsObjects/deviceSettings/shutterSettings.js +12 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/utils.d.ts +7 -0
- package/lib/utils/utils.js +10 -0
- package/package.json +1 -1
|
@@ -26,11 +26,32 @@ export interface iShutterSettings extends iDeviceSettings {
|
|
|
26
26
|
*/
|
|
27
27
|
heatReductionDirectionThreshold: number;
|
|
28
28
|
/**
|
|
29
|
-
* The minimum temperature in degree celsius,
|
|
29
|
+
* The minimum temperature in degree celsius, from which on the shutter is closed purely as window insulation,
|
|
30
|
+
* regardless of sun direction and cloudiness. At this point the ambient heat outweighs the solar gain.
|
|
30
31
|
* @type {number}
|
|
31
32
|
* @default 27
|
|
32
33
|
*/
|
|
33
34
|
heatReductionThreshold: number;
|
|
35
|
+
/**
|
|
36
|
+
* The maximum angle in degrees between sun and window direction, for the sun to count as shining onto the window.
|
|
37
|
+
* @type {number}
|
|
38
|
+
* @default 50
|
|
39
|
+
*/
|
|
40
|
+
heatReductionDirectionTolerance: number;
|
|
41
|
+
/**
|
|
42
|
+
* The cloudiness in percent up to which the full {@link heatReductionPosition} is used.
|
|
43
|
+
* Above this value the target position is faded back towards the normal position.
|
|
44
|
+
* @type {number}
|
|
45
|
+
* @default 40
|
|
46
|
+
*/
|
|
47
|
+
heatReductionCloudinessThreshold: number;
|
|
48
|
+
/**
|
|
49
|
+
* The cloudiness in percent from which on no heat reduction takes place at all,
|
|
50
|
+
* as an overcast sky provides too little solar gain to justify a darkened room.
|
|
51
|
+
* @type {number}
|
|
52
|
+
* @default 80
|
|
53
|
+
*/
|
|
54
|
+
heatReductionMaxCloudiness: number;
|
|
34
55
|
/**
|
|
35
56
|
*
|
|
36
57
|
*/
|
|
@@ -18,6 +18,11 @@ export declare class WeatherService {
|
|
|
18
18
|
* The sun horizontal degree (0 is North)
|
|
19
19
|
*/
|
|
20
20
|
static sunDirection: number;
|
|
21
|
+
/**
|
|
22
|
+
* How many degrees a room has to exceed its desired temperature, before the sun shading
|
|
23
|
+
* triggers on the room temperature alone.
|
|
24
|
+
*/
|
|
25
|
+
private static readonly roomOverheatOffset;
|
|
21
26
|
private static _dataUpdateCbs;
|
|
22
27
|
private static _refreshInterval;
|
|
23
28
|
private static latitude;
|
|
@@ -48,6 +53,32 @@ export declare class WeatherService {
|
|
|
48
53
|
static get currentTemp(): number;
|
|
49
54
|
static willOutsideBeWarmer(referenceTemperature: number, logger: (level: LogLevel, message: string, debugType?: LogDebugType) => void): boolean;
|
|
50
55
|
static weatherRolloPosition(normalPos: number, desiredTemperatur: number, currentTemperatur: number, logger: (level: LogLevel, message: string, debugType?: LogDebugType) => void, shutterSettings: ShutterSettings): number;
|
|
56
|
+
/**
|
|
57
|
+
* Determines whether the room is already warmer than wanted.
|
|
58
|
+
*
|
|
59
|
+
* The outside temperature alone cannot answer whether a room needs shading: a low autumn sun
|
|
60
|
+
* shines deep into a south facing room and heats it up considerably while it stays cool outside.
|
|
61
|
+
* The room's own temperature is the honest measure for that, so it may trigger the shading on its own.
|
|
62
|
+
* @param desiredTemperatur - The temperature the room is supposed to have
|
|
63
|
+
* @param currentTemperatur - The temperature the room currently has
|
|
64
|
+
* @returns True if both values are known and the room exceeds the desired temperature noticeably
|
|
65
|
+
*/
|
|
66
|
+
private static isRoomOverheated;
|
|
67
|
+
/**
|
|
68
|
+
* Determines whether the sun currently shines onto this particular window,
|
|
69
|
+
* based on the angle between sun and window direction.
|
|
70
|
+
* @param shutterSettings - The settings of the shutter in question
|
|
71
|
+
* @param logger - The logging function to use
|
|
72
|
+
* @returns 1 while the sun faces the window, 0 otherwise
|
|
73
|
+
*/
|
|
74
|
+
private static solarExposureShare;
|
|
75
|
+
/**
|
|
76
|
+
* Determines how much solar gain the current sky lets through, as an overcast sky provides
|
|
77
|
+
* too little of it to justify a darkened room.
|
|
78
|
+
* @param shutterSettings - The settings of the shutter in question
|
|
79
|
+
* @returns The share of clear sky, between 0 and 1
|
|
80
|
+
*/
|
|
81
|
+
private static skyClearnessShare;
|
|
51
82
|
static getCurrentCloudiness(): number;
|
|
52
83
|
private static getRainNextMinutes;
|
|
53
84
|
private static getActiveAlerts;
|
|
@@ -168,37 +168,104 @@ class WeatherService {
|
|
|
168
168
|
return referenceTemperature < wData.daily[0].temp.max;
|
|
169
169
|
}
|
|
170
170
|
static weatherRolloPosition(normalPos, desiredTemperatur, currentTemperatur, logger, shutterSettings) {
|
|
171
|
-
|
|
171
|
+
const result = normalPos;
|
|
172
172
|
if (currentTemperatur < desiredTemperatur && currentTemperatur < shutterSettings.heatReductionDirectionThreshold) {
|
|
173
173
|
logger(enums_1.LogLevel.Trace, 'RolloWeatherPosition: Room needs to heat up anyways.');
|
|
174
174
|
return result;
|
|
175
175
|
}
|
|
176
|
-
|
|
176
|
+
if (normalPos < shutterSettings.heatReductionPosition) {
|
|
177
177
|
logger(enums_1.LogLevel.Trace, 'RolloWeatherPosition: Shutter should be down anyways.');
|
|
178
178
|
return result;
|
|
179
179
|
}
|
|
180
|
-
|
|
181
|
-
result = shutterSettings.heatReductionPosition;
|
|
182
|
-
}
|
|
183
|
-
else if (this.hoursTilSunset() < 1) {
|
|
180
|
+
if (this.hoursTilSunset() < 1) {
|
|
184
181
|
logger(enums_1.LogLevel.Trace, "RolloWeatherPosition: It's close to or after todays sunset");
|
|
185
182
|
return result;
|
|
186
183
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
// How much of the full reduction is warranted right now, between 0 (none) and 1 (full).
|
|
185
|
+
let reductionShare;
|
|
186
|
+
if (this.willOutsideBeWarmer(shutterSettings.heatReductionThreshold, logger)) {
|
|
187
|
+
// Insulation regime: at this point the ambient heat outweighs the solar gain, so the closed
|
|
188
|
+
// shutter is worth it as additional window insulation - no matter where the sun stands.
|
|
189
|
+
reductionShare = 1;
|
|
190
|
+
}
|
|
191
|
+
else if (this.willOutsideBeWarmer(shutterSettings.heatReductionDirectionThreshold, logger) ||
|
|
192
|
+
this.isRoomOverheated(desiredTemperatur, currentTemperatur)) {
|
|
193
|
+
// Sun shading regime: only the window the sun actually shines on is worth darkening.
|
|
194
|
+
reductionShare = this.solarExposureShare(shutterSettings, logger) * this.skyClearnessShare(shutterSettings);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
logger(enums_1.LogLevel.Trace, "RolloWeatherPosition: It won't get warm enough today.");
|
|
190
198
|
return result;
|
|
191
199
|
}
|
|
192
|
-
|
|
193
|
-
|
|
200
|
+
if (reductionShare <= 0) {
|
|
201
|
+
return result;
|
|
194
202
|
}
|
|
195
|
-
|
|
196
|
-
|
|
203
|
+
const span = normalPos - shutterSettings.heatReductionPosition;
|
|
204
|
+
// Quantized to 10% steps, so slight weather changes don't cause constant shutter movement.
|
|
205
|
+
// A full reduction keeps the configured position verbatim, as that one is a deliberate choice.
|
|
206
|
+
const target = reductionShare >= 1
|
|
207
|
+
? shutterSettings.heatReductionPosition
|
|
208
|
+
: Math.min(normalPos, Math.max(shutterSettings.heatReductionPosition, Math.round((normalPos - span * reductionShare) / 10) * 10));
|
|
209
|
+
if (target !== normalPos) {
|
|
210
|
+
logger(enums_1.LogLevel.Info, `weatherRolloPosition(${normalPos}, ${desiredTemperatur}, ${currentTemperatur}) --> Target: ${target} ` +
|
|
211
|
+
`(share: ${utils_1.Utils.round(reductionShare, 2)}, cloudiness: ${this.getCurrentCloudiness()}%, ` +
|
|
212
|
+
`sunDirection: ${Math.round(this.sunDirection)}°, windowDirection: ${shutterSettings.direction}°)`);
|
|
197
213
|
}
|
|
198
|
-
|
|
199
|
-
|
|
214
|
+
return target;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Determines whether the room is already warmer than wanted.
|
|
218
|
+
*
|
|
219
|
+
* The outside temperature alone cannot answer whether a room needs shading: a low autumn sun
|
|
220
|
+
* shines deep into a south facing room and heats it up considerably while it stays cool outside.
|
|
221
|
+
* The room's own temperature is the honest measure for that, so it may trigger the shading on its own.
|
|
222
|
+
* @param desiredTemperatur - The temperature the room is supposed to have
|
|
223
|
+
* @param currentTemperatur - The temperature the room currently has
|
|
224
|
+
* @returns True if both values are known and the room exceeds the desired temperature noticeably
|
|
225
|
+
*/
|
|
226
|
+
static isRoomOverheated(desiredTemperatur, currentTemperatur) {
|
|
227
|
+
if (desiredTemperatur <= interfaces_1.UNDEFINED_TEMP_VALUE || currentTemperatur <= interfaces_1.UNDEFINED_TEMP_VALUE) {
|
|
228
|
+
// Without a heat group we have no room temperature to judge by.
|
|
229
|
+
return false;
|
|
200
230
|
}
|
|
201
|
-
return
|
|
231
|
+
return currentTemperatur >= desiredTemperatur + WeatherService.roomOverheatOffset;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Determines whether the sun currently shines onto this particular window,
|
|
235
|
+
* based on the angle between sun and window direction.
|
|
236
|
+
* @param shutterSettings - The settings of the shutter in question
|
|
237
|
+
* @param logger - The logging function to use
|
|
238
|
+
* @returns 1 while the sun faces the window, 0 otherwise
|
|
239
|
+
*/
|
|
240
|
+
static solarExposureShare(shutterSettings, logger) {
|
|
241
|
+
if (shutterSettings.direction === undefined) {
|
|
242
|
+
// Without a known direction we have to assume the worst case of a fully exposed window.
|
|
243
|
+
return 1;
|
|
244
|
+
}
|
|
245
|
+
const delta = utils_1.Utils.degreeDistance(shutterSettings.direction, this.sunDirection);
|
|
246
|
+
if (delta > shutterSettings.heatReductionDirectionTolerance) {
|
|
247
|
+
logger(enums_1.LogLevel.Trace, `RolloWeatherPosition: Sun is facing a different direction (${Math.round(delta)}° off).`);
|
|
248
|
+
return 0;
|
|
249
|
+
}
|
|
250
|
+
return 1;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Determines how much solar gain the current sky lets through, as an overcast sky provides
|
|
254
|
+
* too little of it to justify a darkened room.
|
|
255
|
+
* @param shutterSettings - The settings of the shutter in question
|
|
256
|
+
* @returns The share of clear sky, between 0 and 1
|
|
257
|
+
*/
|
|
258
|
+
static skyClearnessShare(shutterSettings) {
|
|
259
|
+
const cloudiness = this.getCurrentCloudiness();
|
|
260
|
+
const fullReductionMax = shutterSettings.heatReductionCloudinessThreshold;
|
|
261
|
+
const noReductionMin = shutterSettings.heatReductionMaxCloudiness;
|
|
262
|
+
if (cloudiness <= fullReductionMax || noReductionMin <= fullReductionMax) {
|
|
263
|
+
return 1;
|
|
264
|
+
}
|
|
265
|
+
if (cloudiness >= noReductionMin) {
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
return 1 - (cloudiness - fullReductionMax) / (noReductionMin - fullReductionMax);
|
|
202
269
|
}
|
|
203
270
|
static getCurrentCloudiness() {
|
|
204
271
|
const wData = WeatherService.lastResponse;
|
|
@@ -285,6 +352,11 @@ WeatherService.active = false;
|
|
|
285
352
|
* The milliseconds of one day
|
|
286
353
|
*/
|
|
287
354
|
WeatherService.oneDay = 1000 * 60 * 60 * 24;
|
|
355
|
+
/**
|
|
356
|
+
* How many degrees a room has to exceed its desired temperature, before the sun shading
|
|
357
|
+
* triggers on the room temperature alone.
|
|
358
|
+
*/
|
|
359
|
+
WeatherService.roomOverheatOffset = 1;
|
|
288
360
|
WeatherService._dataUpdateCbs = {};
|
|
289
361
|
/**
|
|
290
362
|
* The highest maximum temperature forecast for today (prevents oscillation)
|
|
@@ -25,6 +25,12 @@ export declare class ShutterSettings extends DeviceSettings implements iShutterS
|
|
|
25
25
|
heatReductionDirectionThreshold: number;
|
|
26
26
|
/** @inheritDoc */
|
|
27
27
|
heatReductionThreshold: number;
|
|
28
|
+
/** @inheritDoc */
|
|
29
|
+
heatReductionDirectionTolerance: number;
|
|
30
|
+
/** @inheritDoc */
|
|
31
|
+
heatReductionCloudinessThreshold: number;
|
|
32
|
+
/** @inheritDoc */
|
|
33
|
+
heatReductionMaxCloudiness: number;
|
|
28
34
|
/**
|
|
29
35
|
* Some shutter give no position feedback on their own, so by knowing the durations in either direction,
|
|
30
36
|
* we can programmatically trigger the callbacks.
|
|
@@ -30,6 +30,12 @@ class ShutterSettings extends deviceSettings_1.DeviceSettings {
|
|
|
30
30
|
this.heatReductionDirectionThreshold = 24;
|
|
31
31
|
/** @inheritDoc */
|
|
32
32
|
this.heatReductionThreshold = 27;
|
|
33
|
+
/** @inheritDoc */
|
|
34
|
+
this.heatReductionDirectionTolerance = 50;
|
|
35
|
+
/** @inheritDoc */
|
|
36
|
+
this.heatReductionCloudinessThreshold = 40;
|
|
37
|
+
/** @inheritDoc */
|
|
38
|
+
this.heatReductionMaxCloudiness = 80;
|
|
33
39
|
/**
|
|
34
40
|
* Some shutter give no position feedback on their own, so by knowing the durations in either direction,
|
|
35
41
|
* we can programmatically trigger the callbacks.
|
|
@@ -38,14 +44,18 @@ class ShutterSettings extends deviceSettings_1.DeviceSettings {
|
|
|
38
44
|
this.triggerPositionUpdateByTime = false;
|
|
39
45
|
}
|
|
40
46
|
fromPartialObject(data) {
|
|
41
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
47
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
42
48
|
this.msTilTop = (_a = data.msTilTop) !== null && _a !== void 0 ? _a : this.msTilTop;
|
|
43
49
|
this.msTilBot = (_b = data.msTilBot) !== null && _b !== void 0 ? _b : this.msTilBot;
|
|
44
50
|
this.direction = (_c = data.direction) !== null && _c !== void 0 ? _c : this.direction;
|
|
45
51
|
this.heatReductionPosition = (_d = data.heatReductionPosition) !== null && _d !== void 0 ? _d : this.heatReductionPosition;
|
|
46
52
|
this.heatReductionThreshold = (_e = data.heatReductionThreshold) !== null && _e !== void 0 ? _e : this.heatReductionThreshold;
|
|
47
53
|
this.heatReductionDirectionThreshold = (_f = data.heatReductionDirectionThreshold) !== null && _f !== void 0 ? _f : this.heatReductionDirectionThreshold;
|
|
48
|
-
this.
|
|
54
|
+
this.heatReductionDirectionTolerance = (_g = data.heatReductionDirectionTolerance) !== null && _g !== void 0 ? _g : this.heatReductionDirectionTolerance;
|
|
55
|
+
this.heatReductionCloudinessThreshold =
|
|
56
|
+
(_h = data.heatReductionCloudinessThreshold) !== null && _h !== void 0 ? _h : this.heatReductionCloudinessThreshold;
|
|
57
|
+
this.heatReductionMaxCloudiness = (_j = data.heatReductionMaxCloudiness) !== null && _j !== void 0 ? _j : this.heatReductionMaxCloudiness;
|
|
58
|
+
this.triggerPositionUpdateByTime = (_k = data.triggerPositionUpdateByTime) !== null && _k !== void 0 ? _k : this.triggerPositionUpdateByTime;
|
|
49
59
|
super.fromPartialObject(data);
|
|
50
60
|
}
|
|
51
61
|
toJSON() {
|