Overview
Overview
The NodeJS plugin for Rainmeter allows you to harness the full power of Node.js directly within your skins. It acts as a bridge, enabling you to execute JavaScript files, run asynchronous operations, and interact with the Rainmeter API from a Node.js environment. This opens up a world of possibilities, from fetching data from complex APIs to performing heavy computations without freezing your desktop.
Key Features
-
Full Node.js Environment: Use any Node.js module
(e.g.,
fs
,axios
,path
) in your scripts. - Asynchronous Execution: Run long-running tasks in the background without impacting Rainmeter's performance.
- Two-Way Communication: Call Rainmeter API functions from your Node.js script and get results back.
-
Inline & File-Based Scripts: Write simple scripts
directly in your .ini file or link to external
.js
files for more complex logic. -
Dynamic Function Calls: Execute specific
JavaScript functions on demand using
!CommandMeasure
.
Installation
Requirements
- Rainmeter 4.3 or higher.
-
Node.js
installed on your system. The plugin executes the
node
command, so it must be in your system's PATH.
Steps
-
Download: Get the latest
.rmskin
package from the Download section. -
Install: Double-click the
.rmskin
file to install the plugin using the Rainmeter Skin Installer. -
Verify: After installation, the
NodeJS.dll
file should be located in your Rainmeter plugins directory (e.g.,C:\Users\YourName\Documents\Rainmeter\Skins\YourSkin\@Resources\Plugins
orC:\Program Files\Rainmeter\Plugins
).
Basic Usage
Here’s a simple example of a skin that uses the NodeJS plugin to display the current Node.js version.
1. The .ini Skin File
This measure calls the version.js
script.
[Rainmeter]
Update=1000
[Metadata]
Name=Node JS Version
Author=nstechbytes
Information=Sample skin demonstrating the NodeJS plugin
Version=v1.0
[MeasureNodeVersion]
Measure=Plugin
Plugin=NodeJS.dll
ScriptFile=#@#scripts/version.js
DynamicVariables=1
[Rectangle_Shape]
Meter=Shape
Shape=Rectangle 0,0,410,200,8 | StrokeWidth 0 | FillColor 255,255,255,150
[MeterNodeVersion]
Meter=String
MeasureName=MeasureNodeVersion
Text="Node.js Version: %1"
StringAlign=CenterCenter
FontSize=15
FontWeight=900
FontColor=10,10,10
AntiAlias=1
X=(410/2)
Y=(200/2)
2. The JavaScript File (scripts/version.js)
This script defines an update()
function that returns
the Node.js version string.
function update() {
// process is a global Node.js object
return process.version;
}
module.exports = {
update
};
Options / Parameters
These are the options available for a plugin measure.
Plugin
- Should be set to
NodeJS.dll
. ScriptFile
-
The path to your external
.js
file. Relative paths using#@#
are recommended. Line
- The first line of an inline JavaScript script. Use this for very short, simple scripts instead of an external file.
-
Line2
,Line3
, ... -
Subsequent lines for an inline script. The plugin will read
LineN
options sequentially until it finds one that is not defined. DynamicVariables
-
Set to
1
to allow the use of section variables in the measure's string value.
Important Note on require()
The Node.js require()
function is fully supported
when using ScriptFile
. This allows you to import npm modules (like axios
,
fs
, etc.). To use an npm module, you must first
install it in the same directory as your script file (e.g., by
running npm install axios
in your script's folder,
which will create a node_modules
directory). However,
require()
is not available for
inline scripts defined with the Line
,
Line2
, etc. options.
Core Functions
The plugin looks for specific exported functions within your JavaScript file or inline script. All functions can be synchronous or asynchronous (return a Promise).
initialize()
-
Called once when the measure is first loaded or refreshed. Useful for setting up initial state or performing one-time setup tasks. The value returned will be set as the measure's initial value.
function initialize() { console.log('Script Initialized!'); return 'Ready'; }
update()
-
Called on every skin update cycle. This is the main function for fetching and returning data. The returned value (string or number) becomes the measure's new value.
let count = 0; function update() { count++; return `Update count: ${count}`; }
YourCustomFunction()
-
Any other exported function can be called on demand using bangs. This is powerful for triggering actions without waiting for an update cycle.
function sayHello(name) { return `Hello, ${name}!`; } module.exports = { sayHello };
You can call this with a bang:
LeftMouseUpAction=[!CommandMeasure MeasureName "sayHello('World')"]
RM API (in JS)
The plugin exposes a global RM
object in your Node.js
script, allowing you to interact with the Rainmeter API. This
enables powerful two-way communication where your script can read
skin variables, execute bangs, and get information about the skin
environment.
Available RM Functions
All functions that read data from Rainmeter are synchronous from the perspective of your script; the plugin handles the two-way communication with the host process.
RM.Execute(command)
- Executes a Rainmeter bang. This is a fire-and-forget action.
-
Parameters:
command
(string) - The bang to execute (e.g.,'!Log "Hello"'
,'!ToggleMeter "MyMeter"'
). -
RM.GetVariable(name, defaultValue)
- Gets the value of a Rainmeter variable.
-
Parameters:
name
(string) - The name of the variable to get.defaultValue
(string, optional) - The value to return if the variable doesn't exist. - Returns: (string) The variable's value.
-
RM.ReadString(option, defaultValue)
- Reads a string value from the current measure's section in the .ini file.
-
Parameters:
option
(string) - The key to read.defaultValue
(string, optional) - The default value. - Returns: (string) The option's value.
-
RM.ReadDouble(option, defaultValue)
- Reads a numeric value from the current measure's section.
-
Parameters:
option
(string) - The key to read.defaultValue
(number, optional) - The default value. - Returns: (number) The option's value, parsed as a float.
-
RM.ReadInt(option, defaultValue)
- Reads an integer value from the current measure's section.
-
Parameters:
option
(string) - The key to read.defaultValue
(number, optional) - The default value. - Returns: (number) The option's value, parsed as an integer.
-
RM.ReadStringFromSection(section, option, defaultValue)
- Reads a string value from a different section in the .ini file.
-
Parameters:
section
(string) - The section to read from.option
(string) - The key to read.defaultValue
(string, optional) - The default value. - Returns: (string) The option's value.
RM.GetMeasureName()
- Gets the name of the current measure.
- Returns: (string) The measure's name.
RM.GetSkinName()
- Gets the name of the current skin.
- Returns: (string) The skin's name.
RM.GetSkin()
- Gets a handle (a unique identifier) for the current skin.
- Returns: (string) The skin handle.
RM.GetSkinWindow()
- Gets a handle (a unique identifier) for the current skin's window.
- Returns: (string) The skin window handle.
Section Variables
A powerful feature of the plugin is the ability to call a JavaScript function directly from within a meter's options using a special section variable syntax. This allows you to get dynamic values without needing bangs or separate measures.
The syntax is:
[&MeasureName:Call("functionName(arguments)")]
When Rainmeter parses the meter, it will execute the specified
JavaScript function from the script associated with
MeasureName
and substitute the returned value directly
into the option.
Example: Direct Function Call in a Meter
This example demonstrates calling a sum()
function from
an inline script and displaying the result directly in a String
meter.
[Rainmeter]
Update=8000
[Metadata]
Name=Sum
Author=nstechbytes
Information=Sample skin demonstrating the NodeJS plugin
Version=v1.0
License=MIT
[MeasureNodeJS]
Measure=Plugin
Plugin=NodeJS
Line=function sum() {
Line2=return 5 + 2;
Line3=}
Line4=module.exports = { sum };
[Rectangle_Shape]
Meter=Shape
Shape=Rectangle 0,0,310,100,8 | StrokeWidth 0 | FillColor 255,255,255,150
[MeterText]
Meter=String
MeasureName=MeasureNodeJS
Text=Sum of 5+2 is: [&MeasureNodeJS:Call("sum(5,2)")]
X=(310/2)
Y=(100/2)
StringAlign=CenterCenter
FontSize=15
FontColor=10,10,10
ClipString=2
AntiAlias=1
DynamicVariables=1
In this case, [&MeasureNodeJS:Call("sum(5,2)")]
is
executed, the sum(5, 2)
function in the script runs and
returns 7
. The final text of the meter becomes "Sum of
5+2 is: 7".
Examples
Example 1: Asynchronous API Fetch
This example fetches a random joke from an API. Because it's an async operation, it doesn't freeze Rainmeter while waiting for the network response.
joke.ini
[Rainmeter]
Update=5000
DynamicWindowSize=1
SolidColor=255,255,255
BackgroundMode=2
[Metadata]
Name=NodeJS Plugin Sample
Author=nstechbytes
Information=Sample skin demonstrating the NodeJS plugin
Version=
License=
[MeasureJoke]
Measure=Plugin
Plugin=NodeJS.dll
ScriptFile=#@#scripts/joke.js
DynamicVariables=1
[MeterJoke]
Meter=String
MeasureName=MeasureJoke
W=300
H=100
ClipString=1
FontSize=12
FontColor=10,10,10
Text="%1"
AntiAlias=1
scripts/joke.js
You'll need to install axios
by running
npm install axios
in your script's directory.
const axios = require('axios');
function initialize() {
return 'Joke API Initialized';
}
async function update() {
try {
const response = await axios.get('https://v2.jokeapi.dev/joke/Any?type=single');
if (response.data && !response.data.error) {
return response.data.joke;
}
return 'Could not fetch joke.';
} catch (error) {
console.error('Joke API Error:', error.message);
return 'API request failed.';
}
}
module.exports = {
update,
initialize
};
Example 2: Inline Script with Bangs
This example uses an inline script to manage a simple counter. Bangs are used to increment, decrement, and reset the counter.
counter.ini
[Rainmeter]
Update=100
[Metadata]
Name=Counter
Author=nstechbytes
Information=Sample skin demonstrating the NodeJS plugin
Version=v1.0
License=MIT
[MeasureCounter]
Measure=Plugin
Plugin=NodeJS.dll
Line=let count = 20;
Line2=function incrementValue() { count++; return count; }
Line3=function decrementValue() { count--; return count; }
Line4=function initialize() { console.log("initializing"); count++; return count; }
Line5=module.exports = { initialize, incrementValue, decrementValue };
[Rectangle_Shape]
Meter=Shape
Shape=Rectangle 0,0,300,100,8 | StrokeWidth 0 | FillColor 255,255,255,150
[MeterCounter]
Meter=String
MeasureName=MeasureCounter
W=300
H=100
X=(200/2)
Y=(100/2)
StringAlign=CenterCenter
FontSize=18
FontWeight=900
FontColor=10,10,10
Text=Count: %1
AntiAlias=1
[MeterIncrement]
Meter=String
Text="+"
W=20
H=20
X=250
Y=-10r
SolidColor=109,240,9,255
FontColor=255,255,255
FontSize=15
StringAlign=CenterCenter
LeftMouseUpAction=[!CommandMeasure MeasureCounter "incrementValue()"]
[MeterDecrement]
Meter=String
Text="-"
W=20
H=20
X=250
Y=2R
StringAlign=CenterCenter
SolidColor=207,12,12,255
FontColor=255,255,255
FontSize=15
LeftMouseUpAction=[!CommandMeasure MeasureCounter "decrementValue()"]
Error Handling
The plugin is designed to report errors clearly in the Rainmeter log.
- Plugin Errors: Issues like "Node.js not found" or "ScriptFile not specified" will be logged by the plugin itself.
-
Script Errors: Syntax errors or runtime
exceptions in your JavaScript code are caught and logged. The
plugin uses
console.error
in the wrapper script to pipe these messages to the Rainmeter log with a(Error)
prefix. -
Logging: You can use
console.log()
,console.warn()
, andconsole.error()
in your script to send messages to the Rainmeter log. They will appear with (Notice), (Warning), and (Error) prefixes, respectively.
// This will appear in the Rainmeter log
console.log('This is a standard log message.');
console.warn('This is a warning.');
console.error('This is an error message.');
Advanced Usage
Calling Functions with Arguments
You can pass arguments to your custom functions via
!CommandMeasure
. Ensure your arguments are properly quoted
if they contain spaces.
LeftMouseUpAction=[!CommandMeasure MyMeasure "myFunction('some value', 123)"]
Working Directory
When you use ScriptFile
, the working directory for the
Node.js process is set to the directory containing that script. This
means you can easily use relative paths to require other modules or
read local files (e.g., require('./lib/helper.js')
).
Asynchronous Initialization
The initialize
function can also be async. The plugin
will wait for the promise to resolve before proceeding. This is
useful for setting up connections or loading data that is needed
before the first update.
Limitations
- Node.js Dependency: The user must have Node.js installed and accessible via their system's PATH.
- Performance: While the plugin is efficient, running very complex, CPU-intensive scripts on every update can still impact system performance. Use asynchronous operations for heavy tasks.
- Process Overhead: Each plugin measure starts a Node.js process. While lightweight, having hundreds of measures could increase memory usage.
-
Inline Script Size: Inline scripts are practical
for small snippets. For anything complex,
ScriptFile
is the recommended approach. Rainmeter has a limit on the length of an option value.
FAQ
-
Q: Can I use ES Modules (
import
/export
)? -
A: The plugin currently uses a CommonJS
(
require
/module.exports
) wrapper. While you might be able to use libraries that transpile ES Modules, the native entry point for your script must be CommonJS. - Q: How do I debug my scripts?
-
A: Your primary debugging tool is the Rainmeter log. Use
console.log()
andconsole.error()
liberally in your script to trace execution and inspect variables. You can also run your script directly withnode yourscript.js
in a terminal to catch errors outside of the Rainmeter environment. - Q: Why is my async function not updating the meter?
-
A: The
update
function is called asynchronously in the background. The result of its promise is used to update the measure's value on a subsequent skin cycle. There may be a one-cycle delay before the value appears. Ensure your async function actually returns a value or resolves a promise with a value.
Download
You can download the latest version of the NodeJS plugin from the official GitHub repository or the Rainmeter forums (links to be added).
Download Latest .rmskinLicense & Credits
Author: nstechbytes
License: MIT License
This plugin was built using the official Rainmeter Plugin SDK. Special thanks to the Rainmeter community for their support and resources.