Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions for-developers/module-development/api-changes/v2.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
title: API 2.1 (Companion 5.0+)
sidebar_position: -210
description: 'Overview of API 2.1 changes (abortable learn, action results, graphics overhaul, affectedProperties for advanced feedbacks).'
---

API 2.1 builds on [API 2.0](v2.0.md) with new capabilities and some TypeScript-level type improvements. There are no runtime breaking changes.

If you haven't migrated to API 2.0 yet, start there — it is a prerequisite for 2.1.

## TypeScript: type safety for `subscribe` hooks

When using a `subscribe` callback on an action, `optionsToMonitorForSubscribe` is now **required** in the TypeScript types rather than optional.

Before:

```ts
{
name: 'My Action',
options: [...],
callback: async (action) => { ... },
subscribe: async (action) => { ... },
// optionsToMonitorForSubscribe was optional but recommended
}
```

After:

```ts
{
name: 'My Action',
options: [...],
callback: async (action) => { ... },
subscribe: async (action) => { ... },
optionsToMonitorForSubscribe: ['field1', 'field2'], // now required when subscribe is present
}
```

This is a TypeScript-only change. There is no runtime impact; if your module does not use TypeScript, or you already set `optionsToMonitorForSubscribe` on every action that uses `subscribe`, no changes are needed.

For a refresher on why `optionsToMonitorForSubscribe` matters with expressions, see the [API 2.0 guide](v2.0.md#expression-handling-in-options).

## Abortable learn callbacks

Learn operations can now be cancelled by the user. Companion passes an `AbortSignal` through the learn callback context, which is aborted if the user cancels the in-progress learn before it finishes.

You can observe this signal to exit early and clean up:

```ts
learn: async (action, context) => {
const response = await fetch('http://device.local/settings', { signal: context.signal })
if (context.signal.aborted) return undefined

const data = await response.json()
return { setting: data.value }
},
```

If the signal is already aborted when your callback is invoked, Companion skips it automatically.

This is entirely opt-in — if your learn callback is short or synchronous, you can safely ignore `context.signal`.

## Action callbacks can return a result value

Actions can now declare that they produce a result. When an action returns a value, Companion can store it in a local or custom variable of the user's choosing, making it available to subsequent actions in the same sequence.

Add `hasResult: true` to your action definition and return a `JsonValue` from the callback:

```ts
{
name: 'Read current value',
options: [
{
id: 'channel',
type: 'number',
label: 'Channel',
default: 1,
min: 1,
max: 64,
},
],
hasResult: true,
callback: async (action, context) => {
const level = await myDevice.getLevel(action.options.channel)
return level // a number, string, boolean, null, array, or object
},
}
```

In Companion's UI, the user will have the option to nominate a local or custom variable to receive the returned value after each execution.

:::tip

This pairs well with the expression support introduced in [API 2.0](v2.0.md#automatic-expression-parsing). The stored result can be referenced in later action options via the variable name the user chose.

:::

## Graphics overhaul

### Layered presets

A new `layered` preset type is available. It lets modules describe button graphics using the same element-based system Companion uses internally, without needing to produce raw image buffers via advanced feedbacks.

Available element types:

- **Text** — formatted text label
- **Image** — raster image
- **Box** — filled or stroked rectangle
- **Line** — line segment
- **Circle** — filled or stroked circle
- **Group** — a named collection of the above
- **Composite** — a reusable element defined by your module (see below)

Existing `simple` presets are unaffected and continue to work as before. The `layered` type is an addition for cases where richer graphics are needed.

:::tip

We recommend continuing to use `simple` presets whenever possible, for compatibility with Bitfocus Buttons

:::

### Composite elements

Modules can now define reusable composite drawing elements using `setCompositeElementDefinitions`. A composite is a named component built from simpler drawing elements. They can be referenced in layered presets, and users can also add them to their own buttons through the Companion UI.

```ts
this.setCompositeElementDefinitions({
'signal-indicator': {
name: 'Signal Indicator',
// ...element composition
},
})
```

This is intended as a more structured replacement for the "produce a bitmap in an advanced feedback" pattern, keeping your graphics logic encapsulated and reusable.

Composite element definitions and layered preset data are strictly validated on receipt, so invalid definitions produce clear warnings in the module debug log rather than silently corrupting button graphics.

## Advanced feedbacks: declare `affectedProperties`

Advanced feedbacks (`type: 'advanced'`) should now declare which button style properties they modify, via the new `affectedProperties` field:

```ts
{
type: 'advanced',
name: 'Status Colour',
options: [
{
id: 'active',
type: 'checkbox',
label: 'Active',
default: false,
},
],
affectedProperties: ['bgcolor', 'color'],
callback: async (event) => {
return event.options.active
? { bgcolor: '#00cc00', color: '#ffffff' }
: { bgcolor: '#cc0000', color: '#ffffff' }
},
}
```

Companion uses this to configure a more accurate set of style overrides for the button.

From API 2.1 onwards, Companion will emit a warning in the module debug log for any advanced feedback that does not declare `affectedProperties`. Boolean and value feedbacks are unaffected.

:::tip
Where possible, prefer boolean or value feedbacks, or the new [layered presets](#layered-presets), over advanced feedbacks. Advanced feedbacks are intended only for cases that cannot be expressed any other way.
:::

## Node 26 support

Modules can now declare `node26` as their runtime in `companion/manifest.json`:

```json
{
"$schema": "../node_modules/@companion-module/base/assets/manifest.schema.json",
"type": "connection",
"id": "your-module-name",
"runtime": "node26"
}
```

`node22` remains supported, but we encourage modules to update when they can. Node 26 is an LTS release and is suitable for new modules or modules that want to adopt the latest platform features.