Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.js
*.mjs
*.mjs
CHANGELOG.md
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

All notable changes to this project will be documented in this file.

## [0.7.2](https://github.com/shbatm/MMM-Carousel/compare/v0.7.1...v0.7.2) (2026-01-01)

### Bug Fixes

- resolve positional mode only rotating last position ([effa34d](https://github.com/shbatm/MMM-Carousel/commit/effa34d17d1ccea774a2eebaa7ecdf58f83a6e82))

### Chores

- add demo configs ([347779a](https://github.com/shbatm/MMM-Carousel/commit/347779a0af6a7ede25627eabcc852b1d0005b009))
- **deps:** bump actions/checkout from 5 to 6 ([50bc8bf](https://github.com/shbatm/MMM-Carousel/commit/50bc8bf7649e8f495b3800983a0ef6fe553a17f1))

## [0.7.1](https://github.com/shbatm/MMM-Carousel/compare/v0.7.0...v0.7.1) (2026-01-01)

### Documentation
Expand Down
62 changes: 55 additions & 7 deletions MMM-Carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ Module.register("MMM-Carousel", {
Log.info("[MMM-Carousel] Switched to manual mode - stopping automatic rotation");
this.updatePause(true);
if (this.transitionTimer) {
clearInterval(this.transitionTimer);
clearTimeout(this.transitionTimer);
this.transitionTimer = null;
}
Expand Down Expand Up @@ -357,15 +358,49 @@ Module.register("MMM-Carousel", {
*/
setUpTransitionTimers (positionIndex) {
const modules = this.getFilteredModules(positionIndex);
this.modulesContext = this.buildModulesContext(modules);
const ctx = this.buildModulesContext(modules);
ctx.positionIndex = positionIndex;
ctx.transitionInterval = this.getTransitionTimer(positionIndex);

// Initial transition
this.moduleTransition();
if (positionIndex === null) {
// Global/slides mode: single context
this.modulesContext = ctx;

// Create bound function for manual/timed transitions
this.manualTransition = (goToIndex, goDirection, goToSlide) => {
this.moduleTransition(goToIndex, goDirection, goToSlide);
};
// Initial transition
this.moduleTransition();

// Create bound function for manual/timed transitions
this.manualTransition = (goToIndex, goDirection, goToSlide) => {
this.moduleTransition(goToIndex, goDirection, goToSlide);
};
} else {
/*
* Positional mode: use closure to capture ctx for this position
* Each position gets its own timer with its own context
*/
const transitionFn = () => {
const moduleCount = ctx.modules.length;
ctx.currentIndex = (ctx.currentIndex + 1) % moduleCount;

Log.debug(`[MMM-Carousel] Position ${positionIndex}: transitioning to index ${ctx.currentIndex}`);

// Hide all, then show current
for (const mod of ctx.modules) {
mod.hide(ctx.slideFadeOutSpeed, false, {lockString: "mmmc"});
}
setTimeout(() => {
ctx.modules[ctx.currentIndex].show(ctx.slideFadeInSpeed, false, {lockString: "mmmc"});
}, ctx.slideFadeOutSpeed);
};

// Initial transition
transitionFn();

// Start interval timer (captured in closure)
if (ctx.transitionInterval > 0) {
setInterval(transitionFn, ctx.transitionInterval);
}
}
},

/**
Expand Down Expand Up @@ -680,6 +715,14 @@ Module.register("MMM-Carousel", {

this.updatePause(false);

/*
* Positional mode uses setInterval which auto-restarts
* Only global/slides mode needs manual restart
*/
if (this.config.mode === "positional") {
return;
}

// Get current index from context
const currentIndex = this.modulesContext?.currentIndex || 0;
this.scheduleNextTransition(currentIndex);
Expand All @@ -691,6 +734,11 @@ Module.register("MMM-Carousel", {
return;
}

// Positional mode uses setInterval - pause/play not supported
if (this.config.mode === "positional") {
return;
}

// Check if a timer exists and toggle it
if (this.transitionTimer) {
// Timer is running - pause it
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ Please note that this project is released with a [Contributor Code of Conduct](C
### Developer commands

- `npm install` - Install dependencies like ESLint and prettier.
- `node --run demo:slides` - Start MagicMirror with the slides demo configuration.
- `node --run demo:positional` - Start MagicMirror with the positional demo configuration
- `node --run lint` - Run linting and formatter checks.
- `node --run lint:fix` - Fix linting and formatter issues.
- `node --run test` - Run linting and formatter checks + Run spelling check.
Expand Down
3 changes: 3 additions & 0 deletions cspell.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
"Kristjan",
"mmmc",
"newsfeed",
"updatenotification",
"openmeteo",
"evdev",
"Pagger",
"planetrise",
"PLAYPAUSE",
Expand Down
139 changes: 139 additions & 0 deletions demo.config.positional.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
const config = {
address: "0.0.0.0",
logLevel: [
"INFO",
"LOG",
"WARN",
"ERROR",
"DEBUG"
],
modules: [
{
module: "alert"
},
{
module: "updatenotification",
position: "top_bar"
},
{
module: "clock",
position: "top_left"
},
{
module: "calendar",
header: "US Holidays",
position: "top_left",
config: {
calendars: [
{
fetchInterval: 7 * 24 * 60 * 60 * 1000,
symbol: "calendar-check",
url: "https://ics.calendarlabs.com/76/mm3137/US_Holidays.ics"
}
]
}
},
{
module: "compliments",
position: "lower_third"
},
{
module: "weather",
position: "top_right",
config: {
weatherProvider: "openmeteo",
type: "current",
lat: 40.776676,
lon: -73.971321
}
},
{
module: "weather",
position: "top_right",
header: "Weather Forecast",
config: {
weatherProvider: "openmeteo",
type: "forecast",
lat: 40.776676,
lon: -73.971321
}
},
{
module: "newsfeed",
position: "bottom_bar",
config: {
feeds: [
{
title: "New York Times",
url: "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"
}
],
showSourceTitle: true,
showPublishDate: true,
broadcastNewsFeeds: true,
broadcastNewsUpdates: true
}
},
{
module: "MMM-Remote-Control",
disabled: true,
config: {
secureEndpoints: false
}
},
{
module: "MMM-SendNotificationButton",
position: "top_right"

},


{
module: "MMM-Carousel",
position: "bottom_bar",
config: {
transitionInterval: 10000,
showPageIndicators: false,
showPageControls: false,
ignoreModules: [
"MMM-Remote-Control",
"MMM-SendNotificationButton"
],
mode: "positional",
top_left: {
enabled: true,
ignoreModules: [],
overrideTransitionInterval: 8000
},
top_right: {
enabled: true,
ignoreModules: [],
overrideTransitionInterval: 12000
},
bottom_bar: {
enabled: true,
ignoreModules: [],
overrideTransitionInterval: 15000
},
keyBindings: {
enabled: true,
mode: "DEFAULT"
}
}
},
{
module: "MMM-KeyBindings",
config: {
enableKeyboard: true,
evdev: {
enabled: false
}
}
}
]
};

/** ************* DO NOT EDIT THE LINE BELOW */
if (typeof module !== "undefined") {
module.exports = config;
}
Loading