Migrating from v1 to v3 Blueprints
This guide will help you migrate your existing v1 blueprints to the new v3 features.
Key Improvements in v3
- Hardware Independence: Use capability requirements instead of hardcoded product names for portable blueprints
- Access Levels: Control who can read telemetry, execute commands, or modify configuration
- Device Profiles: Implement standardized interfaces for consistent device behavior across the ecosystem
- UCUM-Compliant Units: Standardized units of measurement with automatic scaling support
- Declarative Configuration: Define configuration in manifest instead of Lua code
- Enhanced Metadata: Support for troubleshooting info in alerts, colors for enums
- Command Response Definitions: Document expected responses directly in manifest
- Native JSON Support: Built-in handling of complex JSON data structures
- String Error Messages: Human-readable errors replace numeric error codes
Step-by-Step Migration
Step 1: Update Blueprint Specification
Change blueprint_spec of your manifest.yaml:
blueprint_spec: device/1.0
blueprint_spec: device/3.0
Step 2: Update Blueprint Metadata
- Remove
vendorandverification_levelfields. Both of these data are now managed as metadata in the marketplace. - Add
categoryfield for UI grouping. See avialable categories at manifest reference.
vendor: Enapter
verification_level: verified
category: electrolysers
Step 3: Replace communication_module
The communication_module section has been replaced with a more flexible runtime section.
- Replace the
communication_modulesection withruntimesection. - Remove
productfield. - Set
typeof the runtime. In case of Lua blueprint it would belua, in case of standalone blueprints it would beembedded. - In case of Lua blueprint:
- Rename
luasection tooptions. - Replace
lua_file, if any, withoptions.file. - Rename
dependenciestoluarocks. - Rename
allow_dev_dependenciestoallow_dev_luarocks.
- Rename
communication_module:
product: ENP-RL6
lua:
file: firmware.lua
runtime:
type: lua
options:
file: firmware.lua
Step 4: Declare Implemented Profiles
v3 introduces device profiles—standardized interfaces for common device types. When a blueprint implements a profile, it must provide all required telemetry, commands, and properties defined by that profile.
Add implements field to declare profiles:
implements:
- sensor.ambient_temperature
telemetry:
ambient_temperature:
type: float
display_name: Ambient Temperature
unit: Cel
If your telemetry name differs from profile, specify the mapping:
implements:
- sensor.solar_irradiance
telemetry:
irradiance:
type: float
display_name: Solar Irradiance
unit: W/m2
implements: sensor.solar_irradiance.solar_irradiance
See all available profiles on GitHub.
Step 5: Update Colors for Enum Values
v3 uses standardized color palette for enum values. Replace hex colors with named colors (e.g., green, red, yellow, blue). Colors are displayed in device status badges and telemetry dashboards.
telemetry:
status:
type: string
enum:
ok:
display_name: OK
color: "#ffeedd"
telemetry:
status:
type: string
enum:
ok:
display_name: OK
color: green
We are strongly recommend to use the following colors for the device status. With this approach users will be able to quickly understand the device status of the different implementations.
| Usage | Color name | Statuses example |
|---|---|---|
| normal operations, idle | green-lighter | idle, OK, powered, operating, online |
| active action | green | steady, charging |
| pre- or post- action | green-dark, green-darker, cyan-dark, cyan-darker | rump up, rump down |
| warning | yellow | warning, overheated, low level |
| error | red | error, gas leakage, fault |
| fatal error | red-darker | fatal error, sensor damaged |
| maintenance | pink-darker | maintenance mode, service mode |
Step 6: Update Units to UCUM Format
v3 requires UCUM-compliant units for telemetry and properties. Replace arbitrary unit strings with UCUM standard codes.
telemetry:
power:
type: float
unit: watt
telemetry:
power:
type: float
unit: W
Add auto_scale for automatic unit conversion in UI:
telemetry:
power:
type: float
unit: W
auto_scale: [W, kW, MW]
UI will display 500 W, 5.2 kW, or 1.5 MW depending on value magnitude.
Examples of common UCUM units:
| Unit | UCUM | Unit | UCUM | Unit | UCUM |
|---|---|---|---|---|---|
| Ampere | A | Liter | L | Second | s |
| Bar | bar | Minute | min | Volt | V |
| Bar gauge | barg | Normal liter | Nl | Volt-ampere | VA |
| Celsius | Cel | Percent | % | Volt-ampere reactive | VAR |
| Hour | h | Percent LEL | %LEL | Watt | W |
| Kilowatt | kW | Revolutions per minute | rpm | Watt-hour | Wh |
Step 7: Add Access Levels
v3 adds access_level field to control who can read telemetry, view properties, execute commands, or modify configuration.
properties:
fw_ver:
display_name: Firmware Version
access_level: readonly
telemetry:
temperature:
display_name: Temperature
access_level: readonly
commands:
restart:
display_name: Restart Device
access_level: user
configuration:
connection:
display_name: Connection Settings
access_level: installer
Available levels (lowest to highest): readonly, user, owner, installer, vendor, system. The default value for properties and telemetry is readonly, while for commands and configuration it is user.
Step 8: Enhance Alerts Metadata
v3 adds richer alert metadata for better troubleshooting. Add troubleshooting steps, related telemetry, affected components, and possible conditions for alerts.
alerts:
cannot_read_data:
severity: error
display_name: Cannot Read Data
description: Failed to read data
alerts:
cannot_read_data:
severity: error
display_name: Cannot Read Data
description: Failed to read data
troubleshooting:
- Check physical connections
- Verify device is powered on
telemetry:
- packet_loss
components:
- Hardware Communication
conditions:
- Hardware failure
- Connection issue
- Wrong configuration
Step 9: Add Command Response Definitions
v3 allows documenting command responses in the manifest. Add response field to commands that return data.
commands:
get_temperature:
display_name: Get Temperature
arguments:
unit_id:
display_name: Unit ID
type: integer
response:
temperature:
display_name: Temperature
type: float
Step 10: Use JSON for Complex Command Arguments
v3 adds native JSON type support for command arguments. Use type: json with json_schema field (inline JSON or path to schema file).
commands:
write:
display_name: Write
group: modbus
arguments:
queries:
display_name: Write Queries
type: json
json_schema: |
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
Step 11: Update Error Handling in Lua Code
v3 uses string error messages instead of numeric error codes. Replace result ~= 0 checks with err ~= nil and remove err_to_str() conversions.
local state, result = di7.is_closed(1)
if result ~= 0 then
enapter.log('Error: ' .. di7.err_to_str(result), 'error')
end
local channel, err = digitalin.new('port://di-1')
if err ~= nil then
enapter.log('Error: ' .. err, 'error')
return
end
local state, err = channel:get_state()
if err ~= nil then
enapter.log('Error: ' .. err, 'error')
end
Step 12: Update to Object-Oriented Lua API
v3 replaces function-based hardware APIs with object-oriented channel APIs. Create channel objects with module.new(uri) constructors, then call methods using colon syntax.
-- Call module function with port number
local state, _ = di7.is_closed(1)
telemetry.di1_closed = state
-- Create channel object
local di1, _ = digitalin.new('port://di-1')
-- Call methods on the object via colon syntax
local state = di1:get_state()
telemetry.di1_closed = (state == digitalin.HIGH)
Common hardware module migrations (not exhaustive list):
| v1 Module | v3 Module |
|---|---|
di7 | digitalin |
rl6 | relay, digitalout |
ai4 | analogin |
ao4 | analogout |
rs232, rs485 | serial |
can | can |
modbus | modbus |
See Lua API reference for more examples of available APIs.
Step 13: Use Declarative Configuration
v3 moves configuration from Lua code to manifest.
- Add configuration section to manifest.
- Remove
read_configurationandwrite_configurationcommands from manifest. - Remove
require('enapter.ucm.config')andconfig.init()from Lua code, andenapter-ucmLuarocks dependency from the manifest. - Use
configuration.read()to access config values. - Use
configuration.after_write()to handle config changes.
configuration:
connection:
display_name: Connection
parameters:
connection_uri:
display_name: Connection URI
type: string
required: true
local conn
function enapter.main()
reconnect()
configuration.after_write('connection', function()
conn = nil -- Reset connection on config change
end)
scheduler.add(1000, reconnect)
end
function reconnect()
if conn then return end
if not configuration.is_all_required_set('connection') then return end
local cfg, err = configuration.read('connection')
if err then return end
conn, _ = modbus.new(cfg.connection_uri)
end
It is a common pattern to create and use the connection_uri parameter to configure how and where device should connect to. See more about connection URIs.
See configuration documentation for more details.
Step 14: Use enapter.main() as Entrypoint
v3 automatically calls enapter.main() as the entrypoint. Remove manual main() calls from your code.
function main()
scheduler.add(1000, send_telemetry)
end
-- Manual call required
main()
function enapter.main()
scheduler.add(1000, send_telemetry)
end
-- No manual call needed
-- enapter.main() is called automatically