New Plugin: Mopidy Raspberry GPIO

I know Mopidy-GPIOTTS exists, but we wanted a cleaner, more flexible plugin for supplying GPIO input to Mopidy. Instead, I’ve created this mess-

I’m interested in any feedback, but I’d like to also ask a question of Mopidy devs- is it possible to have a config option defined in the “schema” but not be required in mopidy.conf?

I ask because I’d like the user to be able to configure the plugin using lines like bcm5 = play_pause,active_low,30, but don’t want to have an empty line for every single GPIO pin to avoid config warnings.

Yes. In the schema definition set optional=True.

@kingosticks as near as I can tell optional=True only makes the existence of a value optional, and doesn’t allow the user to exclude that config value from mopidy.conf altogether. This ran counter to my expectations, but a glance through the code revealed why it might be the case. The optional=True value only affects the return value from a schema type and doesn’t affect whether that config value can be optionally omitted.

The parsing happens here:

Effectively this does two things:

  1. Checks through every value in the config file and raises errors for any that do not match configuraiton values that Mopidy (or its plugins) know about. It will also try to match mistyped config values to existing ones- neat!

  2. Checks through every config value that Mopidy expects and - if it has not been found in step 1 - produces a `‘config key not found’ warning. It appears that any warnings of this type cause the extension to be disabled- presumably because there’s no internal case for handling default config values.

In my case I have code like the following:

    def get_config_schema(self):
        schema = super(Extension, self).get_config_schema()
        for pin in range(28):
            schema["bcm{:d}".format(pin)] = PinConfig()
        return schema

This effectively tells the config parser to allow options bcm0 through bcm27 and treat their values as a PinConfig type. The upshot of this is that my mopidy.config entries have to look like this so it doesn’t break:

enabled = true
bcm0 =
bcm1 =
bcm2 =
bcm3 =
bcm4 = play_pause,active_low,30
bcm5 = volume_up,active_low,30
bcm6 = volume_down,active_low,30
bcm7 =
bcm8 =
bcm9 =
bcm10 =
bcm11 =
bcm12 =
bcm13 =
bcm14 =
bcm15 =
bcm16 =
bcm17 =
bcm18 =
bcm19 =
bcm20 =
bcm21 =
bcm22 =
bcm23 =
bcm24 =
bcm25 =
bcm26 =
bcm27 =

I might be missing something, but I’d usually expect known-good-defaults to be baked-in to the plugin using config.get(key, default) so that settings can be omitted from the config file unless they’re strictly necessary.

Step 2. might be expanded to check if the type is optional and allow the ommission of the key from mopidy.conf:

for key in self.keys():
    if isinstance(self[key], types.Deprecated):
        result.pop(key, None)
    elif key not in result and key not in errors and not is_optional(self[key]):
        result[key] = None
        errors[key] = 'config key not found.'

Where is_optional, by whatever mechanism deemed sensible, returns True is this key is of an optional type.

If the fields are defined in ext.conf and made optional the user does not have to set them in their mopidy.conf. Known defaults should be baked into ext.conf.

Ah this appears to be one of those cases where I’ve been too “smart” for my own good and read the code instead of the documentation! I’ll give it a try, thanks!