# Learning Plugin Development

**URL:** <https://community.volumio.com/t/learning-plugin-development/75113>\
**Category:** Plugins\
**Tags:** question\
**Created:** [January 3, 2026, 3:53am UTC](https://community.volumio.com/t/learning-plugin-development/75113 "2026-01-03T03:53:12Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [January 3, 2026, 3:53am UTC](https://community.volumio.com/t/learning-plugin-development/75113/1 "2026-01-03T03:53:13Z")

</div>

Hi all,

I am creating this topic to put in one place, all the beginner - noob questions that we might have.

That way, the questions and answers will all be in one place for someone else like me.

Please add your noob questions to this topic as well ( as suggested by @nerd )

---

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [January 3, 2026, 3:54am UTC](https://community.volumio.com/t/learning-plugin-development/75113/2 "2026-01-03T03:54:28Z")

</div>

These are the previous 2 questions I posted before starting this topic.

[How to get a setting value from volumio’s internal settings from within a custom plugin code? - Plugins - Volumio Community](https://community.volumio.com/t/how-to-get-a-setting-value-from-volumios-internal-settings-from-within-a-custom-plugin-code/74998)

[pushState Listener triggered multiple times - Plugins - Volumio Community](https://community.volumio.com/t/pushstate-listener-triggered-multiple-times/75096)

---

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [January 3, 2026, 3:56am UTC](https://community.volumio.com/t/learning-plugin-development/75113/3 "2026-01-03T03:56:56Z")

</div>

Hi @nerd

I tried switching off the pushState listener in the onStop function as you suggested.

But the listener is still triggered multiple times.

If I account for this repeat triggers in code, my plugin works as expected.

Thanks.

NOTE: I am currently on Volumio 4 for Raspberry Pi and have the following plugins installed - Auto Volume (my plugin), System Information, Now Playing, Touch Display

---

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [December 23, 2025, 7:33pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/4 "2025-12-23T19:33:17Z")

</div>

I am developing a plugin to control the volume of each song being played.

For that I need to limit the range of values for a config to be below volumio’s “Max Volume Level” setting (Playback Options → Volume Options → Max Volume Level).

How do I query this value from within my plugin’s code and set my UIconfig range accordingly?

Thanks to everyone active in the volumio community.

---

<div class="post-metadata">

**Author:** ![nerd](https://community.volumio.com/user_avatar/community.volumio.com/nerd/32/23615_2.png) [@nerd](https://community.volumio.com/u/nerd)\
**Post date:** [December 25, 2025, 7:20am UTC](https://community.volumio.com/t/learning-plugin-development/75113/5 "2025-12-25T07:20:36Z")

</div>

Hey @netrikkann,

Query the Max Volume Level setting from your plugin code:

```javascript
var maxVolume = this.commandRouter.executeOnPlugin('audio_interface', 'alsa_controller', 'getConfigParam', 'volumemax');

```

The setting is stored as ‘volumemax’ in the alsa\_controller plugin configuration file at:  
/data/configuration/audio\_interface/alsa\_controller/config.json

To dynamically set your UIConfig element range in getUIConfig(), modify the element’s max attribute before resolving:

```javascript
uiconf.sections[X].content[Y].config.bars[Z].max = maxVolume;

```

Or for a simple input with attributes:

```javascript
uiconf.sections[X].content[Y].attributes = [{'max': maxVolume}];

```

Kind Regards,

---

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [December 25, 2025, 1:56pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/6 "2025-12-25T13:56:36Z")

</div>

Hi @nerd

Merry Christmas!

Thanks a lot for the clarification. 🙏 That makes my work a lot simpler.

I am doing plugin development for the first time. The more you learn the more fun it becomes. 👌

Just one more query 🤔 :  
How do I dynamically limit the max setting in my config page, if my volume values are select-options type?

Thanks

---

<div class="post-metadata">

**Author:** ![nerd](https://community.volumio.com/user_avatar/community.volumio.com/nerd/32/23615_2.png) [@nerd](https://community.volumio.com/u/nerd)\
**Post date:** [December 25, 2025, 6:21pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/7 "2025-12-25T18:21:34Z")

</div>

Hey @netrikkann,

For select-options type, filter the options array in getUIConfig() before resolving:

```javascript
ControllerYourPlugin.prototype.getUIConfig = function() {
    var defer = libQ.defer();
    var self = this;

    var lang_code = this.commandRouter.sharedVars.get('language_code');

    self.commandRouter.i18nJson(__dirname+'/i18n/strings_'+lang_code+'.json',
        __dirname+'/i18n/strings_en.json',
        __dirname + '/UIConfig.json')
        .then(function(uiconf)
        {
            // Get system max volume
            var maxVolume = self.commandRouter.executeOnPlugin('audio_interface', 'alsa_controller', 'getConfigParam', 'volumemax');

            // Filter options to only include values <= maxVolume
            var allOptions = uiconf.sections[X].content[Y].options;
            var filteredOptions = allOptions.filter(function(opt) {
                return parseInt(opt.value) <= parseInt(maxVolume);
            });
            uiconf.sections[X].content[Y].options = filteredOptions;

            // Set current value
            uiconf.sections[X].content[Y].value = self.config.get('yourConfigKey');

            defer.resolve(uiconf);
        })
        .fail(function(error)
        {
            self.logger.error('Failed to parse UI Configuration: ' + error);
            defer.reject(new Error());
        });

    return defer.promise;
};

```

Replace X and Y with the correct section and content indices for your select element.

Multiple community plugins already use executeOnPlugin to query alsa\_controller settings. Study their index.js files for practical examples:

- rotaryencoder2
- ir\_controller
- ampswitch
- gpio\_control

Repository: [GitHub - volumio/volumio-plugins-sources-bookworm](https://github.com/volumio/volumio-plugins-sources-bookworm)

Kind Regards,

---

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [December 25, 2025, 7:11pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/8 "2025-12-25T19:11:52Z")

</div>

Hi @nerd

Thanks for the quick reply.

Will look into your directions right away.

---

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [January 1, 2026, 12:18pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/9 "2026-01-01T12:18:03Z")

</div>

I am trying to listen to **pushState** events in my plugin code. for now I am listening and logging.

I register my listener on the onStart function. But every time the state changes the listener is triggered multiple times instead of once.

Can someone tell me what is going wrong here?

```auto
// Plugin has started
AutoVol.prototype.onStart = function() {
    const self = this;
	
	const defer=libQ.defer();

	self.astart = Date.now();

	socket.emit("getState", "");

	socket.on("pushState", (state) => {
		self.statusChanged(state);
	});

	// Once the Plugin has successfull started resolve the promise
	defer.resolve();

    return defer.promise;
};

```

 ![Log](https://community.volumio.com/uploads/default/original/3X/7/3/738857e096aec08de4e53a6f8197497b858a8e96.png)

---

<div class="post-metadata">

**Author:** ![nerd](https://community.volumio.com/user_avatar/community.volumio.com/nerd/32/23615_2.png) [@nerd](https://community.volumio.com/u/nerd)\
**Post date:** [January 1, 2026, 4:04pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/10 "2026-01-01T16:04:52Z")

</div>

Hey @netrikkann,

The issue is listener accumulation. Each time onStart runs, you add another socket.on listener without removing previous ones. After plugin restarts or multiple starts, you end up with duplicate listeners all firing on the same event.

Fix: Remove the listener in onStop.

```javascript
AutoVol.prototype.onStart = function() {
    const self = this;
    const defer = libQ.defer();

    self.astart = Date.now();

    // Store reference to the bound function for later removal
    self.pushStateHandler = function(state) {
        self.statusChanged(state);
    };

    socket.emit("getState", "");
    socket.on("pushState", self.pushStateHandler);

    defer.resolve();
    return defer.promise;
};

AutoVol.prototype.onStop = function() {
    const self = this;
    const defer = libQ.defer();

    // Remove the listener
    socket.off("pushState", self.pushStateHandler);

    defer.resolve();
    return defer.promise;
};

```

Key points:

- Store the handler function reference in self.pushStateHandler
- Use socket.off() in onStop to remove that specific listener
- This prevents listener accumulation across plugin restarts

Alternative: Use socket.once() if you only need the state once, but for continuous monitoring you need the on/off pattern shown above.

Kind Regards,

---

<div class="post-metadata">

**Author:** ![nerd](https://community.volumio.com/user_avatar/community.volumio.com/nerd/32/23615_2.png) [@nerd](https://community.volumio.com/u/nerd)\
**Post date:** [January 1, 2026, 4:11pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/11 "2026-01-01T16:11:13Z")

</div>

@netrikkann,

One request: please consolidate your plugin development questions into a single thread - something like **“Learning Plugin Development”** or similar. It keeps related discussion together and helps others learning the same things find the answers more easily.

Kind Regards,

---

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [August 23, 2026, 5:29pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/12 "2026-08-23T17:29:38Z")

</div>

Hi all ( ping to @nerd ),

My plugin generates and stores an sqlite database in its plugin specific folder.

Is it possible to upload and download the db file using the plugin’s UI?

If not, can I click a button in the plugin’s UI to upload or download the db file to some cloud storage or as a last resort to the connected NAS?

Thanks for reading.

---

<div class="post-metadata">

**Author:** ![nerd](https://community.volumio.com/user_avatar/community.volumio.com/nerd/32/23615_2.png) [@nerd](https://community.volumio.com/u/nerd)\
**Post date:** [August 23, 2026, 6:15pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/13 "2026-08-23T18:15:51Z")

</div>

Hey @netrikkann,

Depending on your requirements, how db is handled - as shipped vs remote - example loader for json type in Pi Screen Setup Plugin:

> **[volumio-plugins-sources-bookworm/pi\_screen\_setup at master ·...](https://github.com/volumio/volumio-plugins-sources-bookworm/tree/master/pi_screen_setup)**
>
> Contribute to volumio/volumio-plugins-sources-bookworm development by creating an account on GitHub.

Or setting webexpress server with custom interface complete files management in FM/DAB Radio Plugin:

> **[volumio-plugins-sources-bookworm/rtlsdr\_radio at master ·...](https://github.com/volumio/volumio-plugins-sources-bookworm/tree/master/rtlsdr_radio)**
>
> Contribute to volumio/volumio-plugins-sources-bookworm development by creating an account on GitHub.

I am sure there are more examples available, including these storing plugin files directly in “INTERNAL” storage exposed via smb server.

Kind Regards,

---

<div class="post-metadata">

**Author:** ![netrikkann](https://community.volumio.com/letter_avatar_proxy/v4/letter/n/ebca7d/32.png) [@netrikkann](https://community.volumio.com/u/netrikkann)\
**Post date:** [August 24, 2026, 12:24pm UTC](https://community.volumio.com/t/learning-plugin-development/75113/14 "2026-08-24T12:24:03Z")

</div>

Hi @nerd , Thanks!

Will look into it.
