hoffmation-base 3.8.0 → 4.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/lib/action/index.d.ts +1 -0
- package/lib/action/index.js +3 -1
- package/lib/action/soilSensorChangeAction.d.ts +16 -0
- package/lib/action/soilSensorChangeAction.js +15 -0
- package/lib/devices/dachs/dachs.d.ts +79 -2
- package/lib/devices/dachs/dachs.js +214 -18
- package/lib/devices/device-cluster.js +7 -0
- package/lib/devices/devices.d.ts +36 -1
- package/lib/devices/devices.js +67 -0
- package/lib/devices/jsObject/jsObjectEnergyManager.d.ts +16 -1
- package/lib/devices/jsObject/jsObjectEnergyManager.js +23 -0
- package/lib/devices/sharedFunctions/index.d.ts +1 -0
- package/lib/devices/sharedFunctions/index.js +1 -0
- package/lib/devices/sharedFunctions/soilSensor.d.ts +23 -0
- package/lib/devices/sharedFunctions/soilSensor.js +58 -0
- package/lib/devices/victron/victron-device.d.ts +83 -1
- package/lib/devices/victron/victron-device.js +151 -2
- package/lib/devices/victron/victron-morning-release.d.ts +16 -0
- package/lib/devices/victron/victron-morning-release.js +2 -0
- package/lib/devices/zigbee/BaseDevices/index.d.ts +1 -0
- package/lib/devices/zigbee/BaseDevices/index.js +1 -0
- package/lib/devices/zigbee/BaseDevices/zigbeeSoilSensor.d.ts +33 -0
- package/lib/devices/zigbee/BaseDevices/zigbeeSoilSensor.js +62 -0
- package/lib/devices/zigbee/index.d.ts +1 -0
- package/lib/devices/zigbee/index.js +1 -0
- package/lib/devices/zigbee/zigbeeCooloSoilSensor.d.ts +44 -0
- package/lib/devices/zigbee/zigbeeCooloSoilSensor.js +81 -0
- package/lib/enums/DeviceCapability.d.ts +2 -0
- package/lib/enums/DeviceCapability.js +2 -0
- package/lib/enums/commandType.d.ts +1 -0
- package/lib/enums/commandType.js +1 -0
- package/lib/enums/device-cluster-type.d.ts +2 -1
- package/lib/enums/device-cluster-type.js +1 -0
- package/lib/enums/deviceType.d.ts +1 -0
- package/lib/enums/deviceType.js +1 -0
- package/lib/interfaces/baseDevices/iAirQualityCollector.d.ts +2 -1
- package/lib/interfaces/baseDevices/iAirQualityReadings.d.ts +50 -0
- package/lib/interfaces/baseDevices/iAirQualityReadings.js +2 -0
- package/lib/interfaces/baseDevices/iAirQualitySensor.d.ts +1 -54
- package/lib/interfaces/baseDevices/iAirQualitySensor.js +0 -5
- package/lib/interfaces/baseDevices/iEnergyManager.d.ts +43 -0
- package/lib/interfaces/baseDevices/iHumidityCollector.d.ts +0 -1
- package/lib/interfaces/baseDevices/iHumidityCollector.js +0 -2
- package/lib/interfaces/baseDevices/iSoilCollector.d.ts +23 -0
- package/lib/interfaces/baseDevices/iSoilCollector.js +2 -0
- package/lib/interfaces/baseDevices/iSoilSensor.d.ts +32 -0
- package/lib/interfaces/baseDevices/iSoilSensor.js +2 -0
- package/lib/interfaces/baseDevices/iTemperatureCollector.d.ts +0 -1
- package/lib/interfaces/baseDevices/iTemperatureCollector.js +0 -2
- package/lib/interfaces/baseDevices/index.d.ts +7 -0
- package/lib/interfaces/baseDevices/index.js +7 -0
- package/lib/interfaces/baseDevices/undefinedAirQualityValue.d.ts +4 -0
- package/lib/interfaces/baseDevices/undefinedAirQualityValue.js +7 -0
- package/lib/interfaces/baseDevices/undefinedHumidityValue.d.ts +4 -0
- package/lib/interfaces/baseDevices/undefinedHumidityValue.js +7 -0
- package/lib/interfaces/baseDevices/undefinedSoilMoistureValue.d.ts +6 -0
- package/lib/interfaces/baseDevices/undefinedSoilMoistureValue.js +9 -0
- package/lib/interfaces/baseDevices/undefinedTempValue.d.ts +4 -0
- package/lib/interfaces/baseDevices/undefinedTempValue.js +7 -0
- package/lib/interfaces/dachs/KeyListEntity.d.ts +0 -5
- package/lib/interfaces/dachs/KeyListEntityResponse.d.ts +6 -0
- package/lib/interfaces/dachs/KeyListEntityResponse.js +2 -0
- package/lib/interfaces/dachs/index.d.ts +1 -0
- package/lib/interfaces/dachs/index.js +1 -0
- package/lib/interfaces/deviceSettings/iDachsDeviceSettings.d.ts +15 -0
- package/lib/interfaces/iActuatorStateSample.d.ts +13 -0
- package/lib/interfaces/iActuatorStateSample.js +2 -0
- package/lib/interfaces/iBatteryLevelSample.d.ts +13 -0
- package/lib/interfaces/iBatteryLevelSample.js +2 -0
- package/lib/interfaces/iConsumptionWindowSample.d.ts +26 -0
- package/lib/interfaces/iConsumptionWindowSample.js +2 -0
- package/lib/interfaces/iDachsHistoryGateResult.d.ts +29 -0
- package/lib/interfaces/iDachsHistoryGateResult.js +2 -0
- package/lib/interfaces/iEnergyHistoryBasis.d.ts +26 -0
- package/lib/interfaces/iEnergyHistoryBasis.js +2 -0
- package/lib/interfaces/iEnergyHistoryEstimate.d.ts +21 -0
- package/lib/interfaces/iEnergyHistoryEstimate.js +2 -0
- package/lib/interfaces/iEnergyHistoryFeatures.d.ts +21 -0
- package/lib/interfaces/iEnergyHistoryFeatures.js +2 -0
- package/lib/interfaces/iEnergyHistoryModel.d.ts +21 -0
- package/lib/interfaces/iEnergyHistoryModel.js +2 -0
- package/lib/interfaces/iEnergyHistoryOptions.d.ts +29 -0
- package/lib/interfaces/iEnergyHistoryOptions.js +2 -0
- package/lib/interfaces/iEnergyHistoryOutlook.d.ts +41 -0
- package/lib/interfaces/iEnergyHistoryOutlook.js +2 -0
- package/lib/interfaces/iEnergyHistorySample.d.ts +19 -0
- package/lib/interfaces/iEnergyHistorySample.js +2 -0
- package/lib/interfaces/iFossilGeneratorRun.d.ts +22 -0
- package/lib/interfaces/iFossilGeneratorRun.js +2 -0
- package/lib/interfaces/iFossilGeneratorSource.d.ts +21 -0
- package/lib/interfaces/iFossilGeneratorSource.js +2 -0
- package/lib/interfaces/iMorningReserveVerdict.d.ts +38 -0
- package/lib/interfaces/iMorningReserveVerdict.js +2 -0
- package/lib/interfaces/iPersist.d.ts +69 -1
- package/lib/interfaces/iProjectedSocBand.d.ts +7 -0
- package/lib/interfaces/iProjectedSocBand.js +2 -0
- package/lib/interfaces/iSoilMoistureSample.d.ts +13 -0
- package/lib/interfaces/iSoilMoistureSample.js +2 -0
- package/lib/interfaces/iWeatherDaySummary.d.ts +33 -0
- package/lib/interfaces/iWeatherDaySummary.js +2 -0
- package/lib/interfaces/index.d.ts +17 -0
- package/lib/interfaces/index.js +17 -0
- package/lib/services/dbo/actuator-state-row.d.ts +14 -0
- package/lib/services/dbo/actuator-state-row.js +2 -0
- package/lib/services/dbo/battery-level-row.d.ts +16 -0
- package/lib/services/dbo/battery-level-row.js +2 -0
- package/lib/services/dbo/energy-consumption-row.d.ts +16 -0
- package/lib/services/dbo/energy-consumption-row.js +2 -0
- package/lib/services/dbo/postgreSqlPersist.d.ts +52 -1
- package/lib/services/dbo/postgreSqlPersist.js +392 -27
- package/lib/services/dbo/soil-moisture-row.d.ts +14 -0
- package/lib/services/dbo/soil-moisture-row.js +2 -0
- package/lib/services/dbo/weather-day-summary-row.d.ts +20 -0
- package/lib/services/dbo/weather-day-summary-row.js +2 -0
- package/lib/services/energy/energy-history-service.d.ts +168 -0
- package/lib/services/energy/energy-history-service.js +499 -0
- package/lib/services/energy/index.d.ts +1 -0
- package/lib/services/energy/index.js +17 -0
- package/lib/services/energy/plant-energy-dials.d.ts +22 -0
- package/lib/services/energy/plant-energy-dials.js +2 -0
- package/lib/services/https-service.d.ts +48 -2
- package/lib/services/https-service.js +136 -10
- package/lib/services/index.d.ts +1 -0
- package/lib/services/index.js +1 -0
- package/lib/services/time-callback-service.d.ts +12 -0
- package/lib/services/time-callback-service.js +16 -2
- package/lib/services/weather/index.d.ts +1 -0
- package/lib/services/weather/index.js +1 -0
- package/lib/services/weather/open-weather-day-summary.d.ts +25 -0
- package/lib/services/weather/open-weather-day-summary.js +2 -0
- package/lib/services/weather/weather-day-summary-fetcher.d.ts +6 -0
- package/lib/services/weather/weather-day-summary-fetcher.js +2 -0
- package/lib/services/weather/weather-history-backfill.d.ts +94 -0
- package/lib/services/weather/weather-history-backfill.js +247 -0
- package/lib/services/weather/weather-service.js +29 -4
- package/lib/settings-service-instance.js +5 -2
- package/lib/settingsObjects/deviceSettings/dachsSettings.d.ts +6 -0
- package/lib/settingsObjects/deviceSettings/dachsSettings.js +14 -1
- package/lib/settingsObjects/deviceSettings/victronDeviceSettings.d.ts +116 -0
- package/lib/settingsObjects/deviceSettings/victronDeviceSettings.js +150 -5
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/dayMs.d.ts +7 -0
- package/lib/utils/dayMs.js +10 -0
- package/lib/utils/energy-history-dials.d.ts +19 -0
- package/lib/utils/energy-history-dials.js +2 -0
- package/lib/utils/energy-history-utils.d.ts +143 -0
- package/lib/utils/energy-history-utils.js +470 -0
- package/lib/utils/energy-manager-utils.d.ts +123 -1
- package/lib/utils/energy-manager-utils.js +226 -0
- package/lib/utils/index.d.ts +4 -0
- package/lib/utils/index.js +4 -0
- package/lib/utils/model-shadow.d.ts +61 -0
- package/lib/utils/model-shadow.js +83 -0
- package/lib/utils/morning-reserve-dials.d.ts +21 -0
- package/lib/utils/morning-reserve-dials.js +2 -0
- package/lib/utils/morning-verdict.d.ts +5 -0
- package/lib/utils/morning-verdict.js +2 -0
- package/lib/utils/utils.d.ts +0 -1
- package/lib/utils/utils.js +5 -5
- package/package.json +3 -2
package/lib/action/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { MotionSensorAction } from './motionSensorAction';
|
|
|
5
5
|
export { PresenceGroupLastLeftAction } from './presenceGroupLastLeftAction';
|
|
6
6
|
export { PresenceGroupFirstEnterAction } from './presenceGroupFirstEnterAction';
|
|
7
7
|
export { ShutterPositionChangedAction } from './shutterPositionChangedAction';
|
|
8
|
+
export { SoilSensorChangeAction } from './soilSensorChangeAction';
|
|
8
9
|
export { TemperatureSensorChangeAction } from './temperatureSensorChangeAction';
|
|
9
10
|
export { ActuatorChangeAction } from './actuatorChangeAction';
|
|
10
11
|
export { BatteryLevelChangeAction } from './batteryLevelChangeAction';
|
package/lib/action/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DingSensorAction = exports.HandleChangeAction = exports.PresenceGroupAnyMovementAction = exports.BatteryLevelChangeAction = exports.ActuatorChangeAction = exports.TemperatureSensorChangeAction = exports.ShutterPositionChangedAction = exports.PresenceGroupFirstEnterAction = exports.PresenceGroupLastLeftAction = exports.MotionSensorAction = exports.HumiditySensorChangeAction = exports.BaseAction = exports.AirQualitySensorChangeAction = void 0;
|
|
3
|
+
exports.DingSensorAction = exports.HandleChangeAction = exports.PresenceGroupAnyMovementAction = exports.BatteryLevelChangeAction = exports.ActuatorChangeAction = exports.TemperatureSensorChangeAction = exports.SoilSensorChangeAction = exports.ShutterPositionChangedAction = exports.PresenceGroupFirstEnterAction = exports.PresenceGroupLastLeftAction = exports.MotionSensorAction = exports.HumiditySensorChangeAction = exports.BaseAction = exports.AirQualitySensorChangeAction = void 0;
|
|
4
4
|
var airQualitySensorChangeAction_1 = require("./airQualitySensorChangeAction");
|
|
5
5
|
Object.defineProperty(exports, "AirQualitySensorChangeAction", { enumerable: true, get: function () { return airQualitySensorChangeAction_1.AirQualitySensorChangeAction; } });
|
|
6
6
|
var baseAction_1 = require("./baseAction");
|
|
@@ -15,6 +15,8 @@ var presenceGroupFirstEnterAction_1 = require("./presenceGroupFirstEnterAction")
|
|
|
15
15
|
Object.defineProperty(exports, "PresenceGroupFirstEnterAction", { enumerable: true, get: function () { return presenceGroupFirstEnterAction_1.PresenceGroupFirstEnterAction; } });
|
|
16
16
|
var shutterPositionChangedAction_1 = require("./shutterPositionChangedAction");
|
|
17
17
|
Object.defineProperty(exports, "ShutterPositionChangedAction", { enumerable: true, get: function () { return shutterPositionChangedAction_1.ShutterPositionChangedAction; } });
|
|
18
|
+
var soilSensorChangeAction_1 = require("./soilSensorChangeAction");
|
|
19
|
+
Object.defineProperty(exports, "SoilSensorChangeAction", { enumerable: true, get: function () { return soilSensorChangeAction_1.SoilSensorChangeAction; } });
|
|
18
20
|
var temperatureSensorChangeAction_1 = require("./temperatureSensorChangeAction");
|
|
19
21
|
Object.defineProperty(exports, "TemperatureSensorChangeAction", { enumerable: true, get: function () { return temperatureSensorChangeAction_1.TemperatureSensorChangeAction; } });
|
|
20
22
|
var actuatorChangeAction_1 = require("./actuatorChangeAction");
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseAction } from './baseAction';
|
|
2
|
+
import { CommandType } from '../enums';
|
|
3
|
+
import { iSoilCollector } from '../interfaces';
|
|
4
|
+
export declare class SoilSensorChangeAction extends BaseAction {
|
|
5
|
+
/** @inheritDoc */
|
|
6
|
+
type: CommandType;
|
|
7
|
+
/**
|
|
8
|
+
* The new soil moisture in percent
|
|
9
|
+
*/
|
|
10
|
+
readonly newSoilMoisture: number;
|
|
11
|
+
/**
|
|
12
|
+
* The sensor that triggered the action
|
|
13
|
+
*/
|
|
14
|
+
readonly sensor: iSoilCollector;
|
|
15
|
+
constructor(sensor: iSoilCollector, newSoilMoisture: number);
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SoilSensorChangeAction = void 0;
|
|
4
|
+
const baseAction_1 = require("./baseAction");
|
|
5
|
+
const enums_1 = require("../enums");
|
|
6
|
+
class SoilSensorChangeAction extends baseAction_1.BaseAction {
|
|
7
|
+
constructor(sensor, newSoilMoisture) {
|
|
8
|
+
super(enums_1.CommandSource.Automatic, `${sensor.customName} detected ${newSoilMoisture}% soil moisture`);
|
|
9
|
+
/** @inheritDoc */
|
|
10
|
+
this.type = enums_1.CommandType.SoilSensorChangeAction;
|
|
11
|
+
this.newSoilMoisture = newSoilMoisture;
|
|
12
|
+
this.sensor = sensor;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.SoilSensorChangeAction = SoilSensorChangeAction;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { iActuator, iBaseDevice, iDachsDeviceSettings, iDachsSettings } from '../../interfaces';
|
|
1
|
+
import { iActuator, iBaseDevice, iDachsDeviceSettings, iDachsSettings, iFossilGeneratorSource } from '../../interfaces';
|
|
2
2
|
import { DeviceCapability } from '../../enums';
|
|
3
3
|
import { DachsTemperatureSensor } from './dachsTemperatureSensor';
|
|
4
4
|
import { ActuatorSetStateCommand, ActuatorToggleCommand, ActuatorWriteStateToDeviceCommand, RestoreTargetAutomaticValueCommand } from '../../command';
|
|
5
5
|
import { BlockAutomaticHandler } from '../../services';
|
|
6
6
|
import { RoomBaseDevice } from '../RoomBaseDevice';
|
|
7
|
-
export declare class Dachs extends RoomBaseDevice implements iBaseDevice, iActuator {
|
|
7
|
+
export declare class Dachs extends RoomBaseDevice implements iBaseDevice, iActuator, iFossilGeneratorSource {
|
|
8
8
|
/** @inheritDoc */
|
|
9
9
|
settings: iDachsDeviceSettings;
|
|
10
10
|
/** @inheritDoc */
|
|
@@ -47,10 +47,41 @@ export declare class Dachs extends RoomBaseDevice implements iBaseDevice, iActua
|
|
|
47
47
|
private _tempHeatStorage;
|
|
48
48
|
private fetchedData;
|
|
49
49
|
private _blockStarted;
|
|
50
|
+
/** The decision of the last history gate evaluation; undefined out of season or without a charge level. */
|
|
51
|
+
private _historyGateResult;
|
|
52
|
+
private _historyGateEvaluations;
|
|
53
|
+
private _historyGateStatements;
|
|
54
|
+
private _lastGateDecision;
|
|
50
55
|
/** @inheritDoc */
|
|
51
56
|
get customName(): string;
|
|
52
57
|
/** @inheritDoc */
|
|
53
58
|
get actuatorOn(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The unit persists its own state changes under {@link id}, so this is the id they are recorded under.
|
|
61
|
+
* @inheritDoc
|
|
62
|
+
*/
|
|
63
|
+
get actuatorId(): string;
|
|
64
|
+
/**
|
|
65
|
+
* A live view of the setting rather than a copy: it is editable at runtime, and the correction of a
|
|
66
|
+
* historical day describes the unit as it is configured now.
|
|
67
|
+
* @inheritDoc
|
|
68
|
+
*/
|
|
69
|
+
get ratedElectricalWattage(): number;
|
|
70
|
+
/**
|
|
71
|
+
* A live view of the setting, for the same reason as {@link ratedElectricalWattage}.
|
|
72
|
+
* @inheritDoc
|
|
73
|
+
*/
|
|
74
|
+
get conversionFactor(): number;
|
|
75
|
+
/**
|
|
76
|
+
* The upper warm water temperature the Dachs is currently allowed to heat to.
|
|
77
|
+
*
|
|
78
|
+
* Only a stated warm season lowers the ceiling. The heating settings are optional and fall back to
|
|
79
|
+
* {@link HeatingMode.None}, so "not winter" covers the installation that states nothing at all - and
|
|
80
|
+
* reading that absence as summer takes the ceiling away from it the whole year round, without a switch
|
|
81
|
+
* anywhere to notice it by.
|
|
82
|
+
* @returns The applicable maximum warm water temperature in °C.
|
|
83
|
+
*/
|
|
84
|
+
private get currentWarmWaterMaxTemp();
|
|
54
85
|
constructor(options: iDachsSettings);
|
|
55
86
|
/** @inheritDoc */
|
|
56
87
|
get id(): string;
|
|
@@ -72,11 +103,57 @@ export declare class Dachs extends RoomBaseDevice implements iBaseDevice, iActua
|
|
|
72
103
|
*/
|
|
73
104
|
private onBatteryLevelChange;
|
|
74
105
|
private checkAllDesiredStates;
|
|
106
|
+
/**
|
|
107
|
+
* The state of charge of the plant's battery, as far as it is known.
|
|
108
|
+
* @returns The level in percent, or {@link NO_STATE_OF_CHARGE} while there is no energy manager or it
|
|
109
|
+
* carries no level - neither of which is a battery that is empty.
|
|
110
|
+
*/
|
|
111
|
+
private static get currentStateOfCharge();
|
|
75
112
|
private onHeatStorageTempChange;
|
|
76
113
|
private onWarmWaterTempChange;
|
|
77
114
|
private checkWwPumpDesiredState;
|
|
78
115
|
private checkHeatingRod;
|
|
79
116
|
private shouldDachsBeStarted;
|
|
117
|
+
/**
|
|
118
|
+
* What this unit makes of what the plant said about the coming morning.
|
|
119
|
+
*
|
|
120
|
+
* Its own question comes first and is the only one it answers itself: while the warm water sits below the
|
|
121
|
+
* minimum the unit is needed for heat, so energy does not decide and the plant's verdict is recorded without
|
|
122
|
+
* being acted on. Everything below that is translation - the plant judged whether the morning holds, this
|
|
123
|
+
* unit only says what it does about it.
|
|
124
|
+
*
|
|
125
|
+
* **A verdict the plant reached without its fitted model may move something, a modelled one may not.** The
|
|
126
|
+
* measured verdicts are arithmetic on the plant's own recorded consumption and carry no unmeasured
|
|
127
|
+
* assumption; the modelled one rests on a fit whose window length has never been checked against recorded
|
|
128
|
+
* data. Stage 4 therefore names which side of the reserve the band fell on and moves nothing - behaviourally
|
|
129
|
+
* the same as stage 5 and distinguishable from it only by the reason line. Whether the model beats the
|
|
130
|
+
* trivial rule is measured once, by the energy manager, over the plant's single shadow record.
|
|
131
|
+
*
|
|
132
|
+
* The stage numbers are this unit's log vocabulary rather than the plant's ladder: stage 1 is the warm water
|
|
133
|
+
* question nobody else has, stages 2 and 3 are the measured verdicts it acts on, stage 4 the modelled one it
|
|
134
|
+
* only reports, stage 5 no verdict at all.
|
|
135
|
+
* @returns The decision, or undefined while the heating mode is winter or the plant says nothing about the
|
|
136
|
+
* coming morning.
|
|
137
|
+
*/
|
|
138
|
+
private evaluateHistoryGate;
|
|
139
|
+
/**
|
|
140
|
+
* Assembles a gate result and the reason line the operator reads it by.
|
|
141
|
+
* @param stage - The stage of this unit's own vocabulary that decided.
|
|
142
|
+
* @param suppress - Whether the electricity driven start is suppressed.
|
|
143
|
+
* @param request - Whether a start is asked for.
|
|
144
|
+
* @param detail - What the plant said, or what this unit answered instead.
|
|
145
|
+
* @param verdict - The plant's verdict, for the numbers the line is read by.
|
|
146
|
+
* @param lowerEdgeSoc - The projected morning charge level at the lower edge.
|
|
147
|
+
* @param upperEdgeSoc - The projected morning charge level at the upper edge.
|
|
148
|
+
* @returns The decision.
|
|
149
|
+
*/
|
|
150
|
+
private static gateResult;
|
|
151
|
+
/**
|
|
152
|
+
* Writes the gate decision to the log - on info whenever the decision changed, on debug otherwise, as the
|
|
153
|
+
* evaluation runs on every battery and temperature update.
|
|
154
|
+
* @param result - The decision to log, if there is one.
|
|
155
|
+
*/
|
|
156
|
+
private logHistoryGate;
|
|
80
157
|
private checkAlternativeActuator;
|
|
81
158
|
private onDachsRunningStateChange;
|
|
82
159
|
}
|
|
@@ -19,6 +19,14 @@ const command_1 = require("../../command");
|
|
|
19
19
|
const services_1 = require("../../services");
|
|
20
20
|
const settings_service_1 = require("../../settings-service");
|
|
21
21
|
const RoomBaseDevice_1 = require("../RoomBaseDevice");
|
|
22
|
+
/**
|
|
23
|
+
* What an energy manager reports while its battery states no charge level at all - see the battery level of
|
|
24
|
+
* `victron-device.ts`, which answers with this instead of a level.
|
|
25
|
+
*
|
|
26
|
+
* A marker, not a low battery: read as a number it lands below every threshold of this unit, so whoever
|
|
27
|
+
* decides on it has to tell the two apart first.
|
|
28
|
+
*/
|
|
29
|
+
const NO_STATE_OF_CHARGE = -1;
|
|
22
30
|
class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
23
31
|
/** @inheritDoc */
|
|
24
32
|
get customName() {
|
|
@@ -28,6 +36,43 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
28
36
|
get actuatorOn() {
|
|
29
37
|
return this._dachsOn;
|
|
30
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The unit persists its own state changes under {@link id}, so this is the id they are recorded under.
|
|
41
|
+
* @inheritDoc
|
|
42
|
+
*/
|
|
43
|
+
get actuatorId() {
|
|
44
|
+
return this.id;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A live view of the setting rather than a copy: it is editable at runtime, and the correction of a
|
|
48
|
+
* historical day describes the unit as it is configured now.
|
|
49
|
+
* @inheritDoc
|
|
50
|
+
*/
|
|
51
|
+
get ratedElectricalWattage() {
|
|
52
|
+
return this.settings.dachsRatedElectricalWattage;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A live view of the setting, for the same reason as {@link ratedElectricalWattage}.
|
|
56
|
+
* @inheritDoc
|
|
57
|
+
*/
|
|
58
|
+
get conversionFactor() {
|
|
59
|
+
return this.settings.dachsConversionFactor;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The upper warm water temperature the Dachs is currently allowed to heat to.
|
|
63
|
+
*
|
|
64
|
+
* Only a stated warm season lowers the ceiling. The heating settings are optional and fall back to
|
|
65
|
+
* {@link HeatingMode.None}, so "not winter" covers the installation that states nothing at all - and
|
|
66
|
+
* reading that absence as summer takes the ceiling away from it the whole year round, without a switch
|
|
67
|
+
* anywhere to notice it by.
|
|
68
|
+
* @returns The applicable maximum warm water temperature in °C.
|
|
69
|
+
*/
|
|
70
|
+
get currentWarmWaterMaxTemp() {
|
|
71
|
+
const mode = settings_service_1.SettingsService.heatMode;
|
|
72
|
+
return mode === enums_1.HeatingMode.Summer || mode === enums_1.HeatingMode.TransitionalSeason
|
|
73
|
+
? this.settings.summerWarmWaterDesiredMaxTemp
|
|
74
|
+
: this.settings.warmWaterDesiredMaxTemp;
|
|
75
|
+
}
|
|
31
76
|
constructor(options) {
|
|
32
77
|
var _a, _b;
|
|
33
78
|
const info = new DeviceInfo_1.DeviceInfo();
|
|
@@ -52,6 +97,8 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
52
97
|
* The timestamp of the last time the Block was enforced.
|
|
53
98
|
*/
|
|
54
99
|
this._blockStarted = 0;
|
|
100
|
+
this._historyGateEvaluations = 0;
|
|
101
|
+
this._historyGateStatements = 0;
|
|
55
102
|
this.jsonOmitTopLevelKeys.push(...['warmWaterPump', 'heatingRod']);
|
|
56
103
|
this.jsonOmitKeys.push(...[
|
|
57
104
|
'client',
|
|
@@ -65,6 +112,9 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
65
112
|
devices_1.Devices.alLDevices[allDevicesKey] = this;
|
|
66
113
|
this.deviceCapabilities.push(enums_1.DeviceCapability.actuator);
|
|
67
114
|
this.deviceCapabilities.push(enums_1.DeviceCapability.blockAutomatic);
|
|
115
|
+
// How the unit announces itself as one of the plant's fuel burning generators: whoever corrects a
|
|
116
|
+
// historical charge level for fuel burnt reads the device list, not a list handed to it by a caller.
|
|
117
|
+
this.deviceCapabilities.push(enums_1.DeviceCapability.fossilGenerator);
|
|
68
118
|
if (options.influxDb) {
|
|
69
119
|
this._influxClient = new lib_1.DachsInfluxClient(options.influxDb);
|
|
70
120
|
}
|
|
@@ -149,7 +199,7 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
149
199
|
this.settings.disableDachsOwnWW ||
|
|
150
200
|
((_a = this.warmWaterDachsAlternativeActuator) === null || _a === void 0 ? void 0 : _a.actuatorOn) ||
|
|
151
201
|
this.heatStorageTempSensor.temperatureSensor.temperature < this.warmWaterSensor.temperatureSensor.temperature ||
|
|
152
|
-
this.warmWaterSensor.temperatureSensor.temperature > this.
|
|
202
|
+
this.warmWaterSensor.temperatureSensor.temperature > this.currentWarmWaterMaxTemp) {
|
|
153
203
|
return;
|
|
154
204
|
}
|
|
155
205
|
const startPumpCommand = new command_1.ActuatorSetStateCommand(c, true, 'Dachs is starting/on');
|
|
@@ -200,6 +250,10 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
200
250
|
if (this.blockAutomationHandler.automaticBlockActive) {
|
|
201
251
|
return;
|
|
202
252
|
}
|
|
253
|
+
// Read, not refreshed. The plant's history belongs to the energy manager, which keeps it up to date on a
|
|
254
|
+
// cadence of its own; asking here costs no query, so the decision may be taken as often as it likes.
|
|
255
|
+
this._historyGateResult = this.evaluateHistoryGate();
|
|
256
|
+
this.logHistoryGate(this._historyGateResult);
|
|
203
257
|
const shouldDachsBeStarted = this.shouldDachsBeStarted(action, batteryLevel);
|
|
204
258
|
this.checkHeatingRod(action, batteryLevel);
|
|
205
259
|
this.checkAlternativeActuator(shouldDachsBeStarted, action);
|
|
@@ -211,13 +265,20 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
211
265
|
setStateCommand.overrideCommandSource = enums_1.CommandSource.Automatic;
|
|
212
266
|
this.setActuator(setStateCommand);
|
|
213
267
|
}
|
|
214
|
-
|
|
268
|
+
/**
|
|
269
|
+
* The state of charge of the plant's battery, as far as it is known.
|
|
270
|
+
* @returns The level in percent, or {@link NO_STATE_OF_CHARGE} while there is no energy manager or it
|
|
271
|
+
* carries no level - neither of which is a battery that is empty.
|
|
272
|
+
*/
|
|
273
|
+
static get currentStateOfCharge() {
|
|
215
274
|
var _a, _b;
|
|
216
|
-
|
|
275
|
+
return (_b = (_a = devices_1.Devices.energymanager) === null || _a === void 0 ? void 0 : _a.batteryLevel) !== null && _b !== void 0 ? _b : NO_STATE_OF_CHARGE;
|
|
276
|
+
}
|
|
277
|
+
onHeatStorageTempChange(action) {
|
|
278
|
+
this.checkAllDesiredStates(action, Dachs.currentStateOfCharge);
|
|
217
279
|
}
|
|
218
280
|
onWarmWaterTempChange(action) {
|
|
219
|
-
|
|
220
|
-
this.checkAllDesiredStates(action, (_b = (_a = devices_1.Devices.energymanager) === null || _a === void 0 ? void 0 : _a.batteryLevel) !== null && _b !== void 0 ? _b : 0);
|
|
281
|
+
this.checkAllDesiredStates(action, Dachs.currentStateOfCharge);
|
|
221
282
|
}
|
|
222
283
|
checkWwPumpDesiredState(action) {
|
|
223
284
|
var _a, _b;
|
|
@@ -245,9 +306,9 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
245
306
|
desiredWwPumpState = false;
|
|
246
307
|
reason = `Temperature of warm water pump ${wwTemp}°C is higher than temperature of heat storage ${heatStorageTemp}°C`;
|
|
247
308
|
}
|
|
248
|
-
else if (wwTemp > this.
|
|
309
|
+
else if (wwTemp > this.currentWarmWaterMaxTemp) {
|
|
249
310
|
desiredWwPumpState = false;
|
|
250
|
-
reason = `Temperature of warm water pump ${wwTemp}°C is higher than the desired max value`;
|
|
311
|
+
reason = `Temperature of warm water pump ${wwTemp}°C is higher than the desired max value of ${this.currentWarmWaterMaxTemp}°C`;
|
|
251
312
|
}
|
|
252
313
|
else if (this._dachsOn) {
|
|
253
314
|
desiredWwPumpState = true;
|
|
@@ -298,6 +359,12 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
298
359
|
shouldDachsBeStarted(action, batteryLevel) {
|
|
299
360
|
var _a, _b, _c;
|
|
300
361
|
const dayType = services_1.TimeCallbackService.dayType(new models_1.SunTimeOffsets());
|
|
362
|
+
/**
|
|
363
|
+
* Set where the empty battery earns the release of the start block. Both the release and the block that
|
|
364
|
+
* may take its place are written further down, behind the heat driven branches - a decision taken before
|
|
365
|
+
* them could either swallow a heat driven start or leave the block standing while one happens.
|
|
366
|
+
*/
|
|
367
|
+
let emptyBatteryEarnsRelease = false;
|
|
301
368
|
if (this.blockDachsStart !== undefined) {
|
|
302
369
|
if (this.settings.disableDachsTemporarily) {
|
|
303
370
|
const blockAction = new command_1.ActuatorSetStateCommand(action, true, `Dachs is disabled temporarily`, null);
|
|
@@ -322,10 +389,12 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
322
389
|
return false;
|
|
323
390
|
}
|
|
324
391
|
else if (batteryLevel < this.settings.batteryLevelAllowStartThreshold) {
|
|
325
|
-
|
|
326
|
-
|
|
392
|
+
// The empty battery earns a release here, but nothing is written yet: whether the release happens
|
|
393
|
+
// or a block takes its place is decided behind the heat driven branches, so that neither outcome
|
|
394
|
+
// can reach around a heat driven start.
|
|
395
|
+
emptyBatteryEarnsRelease = true;
|
|
327
396
|
}
|
|
328
|
-
else if (utils_1.Utils.nowMS() - this._blockStarted >
|
|
397
|
+
else if (utils_1.Utils.nowMS() - this._blockStarted > 3 * 60 * 60 * 1000 &&
|
|
329
398
|
(((_a = settings_service_1.SettingsService.settings.heaterSettings) === null || _a === void 0 ? void 0 : _a.mode) === enums_1.HeatingMode.Winter ||
|
|
330
399
|
this.warmWaterDachsAlternativeActuator === undefined)) {
|
|
331
400
|
const liftAction = new command_1.ActuatorSetStateCommand(action, false, `Battery is at ${batteryLevel}%, but Dachs wasn't allowed to run for 3 hours, Dachs is now allowed to run if needed`, null);
|
|
@@ -342,17 +411,49 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
342
411
|
return false;
|
|
343
412
|
}
|
|
344
413
|
}
|
|
345
|
-
|
|
414
|
+
const alreadyRunning = this._dachsOn;
|
|
415
|
+
// The heat driven decision is taken but not returned yet, so that the block actuator below is written
|
|
416
|
+
// with it already known.
|
|
417
|
+
let heatDrivenStart = undefined;
|
|
418
|
+
if (!alreadyRunning) {
|
|
419
|
+
if (((_c = settings_service_1.SettingsService.settings.heaterSettings) === null || _c === void 0 ? void 0 : _c.mode) === enums_1.HeatingMode.Winter &&
|
|
420
|
+
this.heatStorageTempSensor.temperatureSensor.temperature < this.settings.winterMinimumHeatStorageTemp) {
|
|
421
|
+
// It is winter and heat storage is kinda cold --> Start
|
|
422
|
+
heatDrivenStart = true;
|
|
423
|
+
}
|
|
424
|
+
else if (this.heatStorageTempSensor.temperatureSensor.temperature > this.settings.heatStorageMaxStartTemp) {
|
|
425
|
+
// Heat Storage is already quite full, don't start
|
|
426
|
+
heatDrivenStart = false;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
// Everything above is heat driven and stays untouched; from here on the decision is electricity driven,
|
|
430
|
+
// which is the only branch the history gate speaks about. The gate is read - and the block actuator
|
|
431
|
+
// written - only at this point, so neither of its two directions can reach around a heat driven decision.
|
|
432
|
+
const gate = this._historyGateResult;
|
|
433
|
+
const gateSuppresses = heatDrivenStart === undefined && (gate === null || gate === void 0 ? void 0 : gate.suppress) === true;
|
|
434
|
+
if (emptyBatteryEarnsRelease && this.blockDachsStart !== undefined) {
|
|
435
|
+
if (gateSuppresses) {
|
|
436
|
+
const gateBlockAction = new command_1.ActuatorSetStateCommand(action, true, `History gate suppresses the start - ${gate === null || gate === void 0 ? void 0 : gate.reason}`, null);
|
|
437
|
+
gateBlockAction.overrideCommandSource = enums_1.CommandSource.Force;
|
|
438
|
+
this.blockDachsStart.setActuator(gateBlockAction);
|
|
439
|
+
this._blockStarted = utils_1.Utils.nowMS();
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
const liftAction = new command_1.ActuatorSetStateCommand(action, false, `Battery reached ${batteryLevel}%, Dachs is now allowed to run if needed`, null);
|
|
443
|
+
this.blockDachsStart.setActuator(liftAction);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (alreadyRunning) {
|
|
346
447
|
// We are already running
|
|
347
448
|
return false;
|
|
348
449
|
}
|
|
349
|
-
if (
|
|
350
|
-
|
|
351
|
-
|
|
450
|
+
if (heatDrivenStart !== undefined) {
|
|
451
|
+
return heatDrivenStart;
|
|
452
|
+
}
|
|
453
|
+
if ((gate === null || gate === void 0 ? void 0 : gate.request) === true) {
|
|
352
454
|
return true;
|
|
353
455
|
}
|
|
354
|
-
|
|
355
|
-
// Heat Storage is already quite full, don't start
|
|
456
|
+
if (gateSuppresses) {
|
|
356
457
|
return false;
|
|
357
458
|
}
|
|
358
459
|
if ((dayType === enums_1.TimeOfDay.Daylight || dayType === enums_1.TimeOfDay.BeforeSunrise) &&
|
|
@@ -366,6 +467,102 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
366
467
|
}
|
|
367
468
|
return !this.settings.blockAutomaticSettings;
|
|
368
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* What this unit makes of what the plant said about the coming morning.
|
|
472
|
+
*
|
|
473
|
+
* Its own question comes first and is the only one it answers itself: while the warm water sits below the
|
|
474
|
+
* minimum the unit is needed for heat, so energy does not decide and the plant's verdict is recorded without
|
|
475
|
+
* being acted on. Everything below that is translation - the plant judged whether the morning holds, this
|
|
476
|
+
* unit only says what it does about it.
|
|
477
|
+
*
|
|
478
|
+
* **A verdict the plant reached without its fitted model may move something, a modelled one may not.** The
|
|
479
|
+
* measured verdicts are arithmetic on the plant's own recorded consumption and carry no unmeasured
|
|
480
|
+
* assumption; the modelled one rests on a fit whose window length has never been checked against recorded
|
|
481
|
+
* data. Stage 4 therefore names which side of the reserve the band fell on and moves nothing - behaviourally
|
|
482
|
+
* the same as stage 5 and distinguishable from it only by the reason line. Whether the model beats the
|
|
483
|
+
* trivial rule is measured once, by the energy manager, over the plant's single shadow record.
|
|
484
|
+
*
|
|
485
|
+
* The stage numbers are this unit's log vocabulary rather than the plant's ladder: stage 1 is the warm water
|
|
486
|
+
* question nobody else has, stages 2 and 3 are the measured verdicts it acts on, stage 4 the modelled one it
|
|
487
|
+
* only reports, stage 5 no verdict at all.
|
|
488
|
+
* @returns The decision, or undefined while the heating mode is winter or the plant says nothing about the
|
|
489
|
+
* coming morning.
|
|
490
|
+
*/
|
|
491
|
+
evaluateHistoryGate() {
|
|
492
|
+
var _a, _b;
|
|
493
|
+
if (((_a = settings_service_1.SettingsService.settings.heaterSettings) === null || _a === void 0 ? void 0 : _a.mode) === enums_1.HeatingMode.Winter) {
|
|
494
|
+
// During winter operation the heat demand decides when the unit runs, so the gate stays out of it.
|
|
495
|
+
return undefined;
|
|
496
|
+
}
|
|
497
|
+
// An absent verdict covers the installation without an energy manager, the one whose battery reports no
|
|
498
|
+
// charge level and the one whose manager states no reserve to judge against. The unit does not tell them
|
|
499
|
+
// apart: in none of them is there a judgement to act on, and the stock ladder below then decides as it did
|
|
500
|
+
// before the gate existed. Not logged here either - the plant already says why it stays silent, and this
|
|
501
|
+
// runs on every battery and temperature update.
|
|
502
|
+
const verdict = (_b = devices_1.Devices.energymanager) === null || _b === void 0 ? void 0 : _b.morningReserveVerdict;
|
|
503
|
+
if (verdict === undefined) {
|
|
504
|
+
return undefined;
|
|
505
|
+
}
|
|
506
|
+
this._historyGateEvaluations++;
|
|
507
|
+
if (verdict.modelFitted) {
|
|
508
|
+
this._historyGateStatements++;
|
|
509
|
+
}
|
|
510
|
+
const warmWater = this.warmWaterSensor.temperatureSensor.temperature;
|
|
511
|
+
if (warmWater < this.settings.warmWaterDesiredMinTemp) {
|
|
512
|
+
// The band of a verdict that was not acted on would read as though it had been; the line collapses onto
|
|
513
|
+
// the charge level instead, which is what "energy did not decide this" looks like in the log.
|
|
514
|
+
return Dachs.gateResult(1, false, false, `warm water ${warmWater}°C is below the minimum ${this.settings.warmWaterDesiredMinTemp}°C`, verdict, verdict.currentSoc, verdict.currentSoc);
|
|
515
|
+
}
|
|
516
|
+
if (verdict.holds === undefined) {
|
|
517
|
+
return Dachs.gateResult(5, false, false, verdict.reason, verdict, verdict.band.lower, verdict.band.upper);
|
|
518
|
+
}
|
|
519
|
+
if (!verdict.measured) {
|
|
520
|
+
return Dachs.gateResult(4, false, false, `${verdict.reason}, which this unit does not act on`, verdict, verdict.band.lower, verdict.band.upper);
|
|
521
|
+
}
|
|
522
|
+
return verdict.holds
|
|
523
|
+
? Dachs.gateResult(2, true, false, verdict.reason, verdict, verdict.band.lower, verdict.band.upper)
|
|
524
|
+
: Dachs.gateResult(3, false, true, verdict.reason, verdict, verdict.band.lower, verdict.band.upper);
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Assembles a gate result and the reason line the operator reads it by.
|
|
528
|
+
* @param stage - The stage of this unit's own vocabulary that decided.
|
|
529
|
+
* @param suppress - Whether the electricity driven start is suppressed.
|
|
530
|
+
* @param request - Whether a start is asked for.
|
|
531
|
+
* @param detail - What the plant said, or what this unit answered instead.
|
|
532
|
+
* @param verdict - The plant's verdict, for the numbers the line is read by.
|
|
533
|
+
* @param lowerEdgeSoc - The projected morning charge level at the lower edge.
|
|
534
|
+
* @param upperEdgeSoc - The projected morning charge level at the upper edge.
|
|
535
|
+
* @returns The decision.
|
|
536
|
+
*/
|
|
537
|
+
static gateResult(stage, suppress, request, detail, verdict, lowerEdgeSoc, upperEdgeSoc) {
|
|
538
|
+
return {
|
|
539
|
+
suppress,
|
|
540
|
+
request,
|
|
541
|
+
reason: `Stage ${stage}: ${detail} (soc ${utils_1.Utils.round(verdict.currentSoc, 2)}%, band ` +
|
|
542
|
+
`${utils_1.Utils.round(lowerEdgeSoc, 2)}%..${utils_1.Utils.round(upperEdgeSoc, 2)}%, reserve ${verdict.reserve}%, ` +
|
|
543
|
+
`${verdict.sampleDays} days)`,
|
|
544
|
+
currentSoc: verdict.currentSoc,
|
|
545
|
+
lowerEdgeSoc,
|
|
546
|
+
upperEdgeSoc,
|
|
547
|
+
reserve: verdict.reserve,
|
|
548
|
+
sampleDays: verdict.sampleDays,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Writes the gate decision to the log - on info whenever the decision changed, on debug otherwise, as the
|
|
553
|
+
* evaluation runs on every battery and temperature update.
|
|
554
|
+
* @param result - The decision to log, if there is one.
|
|
555
|
+
*/
|
|
556
|
+
logHistoryGate(result) {
|
|
557
|
+
if (result === undefined) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const decision = `${result.suppress}/${result.request}`;
|
|
561
|
+
const changed = decision !== this._lastGateDecision;
|
|
562
|
+
this._lastGateDecision = decision;
|
|
563
|
+
this.log(changed ? enums_1.LogLevel.Info : enums_1.LogLevel.Debug, `History gate: ${result.reason}; a statement was possible in ${this._historyGateStatements} of ` +
|
|
564
|
+
`${this._historyGateEvaluations} evaluations`);
|
|
565
|
+
}
|
|
369
566
|
checkAlternativeActuator(shouldDachsBeStarted, action) {
|
|
370
567
|
var _a, _b;
|
|
371
568
|
if (!this.warmWaterDachsAlternativeActuator) {
|
|
@@ -397,8 +594,7 @@ class Dachs extends RoomBaseDevice_1.RoomBaseDevice {
|
|
|
397
594
|
this.warmWaterDachsAlternativeActuator.setActuator(command);
|
|
398
595
|
}
|
|
399
596
|
onDachsRunningStateChange(runStateChange) {
|
|
400
|
-
|
|
401
|
-
this.checkAllDesiredStates(runStateChange, (_b = (_a = devices_1.Devices.energymanager) === null || _a === void 0 ? void 0 : _a.batteryLevel) !== null && _b !== void 0 ? _b : 0);
|
|
597
|
+
this.checkAllDesiredStates(runStateChange, Dachs.currentStateOfCharge);
|
|
402
598
|
}
|
|
403
599
|
}
|
|
404
600
|
exports.Dachs = Dachs;
|
|
@@ -58,6 +58,13 @@ class DeviceCluster {
|
|
|
58
58
|
clusterTypes.push(enums_1.DeviceClusterType.TemperaturSensor);
|
|
59
59
|
clusterTypes.push(enums_1.DeviceClusterType.HumiditySensor);
|
|
60
60
|
break;
|
|
61
|
+
case enums_1.DeviceType.ZigbeeCooloSoilSensor:
|
|
62
|
+
// Counts towards the room aggregates as well: this sensor sits in a pot indoors, so its air readings
|
|
63
|
+
// are readings of the room air. A sensor in a bed or a lawn would only get the soil cluster.
|
|
64
|
+
clusterTypes.push(enums_1.DeviceClusterType.SoilSensor);
|
|
65
|
+
clusterTypes.push(enums_1.DeviceClusterType.TemperaturSensor);
|
|
66
|
+
clusterTypes.push(enums_1.DeviceClusterType.HumiditySensor);
|
|
67
|
+
break;
|
|
61
68
|
case enums_1.DeviceType.HmIpGriff:
|
|
62
69
|
clusterTypes.push(enums_1.DeviceClusterType.Handle);
|
|
63
70
|
break;
|
package/lib/devices/devices.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { iBaseDevice, iConfig, iDeviceConfig, iEnergyManager, iRoomImportEnforcer, iTemperatureCollector } from '../interfaces';
|
|
1
|
+
import { iBaseDevice, iConfig, iDeviceConfig, iEnergyManager, iFossilGeneratorSource, iRoomImportEnforcer, iTemperatureCollector } from '../interfaces';
|
|
2
2
|
import { Dachs } from './dachs';
|
|
3
3
|
import { UnifiAccess, UnifiProtect } from './unifi';
|
|
4
4
|
export declare class Devices {
|
|
@@ -67,9 +67,44 @@ export declare class Devices {
|
|
|
67
67
|
* A reference to the Unifi Access device
|
|
68
68
|
*/
|
|
69
69
|
static unifiAccess?: UnifiAccess;
|
|
70
|
+
/**
|
|
71
|
+
* The devices already reported as announcing a fuel burning generator without stating the fields of one,
|
|
72
|
+
* so the line is written on change instead of on every read of {@link fossilGenerators}.
|
|
73
|
+
*/
|
|
74
|
+
private static readonly _incompleteGeneratorsReported;
|
|
70
75
|
constructor(pDeviceData: {
|
|
71
76
|
[id: string]: iDeviceConfig;
|
|
72
77
|
}, pRoomImportEnforcer?: iRoomImportEnforcer, config?: iConfig);
|
|
78
|
+
/**
|
|
79
|
+
* The fuel burning generators of the plant - every device that announced itself as one by carrying
|
|
80
|
+
* {@link DeviceCapability.fossilGenerator}.
|
|
81
|
+
*
|
|
82
|
+
* Which generators a plant has is a property of the plant, not of whoever asks about it, so it is answered
|
|
83
|
+
* here rather than handed to a service by one of its callers. Reading the device list instead of keeping a
|
|
84
|
+
* list of registrations is what makes it independent of construction order: devices are built in the order
|
|
85
|
+
* the installation's configuration lists them, and a generator built before the reader would have to hope
|
|
86
|
+
* the reader existed already.
|
|
87
|
+
*
|
|
88
|
+
* Read anew on every use for the same reason - a generator added later is simply found the next time, and
|
|
89
|
+
* the two numbers of each entry are getters onto settings that are editable at runtime.
|
|
90
|
+
*
|
|
91
|
+
* The capability announces the intent, the three fields are what the correction actually works with, and
|
|
92
|
+
* both are checked. Cast into the role unchecked, a device that carries the capability without the fields
|
|
93
|
+
* hands over `undefined`, the correction turns into `NaN` and its entry is dropped without a word - the
|
|
94
|
+
* day is then under corrected, the photovoltaic looks better than it was, and the gate suppresses a start
|
|
95
|
+
* the house needed. That is the direction without a way back, so such a device is left out and named.
|
|
96
|
+
* @returns One entry per usable generator; empty while the plant has none.
|
|
97
|
+
*/
|
|
98
|
+
static get fossilGenerators(): iFossilGeneratorSource[];
|
|
99
|
+
/**
|
|
100
|
+
* Names a device that announces itself as a fuel burning generator without stating what one has to state.
|
|
101
|
+
*
|
|
102
|
+
* Said once per device rather than once per read: this list is read on every history load, and a line per
|
|
103
|
+
* read would bury the change. The note is dropped again as soon as the device answers completely, so a
|
|
104
|
+
* field that only arrives later is not reported forever.
|
|
105
|
+
* @param key - The key the device is listed under.
|
|
106
|
+
*/
|
|
107
|
+
private static reportIncompleteGenerator;
|
|
73
108
|
static midnightReset(): void;
|
|
74
109
|
static resetDetectionsToday(): void;
|
|
75
110
|
static getBatteryInfo(): string;
|
package/lib/devices/devices.js
CHANGED
|
@@ -61,6 +61,65 @@ class Devices {
|
|
|
61
61
|
shelly_1.ShellyDevice.checkMissing();
|
|
62
62
|
tuya_1.TuyaDevice.checkMissing();
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* The fuel burning generators of the plant - every device that announced itself as one by carrying
|
|
66
|
+
* {@link DeviceCapability.fossilGenerator}.
|
|
67
|
+
*
|
|
68
|
+
* Which generators a plant has is a property of the plant, not of whoever asks about it, so it is answered
|
|
69
|
+
* here rather than handed to a service by one of its callers. Reading the device list instead of keeping a
|
|
70
|
+
* list of registrations is what makes it independent of construction order: devices are built in the order
|
|
71
|
+
* the installation's configuration lists them, and a generator built before the reader would have to hope
|
|
72
|
+
* the reader existed already.
|
|
73
|
+
*
|
|
74
|
+
* Read anew on every use for the same reason - a generator added later is simply found the next time, and
|
|
75
|
+
* the two numbers of each entry are getters onto settings that are editable at runtime.
|
|
76
|
+
*
|
|
77
|
+
* The capability announces the intent, the three fields are what the correction actually works with, and
|
|
78
|
+
* both are checked. Cast into the role unchecked, a device that carries the capability without the fields
|
|
79
|
+
* hands over `undefined`, the correction turns into `NaN` and its entry is dropped without a word - the
|
|
80
|
+
* day is then under corrected, the photovoltaic looks better than it was, and the gate suppresses a start
|
|
81
|
+
* the house needed. That is the direction without a way back, so such a device is left out and named.
|
|
82
|
+
* @returns One entry per usable generator; empty while the plant has none.
|
|
83
|
+
*/
|
|
84
|
+
static get fossilGenerators() {
|
|
85
|
+
const generators = [];
|
|
86
|
+
for (const key in Devices.alLDevices) {
|
|
87
|
+
const device = Devices.alLDevices[key];
|
|
88
|
+
if (!device.deviceCapabilities.includes(enums_1.DeviceCapability.fossilGenerator)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const candidate = device;
|
|
92
|
+
// Finiteness rather than a plausible range: a negative or zero rating over corrects, which only makes
|
|
93
|
+
// the photovoltaic look worse, while an absent one poisons the whole sum of the day.
|
|
94
|
+
if (typeof candidate.actuatorId !== 'string' ||
|
|
95
|
+
candidate.actuatorId === '' ||
|
|
96
|
+
!Number.isFinite(candidate.ratedElectricalWattage) ||
|
|
97
|
+
!Number.isFinite(candidate.conversionFactor)) {
|
|
98
|
+
Devices.reportIncompleteGenerator(key);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
Devices._incompleteGeneratorsReported.delete(key);
|
|
102
|
+
generators.push(candidate);
|
|
103
|
+
}
|
|
104
|
+
return generators;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Names a device that announces itself as a fuel burning generator without stating what one has to state.
|
|
108
|
+
*
|
|
109
|
+
* Said once per device rather than once per read: this list is read on every history load, and a line per
|
|
110
|
+
* read would bury the change. The note is dropped again as soon as the device answers completely, so a
|
|
111
|
+
* field that only arrives later is not reported forever.
|
|
112
|
+
* @param key - The key the device is listed under.
|
|
113
|
+
*/
|
|
114
|
+
static reportIncompleteGenerator(key) {
|
|
115
|
+
if (Devices._incompleteGeneratorsReported.has(key)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
Devices._incompleteGeneratorsReported.add(key);
|
|
119
|
+
logging_1.ServerLogService.writeLog(enums_1.LogLevel.Error, `Device '${key}' announces itself as a fuel burning generator but states no usable actuator id, rated ` +
|
|
120
|
+
'wattage and conversion factor, so nothing is subtracted for it and the photovoltaic looks better ' +
|
|
121
|
+
'than it was');
|
|
122
|
+
}
|
|
64
123
|
static midnightReset() {
|
|
65
124
|
// Nothing yet
|
|
66
125
|
}
|
|
@@ -170,6 +229,9 @@ class Devices {
|
|
|
170
229
|
case 'BlitzShp':
|
|
171
230
|
d = new zigbee_1.ZigbeeBlitzShp(zigbeeInfo);
|
|
172
231
|
break;
|
|
232
|
+
case 'CooloSoil':
|
|
233
|
+
d = new zigbee_1.ZigbeeCooloSoilSensor(zigbeeInfo);
|
|
234
|
+
break;
|
|
173
235
|
case 'HeimanSmoke':
|
|
174
236
|
d = new zigbee_1.ZigbeeHeimanSmoke(zigbeeInfo);
|
|
175
237
|
break;
|
|
@@ -375,3 +437,8 @@ Devices.dachs = undefined;
|
|
|
375
437
|
* @default undefined (no warm water temperature sensor)
|
|
376
438
|
*/
|
|
377
439
|
Devices.temperatureWarmWater = undefined;
|
|
440
|
+
/**
|
|
441
|
+
* The devices already reported as announcing a fuel burning generator without stating the fields of one,
|
|
442
|
+
* so the line is written on change instead of on every read of {@link fossilGenerators}.
|
|
443
|
+
*/
|
|
444
|
+
Devices._incompleteGeneratorsReported = new Set();
|