node-red-contrib-power-saver 5.2.0 → 5.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.
@@ -0,0 +1,16 @@
1
+ const js = require("@eslint/js");
2
+ const globals = require("globals");
3
+
4
+ module.exports = [
5
+ js.configs.recommended,
6
+ {
7
+ languageOptions: {
8
+ ecmaVersion: "latest",
9
+ globals: {
10
+ ...globals.browser,
11
+ ...globals.commonjs,
12
+ ...globals.es2021,
13
+ },
14
+ },
15
+ },
16
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-power-saver",
3
- "version": "5.2.0",
3
+ "version": "5.2.1",
4
4
  "description": "A module for Node-RED that you can use to turn on and off a switch based on power prices",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -48,6 +48,7 @@
48
48
  "url": "https://github.com/ottopaulsen/node-red-contrib-power-saver.git"
49
49
  },
50
50
  "devDependencies": {
51
+ "@eslint/js": "^10.0.1",
51
52
  "@vuepress/bundler-vite": "2.0.0-rc.26",
52
53
  "@vuepress/plugin-google-analytics": "2.0.0-rc.123",
53
54
  "@vuepress/plugin-register-components": "2.0.0-rc.123",
@@ -57,6 +58,7 @@
57
58
  "chai": "6.2.2",
58
59
  "eslint": "10.0.0",
59
60
  "expect": "30.2.0",
61
+ "globals": "^17.4.0",
60
62
  "mocha": "^11.7.5",
61
63
  "node-red": "^4.1.5",
62
64
  "node-red-node-test-helper": "0.3.6",
@@ -1,4 +1,3 @@
1
- const { DateTime } = require("luxon");
2
1
  const { roundPrice } = require("./utils");
3
2
 
4
3
  // Build all periods that are different.
@@ -86,7 +86,7 @@ function makePlanFromPriceData(node, msg, config, doPlanning, calcSavings) {
86
86
  const schedule = trimScheduleToStart(fullSchedule, priceData[0].start);
87
87
  addLastSwitchIfNoSchedule(schedule, minutes, config);
88
88
 
89
- plan = {
89
+ const plan = {
90
90
  minutes,
91
91
  schedule,
92
92
  source,
@@ -174,7 +174,7 @@ function trimScheduleToStart(schedule, startTime) {
174
174
 
175
175
  function deleteSavedScheduleBefore(node, day, checkDays = 0) {
176
176
  let date = day;
177
- let data = null;
177
+ let data;
178
178
  let count = 0;
179
179
  do {
180
180
  date = date.plus({ days: -1 });
@@ -73,7 +73,6 @@ function runSchedule(node, schedule, time, currentSent = false) {
73
73
  const entry = remainingSchedule[0];
74
74
  const nextTime = DateTime.fromISO(entry.time);
75
75
  const wait = nextTime - time;
76
- const onOff = entry.value ? "on" : "off";
77
76
  const statusMessage = `${remainingSchedule.length} changes - ${
78
77
  remainingSchedule[0].value ? "on" : "off"
79
78
  } at ${nextTime.toFormat("HH:mm")}`;
@@ -249,7 +249,6 @@ function handleStateChange(event, config, state, node, homeAssistant, clock = nu
249
249
  // Check if it's the brightness sensor
250
250
  if (config.brightnessSensor && config.brightnessSensor.entity_id === entityId) {
251
251
  const wasBrightnessAllowing = isBrightnessAllowingLights(config); // Check before update
252
- const oldBrightness = config.brightnessSensor.state;
253
252
  config.brightnessSensor.lastChanged = timestamp;
254
253
  config.brightnessSensor.state = newState.state;
255
254
 
@@ -715,14 +714,16 @@ function fetchMissingStates(config, state, node, homeAssistant, clock = null) {
715
714
  `Initial timedOut set to ${state.timedOut} (all triggers actually timed out: ${allTimedOut})`,
716
715
  );
717
716
 
718
- // If motion is detected at startup (timedOut is false), turn lights on
719
717
  if (!allTimedOut) {
720
- debugLog(config, node, "Motion detected at startup, turning lights on");
721
- const level = findCurrentLevel(config, node, clock);
722
- if (level !== null && isBrightnessAllowingLights(config)) {
723
- controlLights(config, config.lights, level, node, homeAssistant);
724
- debugLog(config, node, `Lights turned on to ${level}% at startup (motion detected)`);
725
- }
718
+ const levelConfig = findLevelConfig(config, clock);
719
+ state.lastImmediateTime = levelConfig && levelConfig.immediate === true ? levelConfig.fromTime : null;
720
+ debugLog(
721
+ config,
722
+ node,
723
+ `Startup preserved current light state while motion is still active${state.lastImmediateTime ? ` (immediate period ${state.lastImmediateTime} already active)` : ""}`,
724
+ );
725
+ } else {
726
+ state.lastImmediateTime = null;
726
727
  }
727
728
 
728
729
  return true; // Indicates that initial timedOut was set
@@ -715,7 +715,7 @@
715
715
 
716
716
  // Limit input (without spinner arrows)
717
717
  const limitInput = $(
718
- '<input type="number" id="node-input-brightnessLimit" class="brightness-limit-input" min="0" max="100000" step="1"/>',
718
+ '<input type="number" id="node-input-brightnessLimit" class="brightness-limit-input" max="100000" step="1"/>',
719
719
  ).css({
720
720
  width: "60px",
721
721
  padding: "3px",
@@ -22,7 +22,7 @@ module.exports = function (RED) {
22
22
  let runtimeOverride = null;
23
23
  try {
24
24
  runtimeOverride = node.context().get("override", node.contextStorage);
25
- } catch (err) {
25
+ } catch {
26
26
  // Context storage might not be available (e.g., in tests)
27
27
  }
28
28
 
@@ -89,7 +89,7 @@ module.exports = function (RED) {
89
89
  try {
90
90
  node.context().set("override", nodeConfig.override, node.contextStorage);
91
91
  debugLog(`Override saved to context storage: ${nodeConfig.override}`);
92
- } catch (err) {
92
+ } catch {
93
93
  // Silently fail if context storage not available
94
94
  }
95
95
  };
@@ -298,7 +298,7 @@ module.exports = function (RED) {
298
298
 
299
299
  // Function to fetch current states from Home Assistant
300
300
  const fetchMissingStates = function () {
301
- const initialTimedOutWasSet = funcs.fetchMissingStates(nodeConfig, state, nodeWrapper, homeAssistant);
301
+ funcs.fetchMissingStates(nodeConfig, state, nodeWrapper, homeAssistant);
302
302
 
303
303
  // Fetch initial light states
304
304
  try {
@@ -1,5 +1,4 @@
1
1
  const { addEndToLast, validationFailure } = require("./utils");
2
- const { DateTime } = require("luxon");
3
2
 
4
3
  function getPriceData(node, msg) {
5
4
  const isConfigMsg = !!msg?.payload?.config;
@@ -12,7 +11,7 @@ function getPriceData(node, msg) {
12
11
  return null;
13
12
  }
14
13
 
15
- priceData = [...input.today, ...input.tomorrow];
14
+ const priceData = [...input.today, ...input.tomorrow];
16
15
 
17
16
  addEndToLast(priceData);
18
17
 
@@ -7,7 +7,7 @@ function msgHasSchedule(msg) {
7
7
  return msg.payload.minutes?.length > 0;
8
8
  }
9
9
 
10
- function validateSchedule(msg) {
10
+ function validateSchedule() {
11
11
  return "";
12
12
  }
13
13
 
@@ -119,7 +119,7 @@ function calculate(
119
119
  }
120
120
 
121
121
  savingsList.sort((b, a) => (b.saving === a.saving ? a.count - b.count : b.saving - a.saving));
122
- let onOff = values.map((v) => true); // Start with all on
122
+ let onOff = values.map(() => true); // Start with all on
123
123
 
124
124
  // Find the best possible sequences
125
125
  while (savingsList.length > 0) {
@@ -3,7 +3,6 @@ const {
3
3
  calcNullSavings,
4
4
  fixOutputValues,
5
5
  fixPeriods,
6
- getSavings,
7
6
  saveOriginalConfig,
8
7
  } = require("./utils");
9
8
  const { strategyOnInput } = require("./strategy-functions");
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  const { DateTime } = require("luxon");
3
- const { roundPrice, getDiffToNextOn } = require("./utils");
3
+ const { roundPrice } = require("./utils");
4
4
 
5
5
  function buildMinutePriceVector(priceData) {
6
6
  if (!priceData || priceData.length === 0) {
@@ -27,21 +27,21 @@ module.exports = function (RED) {
27
27
 
28
28
  this.on("input", function (msg) {
29
29
  if (!validateInput(node, msg)) return;
30
- if (!msg.hasOwnProperty("payload")) return;
30
+ if (!("payload" in msg)) return;
31
31
 
32
- if (msg.payload.hasOwnProperty("commands")) {
32
+ if ("commands" in msg.payload) {
33
33
  //Commands override input
34
- if (node.hasOwnProperty("schedule")) {
34
+ if ("schedule" in node) {
35
35
  //Do not execute if schedule is missing
36
- if (msg.payload.hasOwnProperty("time")) {
36
+ if ("time" in msg.payload) {
37
37
  node.dT = findTemp(msg.payload.time, node.schedule);
38
38
  } else {
39
39
  node.dT = findTemp(DateTime.now(), node.schedule);
40
40
  }
41
41
  node.T = node.setpoint + node.dT;
42
- if (msg.payload.commands.hasOwnProperty("sendSchedule")) {
42
+ if ("sendSchedule" in msg.payload.commands) {
43
43
  // Send output if schedule exists
44
- if (node.hasOwnProperty("schedule") && msg.payload.commands.sendSchedule == true) {
44
+ if ("schedule" in node && msg.payload.commands.sendSchedule == true) {
45
45
  node.send([
46
46
  null,
47
47
  null,
@@ -50,7 +50,7 @@ module.exports = function (RED) {
50
50
  ]);
51
51
  }
52
52
  }
53
- if (msg.payload.commands.hasOwnProperty("sendOutput") && msg.payload.commands.sendOutput == true) {
53
+ if ("sendOutput" in msg.payload.commands && msg.payload.commands.sendOutput == true) {
54
54
  // Send output if schedule exists
55
55
  node.send([
56
56
  { payload: node.T, topic: "setpoint", time: node.schedule.time, version: version },
@@ -63,22 +63,22 @@ module.exports = function (RED) {
63
63
  return;
64
64
  }
65
65
  // Using msg.payload.config to change specific properties
66
- if (msg.payload.hasOwnProperty("config")) {
67
- if (msg.payload.config.hasOwnProperty("timeHeat1C")) node.timeHeat1C = Number(msg.payload.config.timeHeat1C);
68
- if (msg.payload.config.hasOwnProperty("timeCool1C")) node.timeCool1C = Number(msg.payload.config.timeCool1C);
69
- if (msg.payload.config.hasOwnProperty("setpoint")) node.setpoint = Number(msg.payload.config.setpoint);
70
- if (msg.payload.config.hasOwnProperty("maxTempAdjustment"))
66
+ if ("config" in msg.payload) {
67
+ if ("timeHeat1C" in msg.payload.config) node.timeHeat1C = Number(msg.payload.config.timeHeat1C);
68
+ if ("timeCool1C" in msg.payload.config) node.timeCool1C = Number(msg.payload.config.timeCool1C);
69
+ if ("setpoint" in msg.payload.config) node.setpoint = Number(msg.payload.config.setpoint);
70
+ if ("maxTempAdjustment" in msg.payload.config)
71
71
  node.maxTempAdjustment = Number(msg.payload.config.maxTempAdjustment);
72
- if (msg.payload.config.hasOwnProperty("boostTempHeat"))
72
+ if ("boostTempHeat" in msg.payload.config)
73
73
  node.boostTempHeat = Number(msg.payload.config.boostTempHeat);
74
- if (msg.payload.config.hasOwnProperty("boostTempCool"))
74
+ if ("boostTempCool" in msg.payload.config)
75
75
  node.boostTempCool = Number(msg.payload.config.boostTempCool);
76
- if (msg.payload.config.hasOwnProperty("minSavings")) node.minSavings = Number(msg.payload.config.minSavings);
76
+ if ("minSavings" in msg.payload.config) node.minSavings = Number(msg.payload.config.minSavings);
77
77
  }
78
78
 
79
79
  //merge pricedata to escape some midnight issues. Store max 72 hour history
80
- if (msg.payload.hasOwnProperty("priceData")) {
81
- if (node.hasOwnProperty("priceData")) {
80
+ if ("priceData" in msg.payload) {
81
+ if ("priceData" in node) {
82
82
  node.priceData = mergePriceData(node.priceData, msg.payload.priceData);
83
83
  } else {
84
84
  node.priceData = msg.payload.priceData;
@@ -90,7 +90,7 @@ module.exports = function (RED) {
90
90
  }
91
91
  }
92
92
 
93
- if (node.hasOwnProperty("priceData")) {
93
+ if ("priceData" in node) {
94
94
  node.schedule = runBuySellAlgorithm(
95
95
  node.priceData,
96
96
  node.timeHeat1C,
@@ -102,7 +102,7 @@ module.exports = function (RED) {
102
102
  node.minSavings,
103
103
  );
104
104
 
105
- if (msg.payload.hasOwnProperty("time")) {
105
+ if ("time" in msg.payload) {
106
106
  node.dT = findTemp(msg.payload.time, node.schedule);
107
107
  } else {
108
108
  node.dT = findTemp(DateTime.now(), node.schedule);
package/src/utils.js CHANGED
@@ -5,7 +5,7 @@ function booleanConfig(value) {
5
5
  return value === "true" || value === true;
6
6
  }
7
7
 
8
- function calcNullSavings(values, _) {
8
+ function calcNullSavings(values) {
9
9
  return values.map(() => null);
10
10
  }
11
11
 
@@ -322,7 +322,7 @@ function fillArray(value, count) {
322
322
  if (value === undefined || count <= 0) {
323
323
  return [];
324
324
  }
325
- res = [];
325
+ let res = [];
326
326
  for (let i = 0; i < count; i++) {
327
327
  res.push(value);
328
328
  }