Build a Model
Hands-on: design a reusable, data-bound model from scratch in the Model Builder.
In this tutorial you will build a device status indicator from scratch in the Model Builder. By the end you will have a reusable Model that draws a colored circle and a label, takes a target entity as a typed parameter, and recolors itself live whenever that entity's status changes. You will then save it so it can be instantiated many times against different devices.
Looking for full details?
This is a hands-on tutorial. For the complete reference (every field, option, and edge case), see the Model Builder reference.
What you'll need
- Access to the ControlBird UI from a desktop browser. The Model Builder is desktop-only and opens with a default canvas of 1200×700 px.
- At least one entity in the store with a
Statusfield you can watch, for example a device with values likeOnlineorOffline. - About 15–20 minutes. No prior models are required; you will start from a blank canvas.
Click the canvas before using shortcuts
Keyboard shortcuts such as Ctrl+S and Ctrl+Z are blocked while a text input has focus (the Parameters, Notifications, or callback editors). After typing in a panel, click an empty spot on the canvas first, then use the shortcut.
Step by step
Open the Model Builder from the ControlBird UI, then click New to start a blank model.
Expected result: an empty canvas with the Shape Palette, Model Library, and Layers panels on the left, and the Properties, Parameters, Notifications, and Validation panels on the right.
Add a circle. From the Shape Palette on the left, click or drag a
Circleonto the canvas. With the circle selected, open the Properties panel on the right and set itsidtostatus-circle, setfillColorto#6b7280(gray) andfillOpacityto0.8, and set theradiusto20.Expected result: a solid gray dot on the canvas. The
idis what your callback will reference later, so the exact value matters.Add a text label. From the Shape Palette, add a
Textshape and position it next to the circle. In the Properties panel set itstextto${deviceLabel}. The${paramName}syntax substitutes a parameter value at instantiation time.Expected result: the label currently shows the literal placeholder; it will render the bound value once the
deviceLabelparameter exists and is supplied.Define your parameters. Open the Parameters panel and click Add. Create the two inputs this model needs:
name=targetDevice,type=entity, validationrequired: true: the device this indicator watches.name=deviceLabel,type=string,defaultValue=Device: the text shown beside the dot.
Expected result: both parameters appear in the panel. Because
deviceLabelhas a default, the label no longer counts as missing in validation.Wire up live data with a notification channel. Open the Notifications panel and add a channel. Set its
nametostatusWatch, setentityExpressionto${targetDevice}, setfieldNametoStatus, and enabletriggerOnChangeso the callback fires only when the value actually changes.Expected result: a channel named
statusWatchthat resolves the entity from thetargetDeviceparameter and observes itsStatusfield.Write the callback. In the channel's
callbackeditor, look up the circle by theidyou set earlier and recolor it based on the incoming value:function(notification) { const status = notification.current.value.String; const circle = this.getShapeById('status-circle'); if (status === 'Online') { circle.setFillColor('#10b981'); // green } else if (status === 'Offline') { circle.setFillColor('#ef4444'); // red } else { circle.setFillColor('#6b7280'); // gray } }Expected result: when an instance is bound to a live device, the dot turns green, red, or gray as the device's
Statuschanges.Check the Validation panel. It runs continuously and reports unused parameters, missing parameter definitions, required parameters without defaults, and unresolved entity or model references. Resolve anything it flags. For example, if
targetDeviceis marked required but has no default, that is expected here because the value is supplied per instance.Expected result: a clean (or intentionally understood) Validation panel before you save.
Tidy the layout. Use the canvas controls to fit all shapes to view and toggle grid snapping for clean alignment. If the label sits behind the circle, select it and press
Ctrl+]to bring it forward (orCtrl+[to send it backward).Save. Click an empty area of the canvas, then press
Ctrl+S. The complete model definition (shapes, parameters, notification channels, and canvas settings) is saved to theConfigurationfield of a newModelentity.Expected result: the model is persisted under
/ControlBird/User Designs/Modelsand now appears in the Model Library for reuse and nesting.
Auto-save only kicks in after the first manual save
Auto-save (every 30 seconds) only triggers once a model has been saved at least once and has a modelId. A brand-new, never-saved model does not auto-save: if you close the tab before your first Ctrl+S, your work is lost. Save early.
Substitution has limits
The ${paramName} syntax only works inside text, html, css, event handlers, and callback expressions. It does not work in numeric properties like radius or fontSize, so set those to concrete values.
Instantiate and reuse it
Your model is now a template. To embed it inside a larger design, open another model and drag this one from the Model Library onto the canvas to create a ModelInstance. Then open the Parameter Binding Editor and supply values for its parameters. For example, bind targetDevice as an expression and set deviceLabel as a constant. Each instance can point at a different device while sharing one design.
Nested models don't inherit channels
A nested ModelInstance inherits its parameter bindings but not the parent model's notification channels. Each model must define its own channels, which is exactly why this indicator carries its own statusWatch channel and works wherever you drop it.
Next steps
You have built and saved a reusable, data-bound model. From here:
- Read the Model Builder reference for every shape property, parameter validation rule, and binding type.
- Inspect the data your model binds to with the Database Browser.
- Understand the mechanism behind notification channels in Notifications & Reactivity.