Commit Graph

5380 Commits

Author SHA1 Message Date
Kristjan ESPERANTO
d203fefed1 feat(systeminfo): include Git hash and branch in system information log (#4167)
I suggest to add the commit hash and branch to the system information.
This should help trouble shooting issues from developers. Like in #4165.

### before

```
####  System Information  ####
- SYSTEM:   manufacturer: Micro-Star International Co., Ltd.; model: MS-7D75; virtual: false; MM: v2.37.0-develop
- OS:       ...
...
```

### after

```
####  System Information  ####
- MM:       version: v2.37.0-develop; git: 03e4eef3d; branch: develop
- SYSTEM:   manufacturer: Micro-Star International Co., Ltd.; model: MS-7D75; virtual: false
- OS:       ...
...
```
2026-05-27 23:25:49 +02:00
Kristjan ESPERANTO
03e4eef3d1 fix(updatenotification): don't spawn a child process when running under PM2 (#4166)
Previously, `nodeRestart()` would spawn a detached child and exit. Under
PM2 that's a problem: PM2 also respawns on exit, so both race to bind
the same port.

The fix: When `process.env.pm_id` is set, just exit and let PM2 handle
the restart.

The spawn logic is moved into its own method so it can be tested
cleanly.

Partially fixes #4165
2026-05-21 22:25:47 +02:00
Kristjan ESPERANTO
e55b11be5d feat(electron): support object-based electronSwitches (#4161)
Previously, `electronSwitches` only accepted strings. This PR adds
support for object entries, allowing switches with values:

```js
electronSwitches: [
  "no-sandbox",
  { "js-flags": "--max-old-space-size=8192" }
]
```

I decided to put the logic into a helper file so it can be unit-tested
independently, since electron.js itself requires a live Electron
environment and cannot be tested in isolation.

Fixes #4159
2026-05-20 15:30:23 -05:00
Kristjan ESPERANTO
fd687bf5fe feat(weather): add Buienradar provider (#4164)
This adds the weather provider Buienradar for the Netherlands and
Belgium without requiring an API key.

In MagicMirrorOrg/MagicMirror-Documentation#380 @plebcity shared his
implementation for the old browser-side architecture. I used it as a
reference to build this server-side implementation.

### Example screenshot

<img width="969" height="578" alt="Ekrankopio de 2026-05-20 20-38-36"
src="https://github.com/user-attachments/assets/56623ad8-7439-4047-abad-452ba2ebdcb2"
/>

### Example config

```js
		{
			module: "weather",
			position: "top_left",
			header: "Buienradar - Current",
			config: {
				weatherProvider: "buienradar",
				type: "current",
				locationId: 2747891
			}
		},
		{
			module: "weather",
			position: "top_right",
			header: "Buienradar - Forecast",
			config: {
				weatherProvider: "buienradar",
				type: "forecast",
				locationId: 2747891
			}
		},
		{
			module: "weather",
			position: "bottom_left",
			header: "Buienradar - Hourly",
			config: {
				weatherProvider: "buienradar",
				type: "hourly",
				locationId: 2747891
			}
		},
```

----

When this is accepted and merged I'll update the weather docs.
2026-05-20 22:06:44 +02:00
Kristjan ESPERANTO
9a3538de01 chore: update dependencies (#4162)
This updates the dependencies and implements a workaround for the
failing ci tests on node 26.1.0 (#4150).

I'm not sure myself whether we should go with this workaround or wait
for an upstream fix.
2026-05-20 20:16:17 +02:00
Kristjan ESPERANTO
1b540aeba1 fix(updatenotification): use process.argv[0] as restart binary (#4163)
The restart approach I introduced in #4156 still crashes under Electron:

```
TypeError: Cannot read properties of undefined (reading 'disableHardwareAcceleration')
    at electron.js:18
```

`nodeRestart()` hardcodes `node` as the interpreter, but under Electron
`process.argv[0]` is the Electron binary. Spawning `node js/electron.js`
causes `require("electron")` to return a string instead of the API, so
`electron.app` is `undefined`.

This uses `process.argv[0]` as the binary directly, which works for both
Electron and plain Node.

I introduced more variables for more clarity.

Fixes #4154.
2026-05-20 19:30:51 +02:00
Karsten Hassel
7abbe62cb0 systeminformation thread not ending: move error handling from utils to app (#4160) 2026-05-20 00:03:15 +02:00
Karsten Hassel
3c514dff3a fix systeminformation thread not ending (#4155)
This was introduced when running systeminformation in an own thread.

If the main thread ends before (e.g. when config has errors) the
systeminformation thread never ends.

As this can have side effects under pm2 or docker (because the app never
exits) this PR restores the old behavior.
2026-05-18 21:02:14 +02:00
Kristjan ESPERANTO
d4a5ebe273 refactor: use ES module imports in browser core (#4158)
With these changes a few browser-side core files now use native ES
modules. `Loader`, `MMSocket`, `Module` and `MM` can be imported
directly instead of being read off `window`. `main.js` and `loader.js`
are no longer wrapped in IIFEs - they're just normal modules now.

`Module`, `MM` and `MMSocket` are still exposed as globals, so
third-party modules that use the old API keep working.

The changes are mostly structural, behavior should stay the same. A few
internal helpers in `main.js` got an underscore prefix because their
names clashed with public `MM` methods.

## Why

The old setup relied a lot on script order: a file could use `Loader` or
`MMSocket` only because another script happened to put it on `window`
first. Imports make that explicit.

The bigger goal is to move away from the legacy script-loading patterns
- making it easier to understand and easier to test - in other words:
easier to maintain.

More of the core could be "cleaned up" the same way, but that would blow
up this PR.

For reviewing, I recommend to hide the whitespace changes.
2026-05-18 19:58:44 +02:00
Kristjan ESPERANTO
4425f52bda fix(updatenotification): preserve start mode on restart (#4156)
Use current process arguments when spawning the restart command so
auto-restart keeps the active runtime mode (for example `start:x11`)
instead of always defaulting to `start`.

This should fix #4154.
2026-05-17 13:27:11 +02:00
Kristjan ESPERANTO
41eb17c0e3 refactor(core): remove old Object.assign polyfill (#4157)
Object.assign has been standard since ES2015, and all runtimes we
support already provide it.

Removing this keeps the client code a little cleaner.
2026-05-17 13:26:06 +02:00
Kristjan ESPERANTO
13d51cfce9 CodeQL cleanup for alerts #18, #19, #20 (#4153)
Small cleanup PR to close three CodeQL alerts, without changing app
behavior.

-
[#18](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/18)
in `js/loader.js`
-
[#19](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/19)
in `js/socketclient.js`
-
[#20](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/20)
in `tests/electron/modules/calendar_spec.js`

For `Loader` and `MMSocket`, make the global export explicit via
`globalThis` (instead of suppressing `no-unused-vars`).
For calendar tests, remove the unused debug helper `logAllText`.
Also includes a tiny e2e cleanup: `MM_PORT` is cleared in `afterAll`.

Outcome: Three open CodeQL alerts are addressed with small, low-risk
changes. The global intent for `Loader` and `MMSocket` is explicit, dead
test helper code is removed, and e2e test state is cleaned up more
reliably between runs.

With this, all current [code scanning
issues](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning)
should be resolved in the develop branch 🎈
2026-05-12 23:32:16 +02:00
Kristjan ESPERANTO
79ea2633a7 refactor: rewrite Module as an ES6 class (#4151)
This PR rewrites `module.js` to use a native ES6 `class` instead of
`Class.extend()` - the same old inheritance helper that was removed from
`node_helper.js` in #4147.

The normal module API stays the same: modules still use
`Module.register({...})`. Internally, `Module.create()` now creates a
named subclass for each module, copies over a cloned definition, and
only calls `init()` when it is actually a function.

Outcome: one less file in the browser bundle, no more magic `_super()`
wiring, better stack traces, and tests for the module creation path that
did not exist before. `module.js` is also now a plain class with no
external dependencies - an intentional step towards `export default
Module` when browser scripts eventually move to native ES modules.

Since these changes touch the core of how modules are created, I'd
appreciate a close review and any feedback on edge cases I may have
missed.
2026-05-12 20:01:12 +02:00
dependabot[bot]
b38c7b7aa2 Bump actions/dependency-review-action from 4 to 5 (#4152)
Bumps
[actions/dependency-review-action](https://github.com/actions/dependency-review-action)
from 4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/dependency-review-action/releases">actions/dependency-review-action's
releases</a>.</em></p>
<blockquote>
<h2>5.0.0</h2>
<p>This is a new major version of the Dependency Review Action which
updates the runtime to node24. This requires a minimum Actions Runner
version <a
href="https://github.com/actions/runner/releases/tag/v2.327.1">v2.327.1</a>
to run.</p>
<h2>What's Changed</h2>
<ul>
<li>Add .github/copilot-instructions.md for Copilot coding agent by <a
href="https://github.com/ahpook"><code>@​ahpook</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1067">actions/dependency-review-action#1067</a></li>
<li>Update Node.js runtime from 20 to 24 by <a
href="https://github.com/scottschreckengaust"><code>@​scottschreckengaust</code></a>
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1084">actions/dependency-review-action#1084</a></li>
<li>Bump spdx-license-ids from 3.0.20 to 3.0.23 by <a
href="https://github.com/mongolyy"><code>@​mongolyy</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1091">actions/dependency-review-action#1091</a></li>
<li>docs: bump actions/checkout from v4 to v6 in workflow examples by <a
href="https://github.com/Marukome0743"><code>@​Marukome0743</code></a>
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1077">actions/dependency-review-action#1077</a></li>
<li>fix: patched version display for advisories with non-strict semver
ranges (e.g. Maven beta versions) by <a
href="https://github.com/tspascoal"><code>@​tspascoal</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1076">actions/dependency-review-action#1076</a></li>
<li>Resolve security findings by <a
href="https://github.com/AshelyTC"><code>@​AshelyTC</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1094">actions/dependency-review-action#1094</a></li>
<li>v5.0.0 release branch by <a
href="https://github.com/ahpook"><code>@​ahpook</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1098">actions/dependency-review-action#1098</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/scottschreckengaust"><code>@​scottschreckengaust</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1084">actions/dependency-review-action#1084</a></li>
<li><a href="https://github.com/mongolyy"><code>@​mongolyy</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1091">actions/dependency-review-action#1091</a></li>
<li><a
href="https://github.com/Marukome0743"><code>@​Marukome0743</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1077">actions/dependency-review-action#1077</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/dependency-review-action/compare/v4.9.0...v5.0.0">https://github.com/actions/dependency-review-action/compare/v4.9.0...v5.0.0</a></p>
<h2>Dependency Review Action 4.9.0</h2>
<p>This feature release contains a couple of notable changes:</p>
<ul>
<li>There is a new configuration option
<code>show_patched_versions</code> which will add a column to the
output, showing the fix version of each vulnerable dependency. Thanks <a
href="https://github.com/felickz"><code>@​felickz</code></a>!</li>
<li>Runs which do not display OpenSSF scorecards no longer fetch
scorecard information; previously it was fetched regardless of whether
or not it was displayed, causing unneccessary slowness. Great catch <a
href="https://github.com/jantiebot"><code>@​jantiebot</code></a>!</li>
<li>There are a couple of fixes to purl parsing which should improve
match accuracy for <code>allow-package-dependency</code> lists,
including case (in)sensitivity and url-encoded namespaces Thanks <a
href="https://github.com/juxtin"><code>@​juxtin</code></a>!</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Compare normalized purls to account for encoding quirks by <a
href="https://github.com/juxtin"><code>@​juxtin</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1056">actions/dependency-review-action#1056</a></li>
<li>Make purl comparisons case insensitive by <a
href="https://github.com/juxtin"><code>@​juxtin</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1057">actions/dependency-review-action#1057</a></li>
<li>Feat: Add <code>Patched Version</code> to
<code>Vulnerabilities</code> summary by <a
href="https://github.com/felickz"><code>@​felickz</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1045">actions/dependency-review-action#1045</a></li>
<li>fix: only get scorecard levels if user wants to see the OpenSSF
scorecard by <a
href="https://github.com/jantiebot"><code>@​jantiebot</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1060">actions/dependency-review-action#1060</a></li>
<li>Bump actions/stale from 10.1.0 to 10.2.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1058">actions/dependency-review-action#1058</a></li>
<li>Bump actions/checkout from 4 to 6 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1021">actions/dependency-review-action#1021</a></li>
<li>Updates for release 4.9.0 by <a
href="https://github.com/ahpook"><code>@​ahpook</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1064">actions/dependency-review-action#1064</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/jantiebot"><code>@​jantiebot</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1060">actions/dependency-review-action#1060</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/dependency-review-action/compare/v4.8.3...v4.9.0">https://github.com/actions/dependency-review-action/compare/v4.8.3...v4.9.0</a></p>
<h2>4.8.3</h2>
<h2>Dependency Review Action v4.8.3</h2>
<p>This is a bugfix release that updates a number of upstream
dependencies and includes a fix for the earlier feature that detected
oversized summaries and upload them as artifacts, which could
occasionally crash the action.</p>
<p>We have also updated the release process to use a long-lived
<code>v4</code> <strong>branch</strong> for the action, instead of a
force-pushed tag, which aligns better with git branching strategies; the
change should be transparent to end users.</p>
<h2>What's Changed</h2>
<ul>
<li>GitHub Actions can't push to our protected main by <a
href="https://github.com/dangoor"><code>@​dangoor</code></a> in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/1017">actions/dependency-review-action#1017</a></li>
<li>Bump actions/stale from 9.1.0 to 10.1.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/dependency-review-action/pull/995">actions/dependency-review-action#995</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a1d282b36b"><code>a1d282b</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/1098">#1098</a>
from actions/ahpook/v5-release</li>
<li><a
href="eb6c199c5a"><code>eb6c199</code></a>
update examples to show <a
href="https://github.com/v5"><code>@​v5</code></a></li>
<li><a
href="3943c2c5be"><code>3943c2c</code></a>
v5.0.0 release branch</li>
<li><a
href="454943c880"><code>454943c</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/1094">#1094</a>
from actions/ashelytc/security-findings</li>
<li><a
href="6d92a1228e"><code>6d92a12</code></a>
revert <code>@​typescript-eslint/parser</code> update</li>
<li><a
href="a8e5a7e936"><code>a8e5a7e</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/1076">#1076</a>
from tspascoal/fix-version-matching-for-non-string-s...</li>
<li><a
href="b6b7079031"><code>b6b7079</code></a>
update <code>@​typescript-eslint/parser</code> to 8.40.0</li>
<li><a
href="821a21dd69"><code>821a21d</code></a>
update more dependencies</li>
<li><a
href="05aaaae45c"><code>05aaaae</code></a>
run npm audit fix</li>
<li><a
href="55d3e75450"><code>55d3e75</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/dependency-review-action/issues/1077">#1077</a>
from Marukome0743/docs/checkout</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/dependency-review-action/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/dependency-review-action&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 19:13:34 +02:00
Karsten Hassel
170c4a9883 remove warning in unit tests (for nodejs >= v25) (#4149)
The unit tests were full of warnings `ExperimentalWarning: localStorage
is not available because --localstorage-file was not provided.` for node
versions >=25.

Additionally this PR nails node to `v26.0.0` in the automated-tests
workflow until the [playwright
issue](https://github.com/microsoft/playwright/issues/40724) is fixed.
Will revert this after fixed playwright version is available.
2026-05-09 23:00:16 +02:00
Kristjan ESPERANTO
2850e14172 Unify linting: replace Stylelint and markdownlint with ESLint (#4148)
We were running three separate lint tools (ESLint, Stylelint,
markdownlint-cli2) and a separate Prettier step. This PR consolidates
everything: `@eslint/css` and `@eslint/markdown` bring CSS and Markdown
linting into ESLint, so one tool now covers JS, CSS, and Markdown. The
Prettier step got merged into the lint scripts too, so there's now just
`test:lint` to check things and `lint:fix` to fix them.

Outcome: fewer tools to maintain, simpler/fewer scripts, fewer config
files.

As a side effect, the dependabot alerts
https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/140
and
https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/141
are resolved, as we no longer use the vulnerable `fast-uri` package
(which was a dependency of `stylelint`).
2026-05-09 12:01:18 +02:00
Kristjan ESPERANTO
67db41cfcb refactor: rewrite NodeHelper as an ES6 class (#4147)
This PR rewrites `node_helper.js` to use a native ES6 `class` instead of
`Class.extend()` - a manual inheritance helper from 2008, written back
when `class` syntax didn't exist yet. Node.js has supported native
classes since v6, so there's no reason to keep the workaround around.

The public API is unchanged - module authors still write the same
`NodeHelper.create({...})` they always have.

Outcome: same behavior, normal modern JavaScript, better stack traces,
and `node_helper.js` no longer pulls in `class.js` on the server side.
Removing `class.js` altogether is a follow-up (it's still used on
browser side).
2026-05-08 19:45:47 +02:00
Kristjan ESPERANTO
9386c44928 fix: resolve CodeQL alerts #24 and #26 (#4145)
**[#24](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/24)
– `js/class.js`**

`fnTest` works by serialising a function to a string and checking if
`"xyz"` appears in it - the function is never actually called. The bare
`xyz;` is never executed, so CodeQL is right to flag it. `return xyz;`
makes the intent clear. So this is purely a cosmetic change.


**[#26](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/26)
– `tests/e2e/helpers/global-setup.js`**

CodeQL flagged `if (exec) exec;` as a useless expression - and it was
right. But the real find was one level deeper.

`startApplication` hardcoded `const port = 8080`, so `MM_PORT` was
always overwritten before the app started. The test named "Set port 8100
on environment variable MM_PORT" was actually testing port 8080 the
whole time - it just happened to pass anyway.

Removed the dead `exec` parameter, made `startApplication` read
`MM_PORT` from the environment, and fixed the test so it actually checks
what it says it checks.
2026-05-07 00:28:02 +02:00
Kristjan ESPERANTO
7da9e5af02 perf(calendar): use async ICS parsing to avoid blocking event loop (#4143)
This switches the calendar fetcher from synchronous to asynchronous ICS
parsing.

It does not necessarily make parsing faster overall, but it avoids long
event-loop stalls with large calendar files (especially on slower
devices and with multiple feeds).

So this does not fully solve #4103 on its own, but it clearly mitigates
it.

It should also combine well with a future pre-filter approach discussed
in the issue:
- with pre-filter = less data to parse (future PR)
- with async parsing = less blocking while parsing (this PR)

Together, that is likely the strongest path to fully address #4103.
2026-05-06 20:40:44 +02:00
Karsten Hassel
842beef97d update eletron to v42 (#4144)
additional things:
- removed fix chrome-sandbox permissions (runs without)
- added `npx install-electron` which downloads the electron binaries

new in v42: 

> Electron now downloads its binary into node_modules dynamically on
first launch instead of running a postinstall script. Added the
install-electron script to manually trigger the download as well.
https://github.com/electron/electron/pull/49328
2026-05-06 20:25:37 +02:00
Kristjan ESPERANTO
67911546ed refactor(utils): drop ajv dependency (#4142)
This PR removes `ajv` from module position validation in `js/utils.js`
and replaces it with straightforward JavaScript checks. The old schema
only covered a few basic structure rules, so using `ajv` here felt
heavier than necessary. The updated validation keeps the same expected
behavior for valid configs and unknown-position warnings, but the code
path is now easier to read and easier to reason about when something
goes wrong.

Outcome: same validation behavior, one less dependency, less lines of
production code and better test coverage.
2026-05-06 19:11:47 +02:00
Kristjan ESPERANTO
c97d0947b6 fix(systeminformation): output right 'used node' version (from parent process) (#4141)
I noticed that in the System Information output, `used node` and
`installed node` were always identical when starting via Electron. That
usually shouldn't be the case. After digging into it, I found that since
PR #4002, `used node` in the subprocess effectively reported the system
Node version, not the parent process (which runs in Electron) version.

This PR fixes that by passing `used node` from the parent process and
logging it correctly.

**Before:**

`VERSIONS: electron: 41.3.0; used node: 26.0.0; installed node: 26.0.0;
...`

**After:**

`VERSIONS: electron: 41.3.0; used node: 24.15.0; installed node: 26.0.0;
...`
2026-05-06 19:10:57 +02:00
Karsten Hassel
461dbc04aa update dependencies and workflows to node v26 (#4140) 2026-05-06 09:15:42 +02:00
Andrés Vanegas Jiménez
b474198267 fix: skip postinstall git clean when not in a git repository (#4139)
## What does this PR accomplish?

The `postinstall` script runs `git clean -df fonts vendor
modules/default`
unconditionally. When `magicmirror` is installed as an npm dependency
(e.g. `npm install magicmirror` or as a transitive dep in another
project),
npm runs `postinstall` in a directory that is **not** a git repository.
This causes:

```
fatal: not a git repository (or any of the parent directories): .git
```

npm treats the non-zero exit as a failure → the entire installation
aborts
with `code 128`. This makes it impossible to use `magicmirror` as an npm
dependency in any third-party project.

The issue has been present since at least **v2.34.0**. In **v2.35.0**
`modules/default` was added to the clean targets, but the root cause
predates that change.

## Fix

Added `scripts/postinstall.js` — a cross-platform Node.js script that
guards the `git clean` call with a `git rev-parse --git-dir` check.

- When running inside a real git repository: behaviour is identical to
before.
- When running outside one (e.g. installed as an npm package): step is
silently skipped.

`package.json` `postinstall` updated from the bare `git clean` call to:

```json
"postinstall": "node scripts/postinstall.js"
```

This approach is cross-platform (Linux, macOS, Windows) and keeps the
logic readable rather than inlined as a one-liner.

Does this solve a related issue?

No existing issue tracked. Discovered while using magicmirror as a
devDependency in a companion tooling project — installation failed
unconditionally on all platforms.

Checklist

- Targets the develop branch
- No visual changes (script + package.json only)
- node --run lint:prettier run — no changes needed for .js script
2026-05-05 22:05:46 +02:00
Kristjan ESPERANTO
623e1e23d1 fix(updatenotification): fix ref diff parsing for fetch --dry-run (#4138)
The regex captured the full fetch output line including the branch name.
Before #4115, the command was built as a shell string, so the shell
split the arguments correctly. After #4115 switched to `execFile`, the
range and branch name were passed as a single argument
(`60e0377..332e429 develop`) - which git rejects as ambiguous.

The fix replaces the regex with column-based line parsing: find the line
for the current branch, take the first column. Falls back to
`<branch>..origin/<branch>` when fetch reports no changes (same behavior
as before).

Also adds unit tests for the new helper and corrects existing test
snapshots that encoded the broken behavior.

Fixes #4137.
2026-05-03 22:16:17 +02:00
Kristjan ESPERANTO
d20306cc4f fix(electron): resolve CodeQL alerts #22 and #25 in electron.js (#4136)
I reviewed the CodeQL alerts for `js/electron.js`:

-
[#25](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/25)
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/25
-
[#22](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/22)
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/22

Both point to real bugs.

-
[#25](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/25):
The window size fallback was written as a comma expression (`(800,
600)`), so it did not produce the expected object structure `{ width,
height }`. I am not surprised it went unnoticed because it sits in a
fallback path.
-
[#22](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/22):
`...new Set(electronSwitchesDefaults, config.electronSwitches)` silently
ignored the second parameter. As a result, custom `electronSwitches`
were never applied. I am wondering: this has been broken since PR #2643
introduced it, so I'm quite sure it could not have worked as intended in
that form. Why didn't anyone (not even @eouia) notice that? 🤔

## Changes

- Fix for
[#25](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/25):
- Corrects the fallback from `(800, 600)` to a valid size object `{
width: 800, height: 600 }`.
- Fix for
[#22](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/22):
  - Sets the default switch explicitly as a correct key-value pair:
- `app.commandLine.appendSwitch("autoplay-policy",
"no-user-gesture-required")`
  - Applies custom `config.electronSwitches` individually afterward.
2026-05-03 15:59:17 +02:00
Kristjan ESPERANTO
3335781af4 Remove unnecessary conditionals and fix falsy property check in imperial conversion (#4135)
While removing unnecessary conditionals (for `compliments` reported in
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/27
and for `weather` reported in
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/28),
I noticed a bug in the imperial conversion path: falsy property checks
like `if (imperialWeatherObject.temperature)` silently skip the
conversion when the value is `0`. So at exactly 0 °C, the result would
show `0 °F` instead of the correct `32 °F`.

I wrote unit tests for `convertWeatherObjectToImperial` first, which
confirmed the bug, then fixed it by replacing the falsy checks with the
`in` operator. Which I also find more intuitive to read.

Weather APIs deliver floating point values, so hitting exactly `0.0`
might be rare in practice - more likely `0.1` or `-0.1`, which are
truthy and convert fine. That should explain why it went unnoticed by
users.
2026-05-03 12:49:34 +02:00
Kristjan ESPERANTO
f2759ad4f6 refactor(updatenotification): replace pm2 usage with node logic (#4134)
We can rely on PM2's native restart-on-exit behavior instead of the
programmatic pm2 API.

Fixes
https://github.com/MagicMirrorOrg/MagicMirror/security/dependabot/82 by
removing pm2.

Note: Originally this PR was intended to update pm2, but after
discussion, we decided to replace it instead. See the discussion below.
2026-05-03 09:51:08 +02:00
Kristjan ESPERANTO
0905d66a91 polish HTTP 304 docs/test/handling (#4129)
After #4120 was merged, here's a bit of polish on top.

- docs: Optimize JSDoc and comments.
- test: The existing 304 test is moved to the dedicated status code
block and extended with an `errorSpy` to also verify that no `error`
event is emitted.
- refactor: The success branch is moved before the error branch in
`fetch()`. This is more intuitive.

The bottom line is that this PR improves maintainability. For users,
this PR doesn't change anything.
2026-05-01 00:02:01 +02:00
Karsten Hassel
01ae1b443d update version in package.json 2026-04-30 22:55:47 +02:00
Karsten Hassel
99a37ad818 Merge remote-tracking branch 'origin/master' into develop 2026-04-30 22:39:06 +02:00
Karsten Hassel
8dbee62730 Prepare Release 2.36.0 (#4126) 2026-04-30 22:26:57 +02:00
Karsten Hassel
5a82c9d132 update dependencies (#4124) 2026-04-29 06:32:24 +02:00
Kristjan ESPERANTO
8af3d02cbb fix(weather): use nearest openmeteo hourly data (#4123)
Open-Meteo updates `current_weather` every 15 minutes, but the hourly
array only has entries at full hours. The old code did an exact
timestamp match - so at 14:15, 14:30 or 14:45 it never found a match and
silently fell back to index 0, showing midnight values for humidity,
feels-like temp, precipitation, etc.

Fix: `findLastIndex((hour) => hour.time <= currentMs)` - the last hourly
entry at or before the current time.

While fixing the bug I found several dead branches left over from the
#4032 refactor: a path for pre-transposed hourly data that
`#parseWeatherApiResponse` makes unreachable, an `Array.isArray` guard
that's always true, and a `Log.debug` inside the dead branch. Removing
those accounts for most of the ~40 deleted lines - the actual fix is one
line.

Fixes #4122.
2026-04-28 21:42:38 +02:00
Kristjan ESPERANTO
4c2a373ae3 fix(weather): avoid loading state after reconnect (#4121)
When a client reconnects while the backend is still in its rate-limit
protection phase, the weather module has no data to show and stays on
`Loading...` until the next scheduled API call. This mainly affects
server mode setups, where the server keeps running while a remote client
temporarily loses its connection and reloads. It was [raised in the
forum](https://forum.magicmirror.builders/topic/20218/request-loop-loading...-in-standard-weather-module-open-meteo-after-update/11?_=1777106416020)
and is worthy of a fix to improve the user experience.

With this PR the node helper caches the last successful `WEATHER_DATA`
payload per instance and replays it immediately on reconnect. The client
gets its last known state right away instead of waiting for the next
fetch. The cache is cleaned up when the provider stops.

Tests are included to cover reconnect with and without cached data, and
the cleanup path.
2026-04-27 23:15:01 +02:00
Sonny B
b8548f2203 Allow HTTPFetcher to pass through 304 responses (#4120)
## Summary

This updates `js/http_fetcher.js` so HTTP `304 Not Modified` responses
are emitted through the normal `response` event instead of being treated
as errors.

That aligns the core fetcher with the built-in `yr` weather provider in
`v2.35.0`, which already includes explicit logic to handle `304` and
reuse cached data.

Closes #4119.

## Root cause

`HTTPFetcher` currently treats all non-OK responses as errors. Since
`304` is not an OK response, it never reaches providers listening on the
`response` event.

At the same time, `defaultmodules/weather/providers/yr.js` expects to
receive `304` and handle it by reusing cached data.

## What changed

- special-case HTTP `304` in `HTTPFetcher`
- emit `304` through the normal `response` event
- preserve the existing error path for other non-OK statuses

## Validation

- confirmed `defaultmodules/weather/providers/yr.js` already expects
`response.status === 304`
- compared the unpatched `v2.35.0` `HTTPFetcher` behavior to the
provider logic
- ran `node --check js/http_fetcher.js`

Co-authored-by: sonnyb9 <sonnyb9@users.noreply.github.com>
2026-04-27 23:09:20 +02:00
Kristjan ESPERANTO
9dc4b7a88f fix(updatenotification): harden git command execution + simplify checkUpdates (#4115)
This fixes CodeQL alert
[#16](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/16)
by replacing shell-built git commands with `execFile` + `cwd` in
updatenotification’s `git_helper`.

It also includes a small cleanup in `checkUpdates()` (remove unnecessary
async/Promise wrapper) and updates the related unit tests.
2026-04-27 23:06:29 +02:00
Kristjan ESPERANTO
7e1286257c fix(http-fetcher): fall back to reloadInterval after retries exhausted (#4113)
As reported in #4109, the weather module retries much more frequently
than expected after network errors. #4092 already fixed the main cause
(duplicate fetchers), but the backoff logic in `HTTPFetcher` still has a
gap: once retries are exhausted, `calculateBackoffDelay` keeps returning
a short fixed delay (60s) instead of falling back to `reloadInterval`.
The same problem existed for 5xx errors, where the delay grew to 8× the
configured interval.

Inspired by #4110 (thanks @CodeLine9), this PR makes both error paths
fall back to `reloadInterval` after retries are exhausted. I also
simplified the catch block, extracted a `#shortenUrl()` helper for log
messages, and added tests for the backoff progression.
2026-04-27 23:01:42 +02:00
Kristjan ESPERANTO
3f2a0302eb ci(actions): set explicit token permissions (#4114)
Add minimal GITHUB_TOKEN permissions to electron-rebuild workflow to
enforce least privilege and satisfy CodeQL alert
[#14](https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning/14).
2026-04-19 19:36:39 +02:00
Kristjan ESPERANTO
de3f57f8af weather: fix UV index display and add WeatherFlow precipitation (#4108)
While looking at the WeatherFlow provider (to evaluate #4107), I noticed
a few things that weren't quite right.

1. **UV index was broken for most providers in forecast/hourly views.**
The templates read `uv_index`, but only the WeatherAPI provider actually
wrote that key. All other providers (OpenWeatherMap, WeatherFlow,
PirateWeather, etc.) use `uvIndex` - so UV was silently never displayed
for them. This went unnoticed because `showUVIndex` defaults to `false`
and there were no test assertions for it. Standardized everything on
`uvIndex` and added test coverage.

2. **WeatherFlow didn't map precipitation for current weather.** The API
provides `precip_accum_local_day` and `precip_probability`, but they
weren't passed through. While adding them I also noticed the template
used truthiness checks, which hid valid zero values. Fixed both.

3. **`||` vs `??` in WeatherFlow provider.** Several numeric fields used
`|| null`, replacing valid `0` with `null`. Switched to `??` for
correctness.
2026-04-19 12:50:20 +02:00
Karsten Hassel
d3a3ad9caf config endpoint must handle functions in module configs (#4106)
Fixes #4105

```bash
In JavaScript, standard JSON does not support functions.
If you use JSON.stringify() on an object containing functions,
those functions will be omitted (if they are object properties)
or changed to null (if they are in an array).
```

---------

Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
2026-04-12 15:50:00 -05:00
Karsten Hassel
61870ae623 fix replaceSecretPlaceholder (#4104)
Disable secret substitution only for cors=allowAll, the last attempt in
#4102 was to restrictive.

Secret substitution should also work if cors=disabled.

---------

Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
2026-04-12 00:46:10 +02:00
Karsten Hassel
39578d69b4 restrict replaceSecretPlaceholder to cors with allowWhitelist (#4102)
and cleanup spelling
2026-04-10 23:11:57 +02:00
Kristjan ESPERANTO
22a58d4ae5 fix(weather): restore OpenWeatherMap v2.5 support (#4101)
After the big weather refactor (#4032), OpenWeatherMap was effectively
hard-wired to One Call v3. One Call 2.5 is deprecated and no longer
available, so it looked like v2.5 support was effectively over — but the
classic `/weather` and `/forecast` endpoints were never actually
dropped. This restores support for those.

Fixes #4100.

## What this PR does
- handles OpenWeatherMap responses by endpoint again (`/onecall`,
`/weather`, `/forecast`)
- restores v2.5 current and forecast support (including hourly via
3-hour forecast slots)
- filters outdated hourly entries centrally while keeping the current
hour visible (if available)

## Screenshot
<img width="768" height="481" alt="bildo"
src="https://github.com/user-attachments/assets/9bce3531-3731-4fd7-b41e-e20603afa725"
/>
2026-04-10 22:20:41 +02:00
Kristjan ESPERANTO
2e97e29ab5 fix(http_fetcher): use undici.fetch when dispatcher is present (#4097)
### What's the problem?

The `selfSignedCert` option passes an undici `Agent` as `dispatcher` to
`fetch()`. But Node's built-in `fetch()` and undici@8's `Agent` use
different internal handler APIs - passing them together throws:

```
invalid onRequestStart method
```

### What's the fix?

When `selfSignedCert` is enabled (i.e. a `dispatcher` is set), use
undici's own `fetch()` instead of the global one. For all other
requests, keep using `globalThis.fetch`.

```js
const fetchFn = requestOptions.dispatcher ? undiciFetch : globalThis.fetch;
```

### Why not just always use undici's fetch?

That would fix the crash - but it would break some tests. MSW (Mock
Service Worker), which is used in our test suite to intercept HTTP
requests, only hooks into `globalThis.fetch`. Undici's fetch bypasses
those interceptors entirely, so tests would start making real network
requests instead of getting the mocked responses. We could rewrite all
tests to use undici-compatible mocking instead - but that would be a
massive change for no real benefit.

----

Fixes #4093
2026-04-08 18:42:30 +02:00
Kristjan ESPERANTO
d8c29d5ec3 fix: prevent crash when config is undefined in socket handler (#4096)
If a module uses this.io.of() to register a custom socket.io namespace,
connections on that namespace trigger the onAny handler in setSocketIO
before config is set, causing a TypeError.

Fixes #4089
2026-04-07 21:15:25 +02:00
Karsten Hassel
9b97add1ae fix cors function for alpine linux (#4091)
I provide docker images with alpine linux and tested the new cors
approach.

It didn't work because after calling

```js
  const dispatcher = new Agent({ connect: { lookup: (_h, _o, cb) => cb(null, address, family) } });
```

the dispatcher variable was undefined.

This PR solves this and I tested this under debian too.

The mix of internal fetch and newer undici did not work and alpine needs
additionally the `process.nextTick`.

---------

Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
2026-04-05 21:42:32 +02:00
Kristjan ESPERANTO
0da343b961 fix(weather): use stable instanceId to prevent duplicate fetchers (#4092)
During the server-side migration (PR #4032), the `instanceId` was built
with `Date.now()`, making it unique per client reload. This approach was
fine in the old browser-based architecture where reloads cleaned up
everything automatically. But now the node_helper persists across
reloads, so each reconnect created a new `HTTPFetcher` while the old one
kept polling - silently multiplying API calls over time.

The fix is simple: use `this.identifier` as the `instanceId`, which is
already stable and unique per module slot. This is the same approach the
Calendar module uses.

On the node_helper side, when a provider already exists for the same
`instanceId`, we now skip re-creation and just resend
`WEATHER_INITIALIZED` so the client picks up where it left off.

Fixes https://forum.magicmirror.builders/topic/20199
2026-04-05 21:42:02 +02:00
Kristjan ESPERANTO
96c18ec8b0 fix(cors): prevent SSRF via DNS rebinding (#4090)
PR #4084 blocked SSRF by checking the IP before `fetch()` — but
`fetch()` resolves DNS again on its own. With DNS rebinding (TTL=0,
alternating IPs) an attacker can slip a private IP through between check
and connection.

Fix: resolve DNS once, validate, pin the validated IP for the
connection.

No second DNS query → no rebinding window. `isPrivateTarget()` is gone,
code is shorter than before.

Not a likely attack for a typical MagicMirror setup, but it doesn't add
complexity so there's no reason not to close the gap.
2026-04-04 20:40:14 +02:00
Kristjan ESPERANTO
f079324426 ci(codeql): also scan develop branch on push and PR (#4086)
Previously only master was scanned via the default CodeQL setup. Since
development happens on develop, this PR replaces the default setup with
a custom workflow that covers both branches. This gives an overview of
the security status across the current release (master) and the
development branch (develop).

As a result we should also see issues in the develop branch here:
https://github.com/MagicMirrorOrg/MagicMirror/security/code-scanning
2026-04-04 16:39:53 +02:00