Skip to main content

Overview

GrandMA2 plugins consist of two files that work together: a .lua file containing the code and an .xml file that registers the plugin with the console. This guide covers the complete installation process.

Plugin File Structure

XML File Format

Every MA2 plugin requires an XML file that follows the GrandMA2 schema:
<?xml version="1.0" encoding="utf-8"?>
<MA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://schemas.malighting.de/grandma2/xml/MA" 
    xsi:schemaLocation="http://schemas.malighting.de/grandma2/xml/MA 
    http://schemas.malighting.de/grandma2/xml/3.0.187/MA.xsd" 
    major_vers="3" minor_vers="0" stream_vers="187">
  <Info datetime="2024-10-16T12:00:00" showfile="lua" />
  <Plugin index="2" execute_on_load="0" luafile="ColorPickerUpdate.lua" name="Color Picker Update"/>
</MA>

Key XML Attributes

The plugin slot number (1-1000). Each plugin needs a unique index in your showfile.
<Plugin index="10" ... />
Controls whether the plugin runs automatically when the showfile loads:
  • 0 = Manual execution only (recommended)
  • 1 = Auto-execute on showfile load
execute_on_load="0"
The filename of the associated Lua script (must match exactly).
luafile="PulseGenerator.lua"
The display name shown in the plugin pool.
name="Pulse Generator"

Installation Methods

1

Prepare the Files

Ensure you have both files:
  • PluginName.lua - The Lua script
  • PluginName.xml - The XML descriptor
The luafile attribute in the XML must exactly match the .lua filename.
2

Access the Plugin Path

On GrandMA2, the plugin directory location is stored in the system variable PLUGINPATH.
local pluginPath = gma.show.getvar('PLUGINPATH')..'/';
Typical paths:
  • Internal Drive: gma2/plugins/
  • USB Drive: /media/usb0/plugins/
3

Copy Files to Console

4

Import the Plugin

From the GrandMA2 command line:
Import "PluginName.xml" Plugin [index]
Example:
Import "ColorPickerUpdate.xml" Plugin 2
If a plugin already exists at that index, it will be overwritten without warning.
5

Verify Installation

  1. Open the Plugin Pool window
  2. Look for your plugin at the specified index
  3. The plugin name from the XML should be visible
  4. Double-click or press the plugin button to execute

Programmatic Plugin Creation

Plugins can create other plugins dynamically. Here’s the pattern from the source code:
function createPlugin(num, name, script, EOL)
  -- Execute On Load setting
  local EOLnum
  if EOL then EOLnum = 1 
  else EOLnum = 0 end
  
  -- Select internal drive
  cmd('SelectDrive 1')
  
  local plugin = {}
  plugin.name = 'tempfile_createplugin'
  plugin.directory = gma.show.getvar('PLUGINPATH')..'/'
  plugin.fullpathLUA = plugin.directory..plugin.name..'.lua'
  plugin.fullpathXML = plugin.directory..plugin.name..'.xml'

  -- Create XML content
  local xmlText = [[
<?xml version="1.0" encoding="utf-8"?>
<MA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://schemas.malighting.de/grandma2/xml/MA" 
    xsi:schemaLocation="http://schemas.malighting.de/grandma2/xml/MA 
    http://schemas.malighting.de/grandma2/xml/3.2.2/MA.xsd" 
    major_vers="3" minor_vers="2" stream_vers="2">
  <Info datetime="2016-09-26T20:40:54" showfile="dummyfile" />
  <Plugin index="1" execute_on_load="]]..EOLnum..[[" 
          name="]]..name..[[" 
          luafile="]]..plugin.name..[[.lua" />
</MA>]]

  -- Write files to disk
  local fileXML = assert(io.open(plugin.fullpathXML, 'w'))
  fileXML:write(xmlText)
  fileXML:close()
  
  local fileLUA = assert(io.open(plugin.fullpathLUA, 'w'))
  fileLUA:write(script)
  fileLUA:close()

  -- Import plugin
  cmd('Import "'..plugin.name..'.xml'..'" Plugin '..tostring(num))

  -- Clean up temporary files
  os.remove(plugin.fullpathXML)
  os.remove(plugin.fullpathLUA)
end
This pattern is useful for installer plugins that set up multiple related plugins or generate custom plugins based on user input.

File Locations Reference

System VariablePurposeTypical Path
PLUGINPATHPlugin storage/gma2/plugins/
PATHShowfile path/gma2/showdata/shows/
Import/ExportTemporary files[PATH]/importexport/

Accessing Paths in Code

-- Get plugin directory
local pluginDir = gma.show.getvar('PLUGINPATH')..'/'

-- Get showfile path
local showPath = gma.show.getvar('PATH')..'/'

-- Get import/export directory
local importExportDir = showPath..'importexport/'

Common Installation Issues

Possible Causes:
  • XML filename doesn’t match import command
  • Files not in correct directory
  • XML syntax error
Solution:
  • Verify both files exist in PLUGINPATH
  • Check XML file for syntax errors
  • Ensure luafile attribute matches .lua filename exactly
Possible Causes:
  • Lua file not found
  • Lua syntax errors
  • Return statement missing
Solution:
  • Check that .lua file exists and has correct name
  • View system messages for Lua errors
  • Ensure plugin returns its main function:
local function PluginName_Start()
  -- plugin code
end

return PluginName_Start
Issue: Importing to an index that’s already in useSolution:
  • Check plugin pool before importing
  • Choose an unused index
  • Or deliberately overwrite to update a plugin

Drive Selection

Always select the correct drive before importing:
-- Select internal drive (most common)
cmd('SelectDrive 1')

-- Then import from that drive
cmd('Import "PluginName.xml" Plugin 5')
Drive selection is critical for programmatic imports. The console looks for files on the currently selected drive.

Best Practices

  1. Use Descriptive Names: Make plugin names clear and specific
  2. Document Index Usage: Keep track of which plugin uses which index
  3. Version Control: Include version info in plugin feedback messages
  4. Test Before Deploying: Always test on a backup showfile first
  5. Backup Originals: Keep copies of .lua and .xml files externally
  6. Avoid Auto-Execute: Set execute_on_load="0" unless absolutely necessary

Plugin Return Patterns

Plugins must return their entry point function:
-- Single function (most common)
return PluginName_Start

-- Multiple functions (with cleanup)
return PluginName_Start, PluginName_Cleanup
The cleanup function is called when the plugin is removed or the showfile closes.

Build docs developers (and LLMs) love