Tutorials

Video tutorials and step-by-step guides to help you learn Fentrica.

Creating Re-usable Export Files

A raw export from the platform is a snapshot — it bakes in the exact IP addresses, ports, unit IDs, and other credentials that were entered when the system was commissioned. That is great for backups, but not great for reuse: if you import the same .fpl into ten sites, every site ends up with the same IP and unit ID, which almost never matches reality.

This tutorial shows you how to promote hard-coded values into variables so the importer prompts for the right value at each site — turning a one-off export into a template you can reuse across an entire fleet.

By the end of this guide you will have taken a raw heating-inverter.fpl, replaced its host, port, and unit ID with variables, and imported it into a second site using different credentials.

What you will build

One-time authoring, many imports

Export raw templateUnpack .fplEdit manifest.jsonDefine variablesRe-pack and testShare template

Prerequisites

  • Admin or Manager access to a Fentrica site
  • A pre-configured technical system you want to turn into a template (already exported once — see Importing and Exporting Technical Systems)
  • A text editor that handles JSON well (VS Code, Sublime, Notepad++, etc.)
  • A ZIP tool — any OS-level extractor works, but keep reading for platform-specific notes
.fpl is just a ZIP

A .fpl file is a standard ZIP archive with the .fpl extension so the platform recognizes it. You can rename heating-inverter.fpl to heating-inverter.zip, edit the contents, then rename it back — no special tools required.

Step 1: Export a raw template from the platform

Start with a technical system that is configured and working. This will be your reference configuration — the shape and structure of everything inside the .fpl is copied exactly as it was set up in the UI.

  1. Open the site from the dashboard
  2. Navigate to Settings > Import / Export
  3. Locate the technical system you want to templatize and click Export on its row
  4. Save the downloaded .fpl file to your working directory

Settings > Import / Export view — export action on a technical system row

Start from a known-good configuration

The values inside the raw export are your baseline. Everything that works on the source site will work on every target site — as long as the variable values you enter at import time are correct for that site.

Step 2: Unpack the .fpl file

You now need to open the archive so you can edit the manifest.

macOS / Linux:

mkdir heating-inverter-template
cd heating-inverter-template
unzip ../heating-inverter.fpl

Windows:

  1. Right-click the file and choose Rename — change the extension from .fpl to .zip
  2. Right-click the renamed .zip and choose Extract All…
  3. Pick a destination folder

Either way you end up with a directory that looks like:

heating-inverter-template/
├── manifest.json
└── assets/
    ├── .cover/
    │   └── cover.jpg
    └── documentation/
        └── service-manual.pdf

Unpacked .fpl folder structure with manifest.json and assets directory

The file you care about is manifest.json. Everything in assets/ travels with the template as-is and does not need to be modified.

Step 3: Inspect the manifest

Open manifest.json in your text editor. You will see a structure similar to this (abbreviated for clarity):

{
  "$schema": "https://api.building.fentrica.com/orgs/schemas/technical-system-export-v1.json",
  "version": "1.0",
  "exportedAt": "2026-04-17T12:34:56.000Z",
  "system": {
    "name": "Heating Inverter AHU-01",
    "type": "heating",
    "manufacturer": "Daikin",
    "model": "VRV-IV"
  },
  "connections": {
    "modbus_connection": {
      "name": "Modbus Connection",
      "type": "modbus_tcp",
      "data": {
        "host": "192.168.1.100",
        "port": 502,
        "unitId": 1
      }
    }
  },
  "datapoints": {
    "supply_temperature": { "name": "Supply Temperature", "...": "..." }
  }
}

The fields most often worth turning into variables live inside connections[*].data:

FieldWhy templatize it
hostEvery site has a different IP address or hostname for the controller
portNon-standard ports vary between installations
unitId / slaveIdModbus unit IDs depend on how the vendor provisioned the equipment
apiKey, tokenCloud credentials are per-site

The system name is also a common candidate, because you usually want each imported copy to be identified by location ("AHU-01", "Boiler Room East", etc.) rather than the original name.

Step 4: Add the variables block

The manifest supports a top-level variables array that declares what the importer should ask for. Each variable has a key, a label, and a type.

Add a variables array just after exportedAt:

{
  "$schema": "https://api.building.fentrica.com/orgs/schemas/technical-system-export-v1.json",
  "version": "1.0",
  "exportedAt": "2026-04-17T12:34:56.000Z",
  "variables": [
    {
      "key": "SYSTEM_NAME",
      "label": "System name",
      "type": "string",
      "required": true
    },
    {
      "key": "HOST",
      "label": "Controller IP address or hostname",
      "type": "string",
      "required": true
    },
    {
      "key": "PORT",
      "label": "Modbus TCP port",
      "type": "integer",
      "defaultValue": 502,
      "required": true
    },
    {
      "key": "SLAVE_ID",
      "label": "Modbus unit/slave ID",
      "type": "integer",
      "defaultValue": 1,
      "required": true
    }
  ],
  "system": { "...": "..." }
}

Variable field reference

FieldDescription
keyUnique name, uppercase letters/digits/underscore only (e.g. HOST, API_KEY, POLL_INTERVAL_MS)
labelHuman-friendly label shown in the import wizard
typeOne of string, integer, number, boolean, select
defaultValueValue pre-filled in the import wizard — users can override it
optionsFor type: "select" only — list of allowed string values shown as a dropdown
requiredWhether the importer must fill in a value. Defaults to true — set to false only for truly optional fields

manifest.json with variables array highlighted

Keys must match the placeholders

The key you declare here is exactly the token the importer looks for later — HOST must be referenced as {{HOST}} in the manifest. Typos silently fail: the placeholder stays as literal text in the imported configuration.

Step 5: Replace hard-coded values with {{VARIABLE}} placeholders

Now go through the manifest and replace the values you promoted with {{KEY}} placeholders.

Before:

{
  "system": {
    "name": "Heating Inverter AHU-01",
    "type": "heating"
  },
  "connections": {
    "modbus_connection": {
      "name": "Modbus Connection",
      "type": "modbus_tcp",
      "data": {
        "host": "192.168.1.100",
        "port": 502,
        "unitId": 1
      }
    }
  }
}

After:

{
  "system": {
    "name": "{{SYSTEM_NAME}}",
    "type": "heating"
  },
  "connections": {
    "modbus_connection": {
      "name": "Modbus Connection",
      "type": "modbus_tcp",
      "data": {
        "host": "{{HOST}}",
        "port": "{{PORT}}",
        "unitId": "{{SLAVE_ID}}"
      }
    }
  }
}

Side-by-side diff of manifest before and after variable substitution

How placeholder substitution works

During import, the platform performs a string-level replacement on the raw manifest text before parsing it as JSON:

  • "host": "{{HOST}}" with HOST = "10.0.0.5" becomes "host": "10.0.0.5" → a string
  • "port": "{{PORT}}" with PORT = 502 becomes "port": 502 → a number
  • "enabled": "{{ENABLED}}" with ENABLED = true becomes "enabled": true → a boolean

That is why you can write the placeholder inside quotes even for integer and boolean variables — the type declared in the variables array determines how the replacement value is serialized back into the JSON.

Don't touch `{{ref:dp:...}}` references

You may see tokens like {{ref:dp:supply_temperature}} inside the controlHub or visualizationHub arrays. Those are internal datapoint references the importer resolves automatically, not user variables. Leave them alone — they are what makes your timeseries charts and control hub items point to the correct (newly created) datapoints after import.

What NOT to templatize

FieldWhy to leave it alone
$schema, versionRequired metadata — changing these breaks schema validation
type on system/connections/datapointsEnums validated against the schema; putting a placeholder fails validation
Datapoint accessType, writeInputTypeEnums — same reason
{{ref:dp:...}}, {{ref:md:...}}Internal ID references, not variables
The datapoint key (e.g. supply_temperature)Referenced by controlHub items — changing it breaks visualizations

If you need a variable value somewhere an enum is required, you probably want two different templates rather than one.

Step 6: Re-pack the .fpl

Once manifest.json is saved, compress the folder back into a .fpl.

macOS / Linux:

cd heating-inverter-template
zip -r ../heating-inverter-template.fpl manifest.json assets

Windows:

  1. Select manifest.json and the assets folder (not the parent folder — the manifest must sit at the root of the ZIP)
  2. Right-click and choose Send to > Compressed (zipped) folder
  3. Rename the resulting .zip to .fpl
Put manifest.json at the root

manifest.json must sit at the root of the archive, not inside a subfolder. If you accidentally zip the parent folder, the importer fails with "No manifest.json found in the package".

Step 7: Test the template

Import the re-packed template into a different site (or the same site, under a different name) to verify everything resolves.

  1. Open the target site from the dashboard
  2. Navigate to Settings > Import / Export
  3. Click Import and upload your new .fpl
  4. In the preview step, verify that all the variables you declared show up as input fields with the correct labels
  5. Fill in values appropriate for the target site
  6. Pick an edge device (if the template includes connection config) and click Import
  7. Open the newly created technical system and check that the host, port, unit ID, and name reflect the values you just entered — not the defaults from the source site

Import wizard preview step showing user-defined variables

Imported technical system with resolved values

If any variable still shows the original hard-coded value, double-check that:

  • The key in the variables array matches the placeholder exactly (case-sensitive)
  • The placeholder is surrounded by double curly braces: {{HOST}}, not {HOST} or ${HOST}
  • You re-packed the edited manifest.json — not the original

Step 8: Share the template

Once the template imports cleanly, version it. Suggested practices:

  • Name it descriptivelyschneider-iem3000-v1.fpl, daikin-vrv-iv-heating-v2.fpl
  • Store it in a shared drive or Git repository so the commissioning team always grabs the latest version
  • Keep a CHANGELOG next to the file when the underlying equipment firmware or the manifest schema changes
  • Document required variables — the label field helps, but a short README with example values per site speeds up onboarding

Common pitfalls

SymptomCauseFix
Import fails with "Invalid manifest"JSON syntax error from manual editingValidate the JSON with a linter or jq before re-packing
Placeholder stays as {{HOST}} in the imported systemVariable key doesn't match placeholder textCheck exact spelling and casing — HOSTHost
Import fails with "Missing required variables: …"Variable was required but not enteredEither enter a value in the wizard or set "required": false with a defaultValue
Unit ID or port ends up as "502" (string) instead of 502 (number)Variable type was string but should be integerChange the variable type to integer and re-import
Import fails with "No manifest.json found in the package"ZIP contains a wrapping folder instead of the files directlyRepack: select the contents, not the parent folder

What's next