# Musson Industrial’s Embr-Charts Module

**URL:** <https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618>\
**Category:** 3rd Party Modules\
**Tags:** perspective\
**Created:** [June 10, 2024, 12:53pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618 "2024-06-10T12:53:46Z")\
**Posts on this page:** 20\
**Page:** 18

<div class="post-metadata">

**Author:** ![mdemaris](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mdemaris/32/54639_2.png) [@mdemaris](https://forum.inductiveautomation.com/u/mdemaris)\
**Post date:** [February 16, 2026, 7:16pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/344 "2026-02-16T19:16:30Z")

</div>

Yeah... Using Github is not a strength, lol. Think I got it.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 16, 2026, 7:29pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/345 "2026-02-16T19:29:56Z")

</div>

Embr is released as a suite; `Embr v8.1 - 2026.01.21` was the latest 8.1 release. Under that GitHub release you will find downloads for all the 8.1 modules.

The module files have either `Ignition81` or `Ignition83` in the name to help 😉

---

<div class="post-metadata">

**Author:** ![mdemaris](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mdemaris/32/54639_2.png) [@mdemaris](https://forum.inductiveautomation.com/u/mdemaris)\
**Post date:** [February 17, 2026, 2:58am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/346 "2026-02-17T02:58:37Z")

</div>

I would like to use Chart.js or an Apex chart to provide an Easy Chart type of experience for our technicians. The tags in question do not have history enabled, but the data is captured when the device is powered on via a Transaction Group.

The Easy Chart pulls the data in via the DB Pens.

I have tried binding a dataset from a named query to a few different things, but after reading the docs it looks like I have to transform the dataset into an array.

**EDIT** : This chart is `Chart.js Line`

After going in circles with AI, I got my two chosen columns, `t_stamp` and `Col01`, to display on the chart, but it does not look like a trend line like Easy Chart. It's just plotting `(x,y)`, and when I add some sort of callback on the `scales: y`, the y-axis labels disappear. I'm assuming it's because these values are very small (1.0E-4 to -6 range).  
AI was trying to get me to add a `scales: x: time: unit:'minute'`, but I don't think `time` is an allowed attribute of `scales`.

The dataset binding is returned as JSON, here is the script transform:

```python
rows = value or []
	
	# Build [{x: <epoch_ms>, y: <float>}, ...]
	points = []
	for r in rows:
	    ts = r.get('t_stamp')
	    y_raw = r.get('CZ01')
	    if ts is None or y_raw is None:
	        continue
	    try:
	        x = int(ts) # epoch in ms; Chart.js time scale accepts this
	        y = float(y_raw)
	        points.append({"x": x, "y": y})
	    except Exception:
	        # skip any malformed values
	        continue
	
	# Sort by time just in case
	points.sort(key=lambda p: p["x"])
	
	# Return Chart.js-style data object
	return {
	    "labels": [], # not used when we provide {x,y} points
	    "datasets": [{
	        "label": "CZ01",
	        "data": points,
	        "borderColor": "#4f46e5",
	        "backgroundColor": "rgba(79,70,229,0.12)",
	        "tension": 0.2,
	        "pointRadius": 0,
	        "fill": False
	    }]
	}

```

Here is what the chart looks like with the top 200 data points:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/9/29d53a511afb87f1ebd5f16f59b386e18d66bf90.png)

AI provided the script transform, one of several versions.

And this is the `props.options`:

```python
{
  "animation": {
    "duration": 1000,
    "easing": "easeOutQuart",
    "delay": 0,
    "loop": false
  },
  "animations": {
    "numbers": {
      "properties": [
        "x",
        "y",
        "borderWidth",
        "radius",
        "tension"
      ],
      "type": "number"
    },
    "colors": {
      "properties": [
        "color",
        "borderColor",
        "backgroundColor"
      ],
      "type": "color"
    }
  },
  "elements": {
    "point": {},
    "line": {},
    "bar": {},
    "arc": {}
  },
  "events": [
    "mousemove",
    "mouseout",
    "click",
    "touchstart",
    "touchmove"
  ],
  "font": {
    "size": 12,
    "style": "normal",
    "weight": "normal"
  },
  "interaction": {
    "mode": "nearest",
    "intersect": true,
    "axis": "x",
    "includeInvisible": false
  },
  "layout": {
    "autoPadding": true,
    "padding": 0
  },
  "maintainAspectRatio": false,
  "normalized": true,
  "parsing": false,
  "plugins": {
    "annotation": false,
    "autocolors": false,
    "colors": {
      "enabled": true
    },
    "crosshair": false,
    "datalabels": false,
    "dragData": false,
    "decimation": {
      "enabled": true,
      "threshold": 1,
      "algorithm": "lttb"
    },
    "filler": {
      "drawTime": "beforeDraw",
      "propagate": false
    },
    "legend": {
      "display": true,
      "position": "top",
      "align": "center"
    },
    "subtitle": {
      "align": "center",
      "display": false,
      "position": "top",
      "text": "Title"
    },
    "title": {
      "align": "center",
      "display": false,
      "position": "top",
      "text": "Title"
    },
    "tooltip": {
      "enabled": true,
      "position": "average",
      "callbacks": {
        "label": "function(ctx){ return \u0027CZ01: \u0027 + Number(ctx.parsed.y).toExponential(6);"
      },
      "backgroundColor": "#000000cc",
      "titleColor": "#ffffff",
      "titleAlign": "left",
      "bodyColor": "#ffffff",
      "bodyAlign": "left",
      "footerColor": "#ffffff",
      "footerAlign": "left",
      "padding": 6,
      "displayColors": true,
      "borderColor": "#000000",
      "borderWidth": 0
    },
    "zoom": {
      "pan": {
        "enabled": true,
        "mode": "x",
        "modifierKey": null
      },
      "zoom": {
        "wheel": {
          "enabled": true,
          "modifierKey": "ctrl"
        },
        "drag": {
          "enabled": true,
          "modifierKey": "ctrl"
        },
        "pinch": {
          "enabled": true
        },
        "mode": "x"
      }
    }
  },
  "resizeDelay": 0,
  "responsive": true,
  "scales": {
    "x": {
      "type": "time"
    },
    "y": {
      "type": "linear",
      "ticks": {
        "backdropColor": "rgba(255, 255, 255, 0.75)",
        "backdropPadding": 2,
        "display": true,
        "major": {},
        "padding": 3,
        "showLabelBackdrop": false,
        "textStrokeColor": "",
        "textStrokeWidth": 0,
        "z": 0
      }
    }
  },
  "transitions": {
    "active": {
      "animation": {
        "duration": 400
      }
    },
    "hide": {
      "animations": {
        "colors": {
          "properties": [
            "borderColor",
            "backgroundColor"
          ],
          "type": "color",
          "from": "transparent"
        },
        "visible": {
          "type": "boolean",
          "duration": "easeInExpo"
        }
      }
    },
    "reset": {},
    "resize": {
      "animation": {
        "duration": 400
      }
    },
    "show": {
      "animations": {
        "colors": {
          "properties": [
            "borderColor",
            "backgroundColor"
          ],
          "type": "color",
          "from": "transparent"
        },
        "visible": {
          "type": "boolean",
          "duration": 0
        }
      }
    }
  }
}

```

So, what I would like to do from here is to be able to:

- select/deselect multiple tags
- display the data as more of a trend rather than points
- expand/reduce the time frame, say 5 minutes to 2 hours.

Notes:  
The datapoints are randomly generated values between `1E-4` and `1E-5`. I understand I will probably need to use `runPrepQuery` or build a robust Sproc in SQL Server to handle the querying.  
I hope I am the right track with this!

---

<div class="post-metadata">

**Author:** ![daniele.dodi](https://avatars.discourse-cdn.com/v4/letter/d/8e7dd6/32.png) [@daniele.dodi](https://forum.inductiveautomation.com/u/daniele.dodi)\
**Post date:** [February 17, 2026, 8:52am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/347 "2026-02-17T08:52:08Z")

</div>

[ChartTest.zip](https://forum.inductiveautomation.com/uploads/short-url/ookEGkNCjqy8s0BlTXopJ0ubgyf.zip) (64.3 KB)

Thanks for the quick reply. In attached a simplified part of my project. I use a view with a chart, embedded in another view: the binding of props.series generates the errors in the logger.

The errors appear when opening the web page, but not if reloading it. I’m using Chrome.

---

<div class="post-metadata">

**Author:** ![mdemaris](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mdemaris/32/54639_2.png) [@mdemaris](https://forum.inductiveautomation.com/u/mdemaris)\
**Post date:** [February 17, 2026, 6:39pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/348 "2026-02-17T18:39:53Z")

</div>

Are there any plans to support `chartjs-plugin-streaming`?

I found the site here:[chartjs-plugin-streaming](https://nagix.github.io/chartjs-plugin-streaming/master/guide/getting-started.html)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 18, 2026, 3:55am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/349 "2026-02-18T03:55:37Z")

</div>

> [@mdemaris](#):
>
> I would like to use Chart.js or an Apex chart to provide an Easy Chart type of experience for our technicians.

At the moment, you have to build all this functionality yourself. There’s no quick way at it.  
I know members of the community who have built these features into their own projects, but most people want to keep their charting implementations to themselves.

I’ve had ideas for creating an “Power Chart”-like component to include in Embr Charts, but have decided against it for several reasons:

1. Users want to combine realtime and historical data. Doing this in a storage-agnostic way is difficult.
2. I am not in a position to support a component with the required complexity.

> [@mdemaris](#):
>
> Are there any plans to support `chartjs-plugin-streaming`?

According to my previous self, this plugin has some performance issues. [This commit note from 2 years ago:](https://github.com/mussonindustrial/embr/commit/efea6884f1ed5a684541a46766c81dedb7955dad)

> **Remove Streaming Plugin**  
> 2024/5/29  
> On a 100k point stress test, removing the streaming plugin resulted in an increase of 60+ fps when using the zoom plugin to pan.
> 
> Since there is no good way to currently utilize the streaming plugin on the base chart, the plugin was removed.

I’m open to revisiting the inclusion of this plugin though, since it’s been a couple years and Embr’s JavaScript capabilities might be mature enough to support some type of streaming.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 19, 2026, 10:51pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/350 "2026-02-19T22:51:05Z")

</div>

> [@daniele.dodi](#):
>
> The errors appear when opening the web page, but not if reloading it. I’m using Chrome.

This is caused by the classic Perspective binding startup race.

The errors in the logs are from the component complaining that `series[0]` is null. Because your `series` binding isn't persistent, it's beening evaluated as `null` when the chart first loads. Once the binding results evaluate, the chart re-renders and everything continues as expected.

The fix is to persist reasonable default values for your bindings (like a completely empty `series` configuration).

---

<div class="post-metadata">

**Author:** ![daniele.dodi](https://avatars.discourse-cdn.com/v4/letter/d/8e7dd6/32.png) [@daniele.dodi](https://forum.inductiveautomation.com/u/daniele.dodi)\
**Post date:** [February 20, 2026, 10:50am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/351 "2026-02-20T10:50:10Z")

</div>

Ok, I changed the values to persistent and now I don’t get errors. Thank you.

---

<div class="post-metadata">

**Author:** ![liam1](https://avatars.discourse-cdn.com/v4/letter/l/e68b1a/32.png) [@liam1](https://forum.inductiveautomation.com/u/liam1)\
**Post date:** [March 20, 2026, 12:00pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/352 "2026-03-20T12:00:02Z")

</div>

How do I update the chart data from interactions performed by custom plugins?

What is the correct

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/d/5dd019b026904837cb5dafa9ffa7c692f3cea464.png)

---

<div class="post-metadata">

**Author:** ![liam1](https://avatars.discourse-cdn.com/v4/letter/l/e68b1a/32.png) [@liam1](https://forum.inductiveautomation.com/u/liam1)\
**Post date:** [March 20, 2026, 12:20pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/353 "2026-03-20T12:20:08Z")

</div>

Okay figured it out , you use in the plugin perspective.sendMessage('scheduleUpdate'… and then create a normal message hander on the component to recieve the updates.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [March 20, 2026, 1:03pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/354 "2026-03-20T13:03:57Z")

</div>

> [@liam1](#):
>
> perspective.sendMessage('scheduleUpdate'… and then create a normal message hander on the component to recieve the updates.

That’s one good way to do it, you could also directly write to the component’s property tree.

I think it’d be: `this.store.custom.write(“path.to.key”, value)`

That’s a very cool use case!

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [April 27, 2026, 4:10am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/355 "2026-04-27T04:10:30Z")

</div>

> [@bmusson](#):
>
> think it’d be: `this.store.custom.write(“path.to.key”, value)`

Hey Ben, I assume this syncs back up to the gateway session value as well?

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [April 27, 2026, 10:45am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/356 "2026-04-27T10:45:24Z")

</div>

Yup, exactly.

You can also use this read and write ANY properties outside of the current component (e.g. view, page, session properties, or even properties on other components).

As a “best practice”, I’d recommend:

1. Only reading from the current components properties
2. Use messages to trigger writes on other components.

But there’s nothing stopping you from going direct.

---

<div class="post-metadata">

**Author:** ![MMaynard](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mmaynard/32/3366_2.png) [@MMaynard](https://forum.inductiveautomation.com/u/MMaynard)\
**Post date:** [May 20, 2026, 5:13pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/357 "2026-05-20T17:13:38Z")

</div>

Can someone please help my addled brain get this function into a chartJs?

```python
options: {
  scales: {
    x: {
      ticks: {
        callback: function(value, index, values) {
          let label = this.getLabelForValue(value);
          if (label.length > 10) {
            return label.substring(0, 10) + '...'; // Add ellipsis if too long
          } else {
            return label;
          }
        }
      }
    }
  }
}

```

I just want to add ellipsis to the X Axis titles... No matter what I try, it just errors out the component...

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [May 20, 2026, 6:16pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/358 "2026-05-20T18:16:13Z")

</div>

You're kinda close...

TBF the ticks callback is annoying.

1. In a Embr Charts `Chart.js` component's Scriptable Options context, the `this` reference points to the Perspective `ComponentStore`, not the Chart.js object.

2. However, the `this.delegate.prox.ref` isn't set until the Chart renders for the first time. Not a big deal from a UI perspective, but you'll need to check that the reference exists before you use it.

3. The `getLabelForValue` function lives on the scale.

Putting it all together:

```js
// ticks.callback
(value, index, values) => {
  const chart = this.delegate.proxy.ref
  if (chart == null) return

  const scale = chart.scales.x

  const label = scale.getLabelForValue(value)
  if (label.length > 10) {
    return label.substring(0, 10) + '...'
  } else {
    return label
  }
}

```

---

<div class="post-metadata">

**Author:** ![MMaynard](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mmaynard/32/3366_2.png) [@MMaynard](https://forum.inductiveautomation.com/u/MMaynard)\
**Post date:** [May 20, 2026, 6:41pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/359 "2026-05-20T18:41:20Z")

</div>

THANK YOU! This makes the category axis look sooo much cleaner, and keeps charts in line with one another.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 21, 2026, 3:57am UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/360 "2026-05-21T03:57:39Z")

</div>

Do you have an example you can share of the difference between not adding this and after adding it?

Obviously it chops the string if it's too long, but not sure how it looks if the string is longer; does it offset things?

---

<div class="post-metadata">

**Author:** ![MMaynard](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mmaynard/32/3366_2.png) [@MMaynard](https://forum.inductiveautomation.com/u/MMaynard)\
**Post date:** [May 21, 2026, 12:21pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/361 "2026-05-21T12:21:39Z")

</div>

> [@nminchin](#):
>
> Do you have an example you can share of the difference between not adding this and after adding it?

Here is a quick mockup of what I have been seeing, we have dashboards with multiple charts on them, and it is a dashboard that deals with multiple locations that can categorize delays with different names, some tend to get a little verbose so it can skew the overall look of the dashboard.

I just changed the strings here to generic, but you can see the difference.

This also makes it so the Tooltip still displays the long strings, but the chart stays lined up properly.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/d/1d94c6390d557a00e129622153608d53c02696dd.png)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [May 21, 2026, 2:52pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/362 "2026-05-21T14:52:23Z")

</div>

> [@MMaynard](#):
>
> This also makes it so the Tooltip still displays the long strings, but the chart stays lined up properly.

Very cool.

---

<div class="post-metadata">

**Author:** ![hw79](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hw79](https://forum.inductiveautomation.com/u/hw79)\
**Post date:** [May 22, 2026, 8:20pm UTC](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618/363 "2026-05-22T20:20:55Z")

</div>

This looks like an equipment schedule, do you mind sharing your chart configuration?

[Previous page](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618.md?page=17)

[Next page](https://forum.inductiveautomation.com/t/musson-industrial-s-embr-charts-module/91618.md?page=19)
