Authoring Charts from the CLI
The chart verbs work differently — you drop measures on nodes rather than building structure, and discovery verbs tell you where.
Chart verbs behave differently from the rest of the CLI, because charts are trees the chart type builds rather than structures you assemble. Once that clicks, scripted chart building is straightforward.
The one thing to understand first#
You do not add a measure to a chart by building nodes. You drop the measure on a component and the chart type builds the sub-tree for you — exactly as drag-and-drop does in Flow Config.
Create a Widget chart and it has components and no items:
comp 1 General
comp 2 Properties
comp 3 Title
comp 4 Period
comp 5 QuickLinks
comp 6 Sections <-- drop here
Drop a measure on Sections and the type materialises the rest:
comp 6 Sections
comp 7 Section
comp 8 Widget
item 1 MeasureItem [Measure = Quality Rate]
item 2 MeasureLimitItem
item 3 MeasureEventAttributeItem
item 4 ColorItem
None of those existed beforehand. That is why there is no "add a measure to a Section" verb — the Section is a result of the drop.
Never hard-code a chart type ID#
Chart type IDs are auto-increment rows and differ between instances:
--list-chart-types
A script with a hard-coded ID will create the wrong kind of chart on a different instance, silently. Look it up by name at the start of the run.
Creating a chart#
--create-chart --name "Steam Flow" --chart-type-id <id> \
--chart-constructor Daily \
--report-folder-id <id> # or --dashboard-id
--chart-constructor names the factory constructor, which fixes the chart's interval permanently. For most time-based types there is one per interval: Tag, Minutely, Hourly, Shiftly, Daily, Weekly, Monthly, Quarterly, Yearly. Event charts take their own — Event Based Timeline, Event Based Table, Event Based Form.
Omit it only where a factory has a single constructor. Pass a name it does not know and the error lists the valid ones.
--time-scheme-id is available where the chart needs a specific calendar.
Ask where the measure goes#
The target component differs per chart type. Rather than guessing:
--list-chart-node-options --chart-id 1 --measure-id 33
It prints the command to use. You can also check a single node, and it tells you what to run instead:
--list-chart-node-options --chart-component-id 3 --measure-id 33
[no] does NOT accept Measure 33 'Quality Rate' (IntervalType Hourly)
> --list-chart-node-options --chart-id 1 --measure-id 33 lists every node that does
The same works with --metric-id and --event-scheme-id.
Use the discovery verb rather than memorising containers. Twenty-one chart types with different structures is more than anyone should hold in their head. Asking the chart is faster than looking it up and cannot go stale.
Adding content#
--add-chart-measure --chart-component-id <id> --measure-id <id>
Metrics and event schemes drop the same way, each producing the structure their chart type defines for them.
Interval matching is enforced: a chart accepts only measures at its own constructor interval, and a Tag interval measure has no interval, so every interval chart refuses it. Chart the hourly or daily derivation.
Inspect what you built#
--list-chart-tree --chart-id <id>
This is the verb to lean on. Because structure is built for you, seeing what was actually created is how you confirm a scripted chart is right.
Assert on it. A chart created but never given a measure exits 0 and renders empty — the most common silent failure in scripted chart building.
Setting properties#
--set-chart-property --chart-id <id> ...
--set-chart-component-property --chart-component-id <id> ...
--set-chart-item-property --chart-item-id <id> ...
These are framework-mediated: the factory loads the chart, the property name and value are validated through the type converter, and the chart is saved. A property that does not exist on that node, or a value of the wrong type, is rejected rather than silently ignored — which is what you want, since a quietly discarded setting is very hard to notice.
Nested items, limits and event attributes#
Items can nest, addressed with --parent-chart-item-id on --add-chart-item.
Some slots are owned by the chart type rather than offered through --list-chart-node-options: the limit item and the event-attribute item a chart type materialises under a measure item. Those are bound on the existing item rather than added:
--update-chart-item-limit --chart-item-id <limit item> --measure-limit-id <ml>
--update-chart-item-event-attribute --chart-item-id <attribute item> --event-attribute-id <ea>
--update-chart-item-measure --chart-item-id <measure item> --measure-id <id>
The framework's rules apply. A limit slot only accepts a limit belonging to the measure on its parent chart item, so the chart must already carry that measure as a series; --measure-limit-id is the ID of the measure-to-limit link, which --list-measure-limits --measure-id <id> reports. An event-attribute slot only accepts an attribute in the parent measure's event-attribute context, which --list-event-attributes --event-scheme-id <id> --measure-id <id> lists. Pass --measure-limit-id 0 to clear a limit binding.
Order: add the measure, read the tree to find the item the type created, then bind.
Named sections#
Several chart types have containers holding multiple named sections. The pattern:
# 1. find the section type under the chart's sections container
# 2. create two sections
# 3. name them
# 4. put an interval-matched measure in each
Each section then holds its own content, which is how a single Widget chart shows several related values under one heading.
Dashboards and panels#
--create-dashboard --name "Boiler 1 Overview" --report-folder-id <id>
--add-dashboard-panel --dashboard-id <id> --chart-id <id> --panel-left 0 --panel-top 0 --panel-width 24 --panel-height 7
Panels sit on a 48-column grid with rows growing downward. A quarter-width widget is 12 columns, a half-width chart 24, a full-width one 48.
A panel ID appears only on the success line of the call that created it, so three verbs let you work with panels afterwards:
--list-dashboard-panels --dashboard-id <id> # IDs, geometry, and the bottom edge the layout reaches
--update-dashboard-panel --dashboard-panel-id <id> --panel-left 12 --panel-top 5 --panel-width 24 --panel-height 7
--delete-dashboard-panel --dashboard-panel-id <id> # the chart itself survives
--update-dashboard-panel takes any of the four geometry options plus --panel-title, and refuses what would not be visible: a panel running past column 48, or a call with no geometry at all.
Where a chart or dashboard is embedded in another chart, --embed-chart-id and --embed-dashboard-id populate those fields.
The Released and Version contract#
Structural changes to a chart apply the same contract as the framework: any edit to a released chart un-releases it and bumps its version.
That is correct behaviour and it means a script that modifies a released chart template leaves it unreleased. If the script's purpose is to update a template, it must release it again afterwards.
Charts can be released and un-released from the CLI in 7.2.0, which is new. The chart is addressed as an entity type, the same way a template metric is:
--release --entity-type Chart --entity-id <id>
--unrelease --entity-type Chart --entity-id <id>
A release is refused when the chart has no template bindings (there is nothing to publish) or when it is bound to model entities (a model chart cannot be a template).
Deleting charts and nodes#
--delete-chart --chart-id <id>
--delete-chart-node --chart-component-id <id> # or --chart-item-id
--delete-chart removes the chart with its whole tree, its dashboard panels and its usage rows. It refuses when the chart is a template with instances, is embedded by other charts, or is used by message segments; --force delinks or clears those references first. --delete-chart-node honours the chart type's own rules about what may be removed.
What charts cannot do from the CLI#
Know the boundary so you plan around it:
Available: create charts and dashboards, add measures, metrics and event schemes, set properties, bind limits and event attributes, swap a series, create, move and remove panels, delete charts and nodes, release and un-release as templates, export with the model (--export-model --export-type reports), list types, list node options, list the tree.
Not available: the remaining template operations for charts — link, delink, instantiate, and synchronise. --link-entity, --delink-entity, --instantiate and --update-from-template take folders, metrics, event schemes and measures, not charts. Those remain Flow Config activities.
So a fully scripted build can create charts, configure them, and release them as templates; instantiating and updating chart instances remains manual. See Chart Templates and Releases.
A working sequence#
# 1. what chart types are installed?
--list-chart-types
# 2. create a chart at the interval you need
--create-chart --name "Boiler 1 Steam" --chart-type-id <id> \
--chart-constructor Daily --report-folder-id <id>
# 3. where can the measure go?
--list-chart-node-options --chart-id <chart> --measure-id <daily>
# 4. put it there
--add-chart-measure --chart-component-id <comp> --measure-id <daily>
# 5. find the limit item the type created under the measure item, and the link id
--list-chart-tree --chart-id <chart>
--list-measure-limits --measure-id <daily>
# 6. bind it (the measure must already be a series)
--update-chart-item-limit --chart-item-id <limit item> --measure-limit-id <ml>
# 7. confirm
--list-chart-tree --chart-id <chart>
# 8. place it on a dashboard
--add-dashboard-panel --dashboard-id <dash> --chart-id <chart> --panel-left 0 --panel-top 7 --panel-width 24 --panel-height 7
Diagnosing#
| Symptom | Cause |
|---|---|
| Wrong chart type created | Hard-coded chart type ID |
| Constructor rejected | Name not valid for that factory — the error lists valid names |
| Measure will not add | Interval mismatch, or a Tag measure on an interval chart |
| No node accepts the measure | Wrong chart type for what you are showing |
| Chart renders empty | Created but no measure dropped — assert with --list-chart-tree |
| Property rejected | Does not exist on that node, or wrong type |
| Limit will not bind | Chart does not carry that measure as a series yet, or you passed the limit's ID rather than the MeasureLimit link ID |
| Event attribute will not bind | Attribute is outside the parent measure's event-attribute context — attach the scheme to the measure with --copy-attributes first |
| Panel update refused | Geometry runs past column 48, or no geometry was given |
| Release refused | Chart has no template bindings, or is bound to model entities |
| Delete refused | Chart is a template with instances, embedded elsewhere, or used by a message segment — --force clears the references |
| Released template became unreleased | Expected — a structural edit un-releases; release again |