[APP][Pro] Sensor Watchdog – looking for testers

APP: Sensor Watchdog – know when a sensor goes quiet

A sensor can stop reporting without anything looking wrong. The device still shows as available, the battery reads 80 %, and your Flows quietly stop working. You find out days later, when something doesn’t happen. I built Sensor Watchdog after one of my own sensors had been dead for week without me noticing. It started as a HomeyScript, and is now an app.

Now I’m looking for a handful of testers. What helps most is the anonymised report the app generates under Settings — copy it, save it as a `.json`, and send me the file or post it here. The report contains no device or zone names or other sensitive data.

[Install the test version]( Sensor Watchdog | Homey )

## What makes it different

It checks each capability separately. Most approaches take the newest timestamp across all of a device’s capabilities. A device whose temperature froze three weeks ago, but whose battery still reports, looks perfectly healthy that way. That exact failure is what made me start.

Silence from an event-driven sensor is not a failure. A contact sensor that hasn’t been opened in two months isn’t broken, and a smoke alarm that hasn’t gone off in a year is doing its job. Only capabilities that update on a rhythm — temperature, power — can raise an alarm for being quiet. Devices with no rhythm at all are marked *cannot be verified* rather than reported as dead.

One alert when an integration goes down. Fifteen devices from the same app going quiet at once is one problem, not fifteen — so you get one notification instead of a tour of the house replacing batteries that are fine.

It learns the reporting rhythm. Instead of asking you to guess a timeout per device, it measures how often each one actually reports and suggests a limit from that.

Everything is configured in the app settings, and there are Flow cards for triggers, conditions and actions.

Testing

You’ll need a Homey Pro — the app uses the `homey:manager:api` permission, which doesn’t exist on Homey Cloud.

Please do this first: go to Learning in the app settings and start discovery mode for 3 days. The app collects data without alerting during that period. Skip it and you may get alerts for healthy devices the shipped defaults are conservative, but they’re still guesses.

What I’d like to hear

  • Devices flagged that shouldn’t have been — the most useful feedback
  • Devices that went quiet without the app noticing
  • Anything under *cannot be verified* that you think should be monitored
  • Anything confusing in the interface

Known limitations

  1. Homey Pro only.
  2. Some apps freeze their timestamps. I’ve seen `measure_battery` sit unchanged for ten months on working devices. Where that happens, no tool can tell whether the sensor is alive.
  3. “Quiet” and “dead” can’t always be told apart. For sensors that only report on change, the app can tell you how long it’s been, and no more. It says so rather than pretending otherwise.
  4. `available` is unreliable. Many apps never mark their devices as unavailable, even when the radio link is gone.

Available in English and Norwegian.

Hello Reidar, I just installed your app and it is in learning mode. Some initial observations:

  • My iPad is set to dark mode. The app settings screen of your app is displayed in light mode however;
  • Zigbee devices that are paired to Homey as a generic Zigbee device were excluded. Not sure whether that is intentional. In my case those devices regard dimmable smart plugs that can be monitored. I added those to be monitored anyway. They now have the rule ‘contact with Homey only’. I assume one of the ‘smart plug’ rules would be a better fit (as they are smart plugs).
  • Similar like Advanced Virtual Devices from the Device Capabilities app (that the app excludes automatically) I think virtual aggregation devices provide by the Power Price app (to e.g. track power consumption of sessions) can also be excluded by default.

Thanks, I appreciate your feedback and taking the time to write up what you found.

Dark mode - I’m working on it.

Power Price - They’ should now excluded by default, same as Device Capabilities.

The generic Zigbee devices - Should be fixed, but needs to be verified.

Could you create a troubleshooting report? It’s under Settings → Troubleshooting → “Create report”. It lists every skipped device with its driver id, class, capabilities and the exact reason it was skipped — and device and zone names are deliberately left out, so it’s safe to paste publicly. That one report would answer this completely, and it’ll also show me how your thermostats and slow sensors are being classified, which is useful for another change I’ve just made.

If you’d rather not generate the report, just tell me what reason is listed next to those Zigbee devices on the Skipped tab — “no sensor data”, “filtered out”, or something else.

Access the appsite and install the app again if you are not running version 0.3.3 - go to settings (last tab) and reset all settings and verify that Power Price now is excluded by default.

This permission gives your app full control over one’s Homey, so please provide its source code (especially since it also looks like it was AI-generated).

Some Aqara devices, the plugs, are flagged even after hours as having no data. When I check the last seen time stamps it’s updated every hour. With zWave devices its a bit difficult. The majority wake up (some should) every 6 hours. My problem child is the devolo zWave sirene. The timestamp is set (hopefully only) at homey boot time. A test command verifies its alive status and updates the last seen timestamp.

Wanted to include a report. Far too many bytes to include. Webpage complained.

This app looks very similar to the app Device Watchdog. Are there any differences?

Yes. Am testing both. Sofar I do prefer „Device Watchdog“. You should have a look at both.
Sensor watchdog is still in early alpha but feedback might give you Features you need.

Thanks for the quick follow-up.

I removed the app and reinstalled it (v0.4.2), just to be sure that everything is reset.

  • The config page is still displayed in light mode on my iPad. My system setting is dark mode and Homey follows my system settings.
  • The two generic Zigbee smart plugs I have are still excluded by default. This is their driver:
  • Power Price devices are now excluded by default :+1:.
  • I also noted that the app seems to think that there is a problem with most of my Shelly devices (switches and power strips) connected via the Shelly Control app. I see this warning in the app:

    Also the list below that warning (in red) contains mostly Shelly devices for which the app indicates that there is no data, while many capabilities of those devices update regularly.

I tried to include the report, but it is to big to be posted here:

Not sure how to share the full info. I made an extract of the report which includes (only) the skipped/excluded generic Zigbee devices and some general data. I removed the devices section and other skipped devices:

Report extract
{
  "generatedAt": "2026-08-19T17:47:50.997Z",
  "ready": true,
  "meta": {
    "appVersion": "0.4.2",
    "language": "en",
    "intervalMinutes": 15,
    "discoveryUntil": "2026-08-22T15:14:31.409Z",
    "realtime": {
      "connected": true,
      "delivering": true,
      "missedByEvents": 0,
      "connectedSince": "2026-08-19T15:14:57.406Z",
      "connectAttempts": 1,
      "eventsReceived": 52,
      "eventsPerMinute": 0.3,
      "availabilityChanges": 1,
      "eventsWithoutAvailable": 0,
      "lastEventAt": "2026-08-19T17:40:02.446Z",
      "knownDevices": 138
    }
  },
  "counts": {
    "monitored": 118,
    "verifiable": 118,
    "ok": 99,
    "problems": 19,
    "pending": 0,
    "unverifiable": 0,
    "newAlerts": 0,
    "recovered": 0,
    "lowBattery": 2,
    "groupFailures": 1,
    "newGroupAlerts": 0,
    "skipped": 20,
    "errors": 0
  },
  "groupFailures": [
    {
      "key": "app:homey:app:cloud.shelly.control",
      "stale": 8,
      "total": 11,
      "ageMinutes": 1234
    }
  ],
  "skipped": [
    {
      "id": "d52",
      "driverId": "homey:virtualdriverzigbee:driver",
      "class": "socket",
      "reason": "excluded",
      "capabilities": [
        "onoff",
        "dim"
      ]
    },
    {
      "id": "d125",
      "driverId": "homey:virtualdriverzigbee:driver",
      "class": "socket",
      "reason": "excluded",
      "capabilities": [
        "onoff",
        "dim"
      ]
    }
  ],
  "overrides": {},
  "devices": [
  ],
  "note": "Enhets- og sonenavn er utelatt med vilje. Enhetene er nummerert d1, d2 …",
  "display": {
    "theme": "dark",
    "source": "prefers-color-scheme",
    "os": "dark",
    "background": "rgb(255, 255, 255)"
  }
}

For example with Github Gist:

https://gist.github.com

You do need a GitHub account to use it.

Hi. I have a few dirreferent senosrs. So happy to help. Always looking at how to improve my homes solution

Two things I noticed: a sirene has the rule: motion/ leak - on change only and since 2 FW updates ago zWave device have ( again) a last seen timestamp. It doesn’t seem to be considered. Intentionaly?

This one is definitely AI generated. Not sure why you need to develop an App when you can just run the Homeyscript. Here is a copy if anyones interested. Hope I’m not comitting a sin by doing this.

// DeviceWatch V4 — notify when devices become unreachable / recover or batteries run low,
// and publish a current-state health snapshot for the dashboard card.
// Flow: every 5 min → run this script → IF returns true → push notification with tag {DeviceWatch_Message}
//       Both true AND false outputs → Enhanced Set Status (widget ID: device_health)
//       Modbus_Unavailable changing to true also triggers a separate Modbus app restart branch
//       (Device Watch flow, added 19 Aug 2026) - see V4 change note below for why persistence
//       was added here rather than in that flow branch.
//
// Logic variables written EVERY run (for the Status Display widget):
//   DH_Level : "OK" | "WARN" | "ALERT"  (WARN = low battery only, ALERT = device offline)
//   DH_Text  : HTML body for the widget
//
// V4 CHANGE (19 Aug 2026): Modbus_Unavailable now requires TWO consecutive unavailable
// scans (10 minutes) before flipping to true, instead of flipping on the very first
// offline reading. Peter added a flow branch that restarts the Modbus app the moment
// Modbus_Unavailable becomes true - reviewed and found clean (no inverted-flag pitfall,
// no restart-spam risk, since variable_changed only fires on a real transition), but the
// single-scan trigger meant a transient blip that would've self-resolved within one 5-min
// cycle (the kind already seen in the Sys Log for TWC/Tesla Gateway) would restart the app
// unnecessarily. Recovery (available again) still clears the counter and flips
// Modbus_Unavailable back to false immediately - no persistence needed on the recovery
// side, only before declaring it down.

const IGNORE = ['Ask Marg', 'Apple TV', 'PowerOff', 'Samsung Blu-ray Player', 'Samsung TV', 'Sony Playstation', 'Watch Apple TV', 'Watch FTA TV','Blind']; // device names to skip
const MATTER_ONLY = false;   // true = only devices whose driver id contains 'matter'

const BATTERY_THRESHOLD = 20;         // percent — flag low at or below this
const BATTERY_RECOVER = 25;           // percent — only declare recovered above this (hysteresis)
const BATTERY_IGNORE = ['Tesla Gateway']; // measure_battery here is Powerwall SOC, not a device battery

const MODBUS_CONSECUTIVE_SCANS_REQUIRED = 2; // 2 x 5-min cycles = 10 min before declaring down

// --- Helper: write a Logic variable by name ---
async function setLogicVar(name, value) {
  const vars = await Homey.logic.getVariables();
  const v = Object.values(vars).find(x => x.name === name);
  if (v) await Homey.logic.updateVariable({ id: v.id, variable: { value } });
}

const devices = await Homey.devices.getDevices();

const watched = Object.values(devices)
  .filter(d => !IGNORE.includes(d.name))
  .filter(d => !MATTER_ONLY || (d.driverId || '').toLowerCase().includes('matter'));

const offline = watched
  .filter(d => d.available === false)
  .map(d => d.name)
  .sort();

// --- Modbus availability flag (persistence-gated, see V4 note above) ---
const modbusOfflineThisScan = offline.includes('Modbus');
let modbusConsecutiveOffline = global.get('devicewatch_modbus_consecutive') || 0;

if (modbusOfflineThisScan) {
  modbusConsecutiveOffline += 1;
} else {
  modbusConsecutiveOffline = 0;
}
global.set('devicewatch_modbus_consecutive', modbusConsecutiveOffline);

const modbusUnavailable = modbusConsecutiveOffline >= MODBUS_CONSECUTIVE_SCANS_REQUIRED;
await setLogicVar('Modbus_Unavailable', modbusUnavailable);

// --- Battery scan (online devices only) ---
const prevLow = global.get('devicewatch_lowbatt') || [];
const lowBattery = [];   // display strings for the card
const lowNames = [];     // names only, for transition tracking
let batteryCount = 0;

for (const d of watched) {
  if (d.available === false) continue;           // stale reading anyway
  if (BATTERY_IGNORE.includes(d.name)) continue;
  const batt = d.capabilitiesObj?.measure_battery?.value;
  if (typeof batt !== 'number') continue;
  batteryCount++;

  const wasLow = prevLow.includes(d.name);
  // Hysteresis: enter low at <= THRESHOLD, only leave low above RECOVER
  const isLow = wasLow ? (batt <= BATTERY_RECOVER) : (batt <= BATTERY_THRESHOLD);

  if (isLow) {
    lowNames.push(d.name);
    lowBattery.push(`${d.name} — ${Math.round(batt)}%`);
  }
}
lowNames.sort();

// --- Dashboard snapshot (runs every cycle, independent of transitions) ---
let dhLevel, dhText;
if (offline.length === 0 && lowBattery.length === 0) {
  dhLevel = 'OK';
  dhText = `All ${watched.length} devices online<br>${batteryCount} batteries checked, all above ${BATTERY_THRESHOLD}%`;
} else {
  dhLevel = offline.length > 0 ? 'ALERT' : 'WARN';
  const lines = [];
  for (const name of offline) lines.push(`&#9888;&#65039; ${name} — offline`);
  for (const entry of lowBattery) lines.push(`&#128267; ${entry}`);
  dhText = lines.join('<br>');
}

await setLogicVar('DH_Level', dhLevel);
await setLogicVar('DH_Text', dhText);
log(`DH: ${dhLevel} | ${dhText.replace(/<br>/g, ' · ')}`);

// --- Transition detection + notification ---
const prev = global.get('devicewatch_offline') || [];
const gone = offline.filter(n => !prev.includes(n));      // newly unreachable
const back = prev.filter(n => !offline.includes(n));      // recovered

const newlyLow = lowNames.filter(n => !prevLow.includes(n));   // battery just went low
const battOk = prevLow.filter(n => !lowNames.includes(n));     // battery replaced / recovered

global.set('devicewatch_offline', offline);
global.set('devicewatch_lowbatt', lowNames);

if (gone.length === 0 && back.length === 0 && newlyLow.length === 0 && battOk.length === 0) return false;

let msg = '';
if (gone.length) msg += `⚠️ Unreachable: ${gone.join(', ')}. `;
if (back.length) msg += `✅ Back online: ${back.join(', ')}. `;
if (newlyLow.length) msg += `🔋 Low battery: ${newlyLow.join(', ')}. `;
if (battOk.length) msg += `✅ Battery OK: ${battOk.join(', ')}.`;

await setLogicVar('DeviceWatch_Message', msg.trim());
log(msg);
return true;

Yes, one can just run a script. I did. Only the app presents the results much nicer than I can do with scripts.

I just have the results posted on my dashboard unless it is a critical device in which case I send myself a critical alert. I didn’t say anything about the author, I just posted the script I got AI to create.

The difference is in how the app decide if a sensor is ok or not. It evaluates the data that the sensor sends/homey receive, and is not based on homeys “last seen” timestamp.

You have to reset settings, uninstalling an app will not remove settings. Regarding the report, I will try to figure out a better/easier way to share the report.
The app will report problems when it’s not receiving sensordata despite that the sensor has contact with the homey. One of the feature with this app is to validate the sensor functionality, not only connectivity.

Sorry,
Guess I misunderstood you.

Thanks for explaining the difference @Reidar.

I performed a full reset via the button on the Setting tab. The result is the same as after a reinstall of the app:

  • The two dimmable plugs paired as a Generic Zigbee device are excluded by default;
  • A warning is displayed that 8 of the 11 devices of the Shelly Control app are silent (while some of their capabilities being updated frequently).
  • The settings screen is displayed in dark mode on the web app, but not on my iOS devices though he system theme is set to dark mode.

I generated a new report and included the 13 devices mentioned above. I removed all other devices from the report to keep the size small enough to post here.

The 2 Generic Zigbee devices (dimmable plugs) are device IDs d52 and d125. They use driver homey:virtualdriverzigbee:driver. Note that I forgot to run the report before including them in the group of devices that are monitored. So when I ran the report they were not in the excluded group anymore.

The other devices included are the 11 Shelly devices connected via the Shelly Control app. These regard 4 physical devices represented in Homey as 11 logical devices:

  • 1 x Shelly 1PM Gen3 + Shelly Plus Add-On module, using driver homey:app:cloud.shelly.control:shelly1pmg3_local. This is a switch with power monitoring capabilities (device d53) + an add-on module to which 5 temperature sensors are connected (device d9). I don’t use the switching part (so there is no energy usage), but other capabilities like voltage are updated frequently. As well as the temperature capabilities of the add-on module.
  • 1 x Shelly 1PM Mini Gen3, using driver homey:app:cloud.shelly.control:shelly1pmminig3_local. This is a switch with power monitoring capabilities (device d51).
  • 2 x Shelly PowerStrip 4 Gen4, using driver homey:app:cloud.shelly.control:shellypstripg4_local. This is a power strip with 4 smart plugs. Each plug is represented as a logical device in Homey. So 8 in total.

I hope this info is useful. I’ll have the app complete its learning cycle now (it restarted the 3 day cycle after the reset).

Report
{
	"generatedAt": "2026-08-21T11:08:11.307Z",
	"ready": true,
	"meta": {
		"appVersion": "0.4.2",
		"language": "en",
		"intervalMinutes": 15,
		"discoveryUntil": "2026-08-24T11:03:23.831Z",
		"realtime": {
			"connected": true,
			"delivering": true,
			"missedByEvents": 0,
			"connectedSince": "2026-08-19T15:14:57.406Z",
			"connectAttempts": 1,
			"eventsReceived": 951,
			"eventsPerMinute": 0.4,
			"availabilityChanges": 34,
			"eventsWithoutAvailable": 0,
			"lastEventAt": "2026-08-21T11:01:38.506Z",
			"knownDevices": 138
		}
	},
	"counts": {
		"monitored": 120,
		"verifiable": 120,
		"ok": 101,
		"problems": 19,
		"pending": 0,
		"unverifiable": 0,
		"newAlerts": 0,
		"recovered": 0,
		"lowBattery": 1,
		"groupFailures": 1,
		"newGroupAlerts": 1,
		"skipped": 18,
		"errors": 0
	},
	"groupFailures": [
		{
			"key": "app:homey:app:cloud.shelly.control",
			"stale": 8,
			"total": 11,
			"ageMinutes": 845
		}
	],
	"skipped": [],
	"overrides": {
		"devices": {
			"d52": {
				"include": true
			},
			"d125": {
				"include": true
			}
		}
	},
	"devices": [
		{
			"id": "d64",
			"driverId": "homey:app:cloud.shelly.control:shellypstripg4_local",
			"class": "socket",
			"available": true,
			"status": "stale",
			"reasonCode": "no_data",
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 30242,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [
				"measure_power"
			],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 30242,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": false,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d53",
			"driverId": "homey:app:cloud.shelly.control:shelly1pmg3_local",
			"class": "socket",
			"available": true,
			"status": "stale",
			"reasonCode": "no_data",
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 29802,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [
				"measure_power"
			],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 29802,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": false,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d76",
			"driverId": "homey:app:cloud.shelly.control:shellypstripg4_local",
			"class": "socket",
			"available": true,
			"status": "stale",
			"reasonCode": "no_data",
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 12490,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 1,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [
				"measure_power"
			],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 12490,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": false,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d83",
			"driverId": "homey:app:cloud.shelly.control:shellypstripg4_local",
			"class": "socket",
			"available": true,
			"status": "stale",
			"reasonCode": "no_data",
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 6500,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [
				"measure_power"
			],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 6500,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": false,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d91",
			"driverId": "homey:app:cloud.shelly.control:shellypstripg4_local",
			"class": "socket",
			"available": true,
			"status": "stale",
			"reasonCode": "no_data",
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 995,
			"signalAgeMinutes": 1,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [
				"measure_power"
			],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 995,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": false,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d80",
			"driverId": "homey:app:cloud.shelly.control:shellypstripg4_local",
			"class": "socket",
			"available": true,
			"status": "stale",
			"reasonCode": "no_data",
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 845,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [
				"measure_power"
			],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 845,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": false,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d27",
			"driverId": "homey:app:cloud.shelly.control:shellypstripg4_local",
			"class": "socket",
			"available": true,
			"status": "stale",
			"reasonCode": "no_data",
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 845,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [
				"measure_power"
			],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 845,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": false,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d75",
			"driverId": "homey:app:cloud.shelly.control:shellypstripg4_local",
			"class": "socket",
			"available": true,
			"status": "stale",
			"reasonCode": "no_data",
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 845,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [
				"measure_power"
			],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 845,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": false,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d3",
			"driverId": "homey:app:cloud.shelly.control:shellypstripg4_local",
			"class": "socket",
			"available": true,
			"status": "ok",
			"reasonCode": null,
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 0,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 1,
					"longestGapMinutes": 2,
					"ageMinutes": 0,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": true,
					"valueChanges": 1,
					"valueUnchangedMinutes": 3,
					"maxUnchangedMinutes": null,
					"smallestChange": 7.7,
					"averageChange": 7.7
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 1,
				"medianMinutes": 2,
				"p95Minutes": 2,
				"maxMinutes": 2,
				"observedForHours": 0,
				"confidence": "low"
			}
		},
		{
			"id": "d52",
			"driverId": "homey:virtualdriverzigbee:driver",
			"class": "socket",
			"available": true,
			"status": "ok",
			"reasonCode": null,
			"pendingReason": null,
			"rule": "device_override",
			"timeoutMinutes": 10080,
			"ageMinutes": null,
			"signalAgeMinutes": null,
			"seenAgeMinutes": 1,
			"seenTimeoutMinutes": null,
			"seenObservations": 0,
			"availabilityOnly": true,
			"monitored": [],
			"staleCapabilities": [],
			"capabilities": [],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d125",
			"driverId": "homey:virtualdriverzigbee:driver",
			"class": "socket",
			"available": true,
			"status": "ok",
			"reasonCode": null,
			"pendingReason": null,
			"rule": "device_override",
			"timeoutMinutes": 10080,
			"ageMinutes": null,
			"signalAgeMinutes": null,
			"seenAgeMinutes": 1,
			"seenTimeoutMinutes": null,
			"seenObservations": 0,
			"availabilityOnly": true,
			"monitored": [],
			"staleCapabilities": [],
			"capabilities": [],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 0,
				"medianMinutes": null,
				"p95Minutes": null,
				"maxMinutes": null,
				"observedForHours": 0,
				"confidence": "none"
			}
		},
		{
			"id": "d9",
			"driverId": "homey:app:cloud.shelly.control:shelly1pmg3_local",
			"class": "socket",
			"available": true,
			"status": "ok",
			"reasonCode": null,
			"pendingReason": null,
			"rule": "defaults",
			"timeoutMinutes": 90,
			"ageMinutes": 1,
			"signalAgeMinutes": 1,
			"seenAgeMinutes": 1,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_temperature.100",
				"measure_temperature.101",
				"measure_temperature.102",
				"measure_temperature.103",
				"measure_temperature.104"
			],
			"staleCapabilities": [],
			"capabilities": [
				{
					"id": "measure_temperature.100",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 5,
					"timeoutMinutes": 90,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": true,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				},
				{
					"id": "measure_temperature.101",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 67,
					"timeoutMinutes": 90,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": true,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				},
				{
					"id": "measure_temperature.102",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 3,
					"timeoutMinutes": 90,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": true,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				},
				{
					"id": "measure_temperature.103",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 0,
					"longestGapMinutes": null,
					"ageMinutes": 30,
					"timeoutMinutes": 90,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": true,
					"valueChanges": 0,
					"valueUnchangedMinutes": 5,
					"maxUnchangedMinutes": 2,
					"smallestChange": null,
					"averageChange": null
				},
				{
					"id": "measure_temperature.104",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 1,
					"longestGapMinutes": 2,
					"ageMinutes": 1,
					"timeoutMinutes": 90,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": true,
					"valueChanges": 1,
					"valueUnchangedMinutes": 3,
					"maxUnchangedMinutes": null,
					"smallestChange": 0.1,
					"averageChange": 0.1
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 1,
				"medianMinutes": 1,
				"p95Minutes": 1,
				"maxMinutes": 1,
				"observedForHours": 0,
				"confidence": "low"
			}
		},
		{
			"id": "d51",
			"driverId": "homey:app:cloud.shelly.control:shelly1pmminig3_local",
			"class": "socket",
			"available": true,
			"status": "ok",
			"reasonCode": null,
			"pendingReason": null,
			"rule": "socket",
			"timeoutMinutes": 720,
			"ageMinutes": 0,
			"signalAgeMinutes": 0,
			"seenAgeMinutes": 0,
			"seenTimeoutMinutes": null,
			"seenObservations": 1,
			"availabilityOnly": false,
			"monitored": [
				"measure_power"
			],
			"staleCapabilities": [],
			"capabilities": [
				{
					"id": "measure_power",
					"kind": "periodic",
					"staticKind": "periodic",
					"reclassified": false,
					"looseTimeout": false,
					"observations": 1,
					"longestGapMinutes": 2,
					"ageMinutes": 0,
					"timeoutMinutes": 720,
					"timeoutSource": "device",
					"provesDeath": false,
					"conditionMet": true,
					"fresh": true,
					"valueChanges": 1,
					"valueUnchangedMinutes": 3,
					"maxUnchangedMinutes": null,
					"smallestChange": 0.1,
					"averageChange": 0.1
				}
			],
			"battery": {
				"text": "n/a",
				"low": false
			},
			"learning": {
				"observations": 1,
				"medianMinutes": 2,
				"p95Minutes": 2,
				"maxMinutes": 2,
				"observedForHours": 0,
				"confidence": "low"
			}
		}
	],
	"note": "Enhets- og sonenavn er utelatt med vilje. Enhetene er nummerert d1, d2 …",
	"display": {
		"theme": "dark",
		"source": "prefers-color-scheme",
		"os": "dark",
		"background": "rgb(255, 255, 255)"
	}
}

Thanks for quality feedback :+1:
Update to latest version, currently 0.4.3, and see if things are solved. Regarding the dark/light-mode it seems to be a weakness in how Homey detects/decide dark or light mode and the use of CSS. I see other apps with the same issue. I’m not prioritizing the dark/light mode at the moment unless someone know how to solve this. I can’t find anything about it in the SDK documentation App Settings | Homey Apps SDK

I’m currently working to optimize the report generated and to reduce the report size.