Shelly Logo Squared

Script Shelly scripts

Track your peak demand on a demand tariff, with a daily report

Measures your home's demand in each half-hour of your tariff's peak window and sends you the day's highest through ntfy, with the highest of the last 30 days.

Updated v1.0

  • Gen3
  • Gen4
  • Pro
peak-demand-tracker.js · 120 lines Download .js
// Peak demand tracker: measures your home's demand in each half-hour of your// tariff's demand window and tells your phone through ntfy each day's peak. Needs a Shelly energy meter. let CONFIG = {  fromTime: "15:00",  toTime: "21:00",  weekdaysOnly: true,  blockMin: 30,  ntfyTopic: "",  meterId: 0,}; // Check the clock every 20 seconds, so each block starts within the minute.let CHECK_EVERY_SEC = 20;// Days of daily peaks to keep, for the highest-in-a-month figure.let HISTORY_DAYS = 30;let KVS_KEY = "peak-demand"; let lastBoundary = "";let blockStartWh = null;let blockStartTime = "";let blockCounts = false;let dayPeak = null;let history = []; function localNow() {  let s = Shelly.getComponentStatus("sys");  if (s === null || typeof s.unixtime !== "number" || s.time === null) return null;  return { date: Math.floor((s.unixtime + (s.utc_offset || 0)) / 86400), time: s.time };} function minutesOf(t) {  return Number(t.slice(0, 2)) * 60 + Number(t.slice(3, 5));} // 1 January 1970 was a Thursday, so day 0 is a Thursday.function isWeekend(date) {  let dow = (date + 4) % 7;  return dow === 0 || dow === 6;} function counts(now) {  if (CONFIG.weekdaysOnly && isWeekend(now.date)) return false;  return now.time >= CONFIG.fromTime && now.time < CONFIG.toTime;} function readWh() {  let d = Shelly.getComponentStatus("em1data", CONFIG.meterId);  if (d === null || typeof d.total_act_energy !== "number") return null;  return d.total_act_energy;} function onSent(res, err) {  if (err !== 0 || res.code !== 200) print("Couldn't send the report to ntfy.");} function send(msg) {  print(msg);  if (CONFIG.ntfyTopic === "") return;  Shelly.call("HTTP.Request", {    method: "POST",    url: "https://ntfy.sh/" + CONFIG.ntfyTopic,    body: msg,    headers: { "Content-Type": "text/plain" },    timeout: 15,  }, onSent);} function report(date) {  history.push({ d: date, kw: dayPeak.kw });  let kept = [];  let top = 0;  for (let i = 0; i < history.length; i++) {    if (history[i].d > date - HISTORY_DAYS) {      kept.push(history[i]);      if (history[i].kw > top) top = history[i].kw;    }  }  history = kept;  Shelly.call("KVS.Set", { key: KVS_KEY, value: JSON.stringify(history) });  send("Today's peak demand was " + dayPeak.kw.toFixed(2) + " kW, from " + dayPeak.from +    " to " + dayPeak.to + ". Highest in the last " + HISTORY_DAYS + " days: " + top.toFixed(2) + " kW.");  dayPeak = null;} function check() {  let now = localNow();  // Without the clock it can't tell where the window is, so it does nothing.  if (now === null) return;  if (minutesOf(now.time) % CONFIG.blockMin !== 0 || now.time === lastBoundary) return;  lastBoundary = now.time;  let wh = readWh();  // The block that just ended: average power over it is its demand.  if (blockCounts && blockStartWh !== null && wh !== null) {    let kw = (wh - blockStartWh) / 1000 * 60 / CONFIG.blockMin;    print("Demand " + blockStartTime + " to " + now.time + ": " + kw.toFixed(2) + " kW.");    if (dayPeak === null || kw > dayPeak.kw) dayPeak = { kw: kw, from: blockStartTime, to: now.time };  }  let next = counts(now);  // The window has just closed: send the day's peak.  if (!next && dayPeak !== null) report(now.date);  blockStartWh = wh;  blockStartTime = now.time;  blockCounts = next;} function onLoaded(res, err) {  if (err === 0 && res && typeof res.value === "string") {    try {      history = JSON.parse(res.value);    } catch (e) {      print("Couldn't read the saved peaks. Starting a new list.");    }  }  Timer.set(CHECK_EVERY_SEC * 1000, true, check);  print("Peak demand tracker started. Window " + CONFIG.fromTime + " to " + CONFIG.toTime +    (CONFIG.weekdaysOnly ? " on weekdays." : " every day."));} Shelly.call("KVS.Get", { key: KVS_KEY }, onLoaded);

What it does

A demand tariff charges for the highest demand you reach in a set window of the day, measured in blocks of time. Your tariff sets the window and the block length. The script reads the meter's energy counter at the start and end of each block in your window and works out the average power over it, in kilowatts. When the window closes, your phone gets the day's highest block, when it was, and the highest of the last 30 days. Seeing that the oven and the dryer landed in the same half-hour is how you learn to spread them out.

The first block after the script starts isn't counted, because it only saw part of it. Weekends are skipped if you've set weekdaysOnly, but public holidays aren't known to the script. The meter's counter moves once a minute, so a block can be off by up to a minute's use. The last 30 days are saved on the Shelly, so a restart keeps them. Until the Shelly's clock has synced, it doesn't read anything.

Install it on your Shelly

View the full install guide

Before you install

  1. Check the meter and its clock

    Once the meter is installed, check it shows power in the Shelly app and that the device's time zone is your own. The blocks start on the hour and the half-hour on the Shelly's clock.

  2. Find your demand window

    Your bill or your retailer's tariff page shows the hours your demand charge applies, and whether it's weekdays only.

  3. Subscribe in ntfy

    Install the ntfy app and subscribe to a topic name nobody would guess, such as smith-demand-4821.

Install the script

Install it on the meter.

Shelly Smart Control

  1. Open the device and tap Scripts

    In Shelly Smart Control, open the room, then the device. Tap Scripts, the { } icon in the menu down the left.

  2. Tap Create new script

    If the device has no scripts yet, the screen says No scripts created.

  3. Name it, paste the code, then tap Save

    Name it peak-demand-tracker. Tap in the code area, paste the script you copied above, then tap Save (the disk icon at the top right).

  4. Tap the play button

    The play button lights up once the script is saved. Tap it and a message says the script has been started.

  5. Close the editor and turn on Run on startup

    Back on the Scripts screen, turn on Run on startup next to your script, so it starts again after a power cut or a firmware update.

Direct access via IP

  1. Open the device's web page and click Scripts

    Find the IP address in the Shelly app under Device settings, then Device information, and type it into a browser on the same Wi-Fi. Click Scripts in the menu on the left.

  2. Click Create script

    If the device has no scripts yet, the list says so.

  3. Name it, paste the code, then click Save

    Name it peak-demand-tracker. Click in the code area, paste the script you copied above, then click Save (the disk icon above the code).

  4. Click Run and check the console

    Run appears once the script is saved. The console under the code then prints what the script is doing.

  5. Turn on Run on startup

    Click Scripts again and turn on Run on startup next to your script, so it starts again after a power cut or a firmware update.

After you install

  1. Check the console

    During the window, the console prints each block's demand in kW as it ends. At the end of the window your phone gets the day's peak.

Changelog

v1.0 First release.

Still need a hand?

We answer most messages within one business day. If your question is about an existing order, have your order number ready. It speeds things up.

Contact us