Alarms calendar
Displays alarms from any device, asset or entity alias on a month calendar, colour-coded by severity, with acknowledged and cleared states visible at a glance. Click a day to drill into its timeline; click an alarm to read its details and acknowledge or clear it, or to fire a widget action of your choosing. Busy days collapse into a cluster link. Calendar navigation drives the timewindow, so browsing month to month fetches the right alarms. Fonts and colours are configurable, with no scripting required.
Alarms calendar
Places alarms on a month grid by the time each one started, coloured by severity. Click a day to drill into its hour-by-hour timeline; click an alarm to read its details and acknowledge or clear it without leaving the dashboard.
Features
- Month grid with alarms on the day they started, ordered by severity within each day
- Two chip styles: soft (tinted fill with a coloured spine) or solid (full severity fill)
- Unacknowledged, acknowledged and cleared states readable at a glance, with no extra icons
- Point-in-time or duration-span rendering — a span alarm draws a bar from start to clear
- Details popover with Acknowledge and Clear, or a custom widget action instead
- Drill into any day, with a breadcrumb back to the month you came from
- Crowded days collapse into
+N more alarms, which opens that day - Legend showing only the severities actually present, plus an alarm counter
- Calendar navigation drives the time window, so browsing months fetches the right alarms
- Fonts and colours configurable from the settings form, with no scripting
Data keys
None. This is an alarm-type widget: it reads whole alarm objects from the alarm subscription rather than telemetry or attribute keys, so the data key list stays empty and any keys you add are ignored.
These alarm fields are read, and all of them come from the platform automatically:
| Field | Type | Used for |
|---|---|---|
startTs (falls back to createdTime) | number | Position on the calendar |
endTs | number | End of the bar in duration-span mode; shown as Last update |
clearTs, ackTs | number | State, and the timestamps in the popover |
cleared, acknowledged, status | boolean / boolean / string | State, whichever shape your version reports |
type | string | Chip label |
severity | string | Chip colour |
originator, originatorName, originatorLabel | entity id / string / string | Popover, optional chip prefix, and the entity passed to actions |
assignee | object | Popover |
details | object or string | Popover |
Alarms are positioned by start time — when the condition first became true, not when the platform processed it. The two differ when a device reports backdated telemetry, and the calendar shows the event time.
Setup
- Add the widget to a dashboard and pick an alarm source: a device, asset, or entity alias.
- Set the Filter — status, severity, type, assignee, propagated alarms. The widget uses it as configured and has no duplicate controls of its own; filtering happens server-side, so narrowing it reduces what is fetched, not just what is drawn.
- Give the widget its own time window if the dashboard hosts other widgets — see below.
Time window
An alarm subscription only returns alarms whose start time falls inside a time window, and that window knows nothing about which month you are looking at. So the calendar drives it: every month step, year step, month or year pick, drill-down, back, and Today pushes the visible range as the new window, and so does the initial load. Rapid clicks collapse into a single query.
Whether that range stays on the widget or moves the whole dashboard is the widget’s own use dashboard time window checkbox — the platform’s existing switch, which this widget deliberately does not override. On a shared dashboard, give the calendar its own time window so navigating it does not drag other widgets along.
The range covers every visible cell, so the greyed leading and trailing days of a month are populated too instead of looking falsely empty.
One consequence worth knowing: an alarm that started before the visible month will not appear even though it was active throughout it. This is most noticeable in duration-span mode, where you might expect a bar across the whole grid.
Severity colours
| Severity | Default | Appearance |
|---|---|---|
| Critical | #d12730 | red |
| Major | #ff8300 | orange |
| Minor | #ffca3d | amber |
| Warning | #abab00 | olive |
| Indeterminate | #00838f | teal |
Each is configurable. In soft chip style the colour becomes a 13% tint for the fill plus a solid 3px spine; in solid style it fills the chip and the text switches between dark and white based on the background’s luminance, so a light severity like Minor stays readable.
State layers on top of severity, using the same colour rather than new ones:
| State | Chip |
|---|---|
| Unacknowledged | full-strength spine |
| Acknowledged | spine faded to 40% |
| Cleared | whole chip dimmed, title struck through |
Alarm details
Clicking an alarm opens a popover anchored to it, listing severity, type, originator, status,
every timestamp the alarm carries, assignee, and the details payload as formatted JSON. It
flips above the alarm when there is no room below and stays inside the widget, so it never
spills over the rest of the dashboard. Close it with the ✕, a click outside, Esc, or by
navigating the calendar.
The footer carries Acknowledge and Clear. Each button disappears once its state is
reached, both disable while a request is in flight, and a failure is reported inline — for
example Clear failed (403) — leaving the alarm untouched so you can retry. On success the
chip in the grid restyles immediately rather than waiting for the next poll.
For read-only audiences, turn Allow acknowledge / clear from the details popover off; the popover then shows details only. Note the widget cannot see a user’s permissions, so the buttons are hidden by this setting rather than automatically.
Actions
Two action sources appear in the widget’s Actions tab. Attach any action type: navigate to a dashboard state, open a custom dialog, run custom JavaScript, or trigger a mobile action.
| Source | Fires on | Entity passed | Extra parameters |
|---|---|---|---|
| Alarm click | An alarm chip, when Clicking an alarm is set to trigger the action | the alarm’s originator | alarm, alarmId, alarmType, alarmSeverity, alarmStatus, alarmStartTs, alarmDetails |
| Day click | Empty space in a day cell | — | date, dateTs, allDay |
Because the originator is passed as the entity, Navigate to new dashboard state works with
no extra wiring — the target state receives the device or asset that raised the alarm. Inside a
custom action the rest is on additionalParams, for example additionalParams.alarm.details.
Clicking an alarm chooses between the details popover and the action; they are deliberately exclusive, because a popover that flashed up before a navigation action took over would be noise. If no action is attached to a source, clicking does nothing — no error.
Configuration
Calendar
| Setting | Default | Description |
|---|---|---|
| First day of week | Monday | Monday, Sunday, or Saturday |
| 24-hour clock | on | Off switches times to AM/PM |
Alarms
| Setting | Default | Description |
|---|---|---|
| Alarm rendering | Point in time | Point places the chip at the start; Duration span draws a bar from start to clear, or to now if still active |
| Prefix title with originator name | off | Renders Generator 03 · lowFuel instead of lowFuel |
| Clicking an alarm | Show alarm details | Open the details popover, or fire the Alarm click widget action instead |
| Allow acknowledge / clear from the details popover | on | Off leaves the popover read-only |
| Alarms shown per day before clustering | 3 | Overflow collapses into +N more alarms. Set to 0 to fit as many as the row height allows |
| Alarms page size | 1024 | Upper bound on alarms fetched per range |
Style
| Setting | Default | Description |
|---|---|---|
| Alarm chip style | Soft | Soft (tinted fill, coloured spine) or Solid (full severity fill) |
| Alarm text | inherited | Size, weight, style, line height |
| Day number text / colour | #5f6368 | Font and colour of the date in each cell |
| Weekday header text / colour | #5f6368 | Font and colour of the Mon–Sun strip |
| Cell background | #ffffff | Grid cell fill |
| Grid line | #e4e6ea | Grid border colour |
| Show severity legend | on | Legend at the top right, listing only severities present |
| Show alarm counter | on | Reads 12 alarms · 5 active |
Accent states — the current day, hover highlights, focus rings, the now indicator, the cluster link — are not configurable on purpose: they derive from the platform’s primary colour so the widget matches the surrounding theme.
The widget declares no font family anywhere. It inherits whichever face the instance uses,
which is Roboto on a stock install and the correct face on a white-labeled one. The only
exception is the details JSON block, which asks for the generic monospace so keys and values
line up.
Requirements
Community and Professional editions both work; nothing here uses PE-only features. Rendering is done by FullCalendar 6, loaded from a CDN and declared in the widget’s resources:
https://cdn.jsdelivr.net/npm/fullcalendar@6.1.21/index.global.min.jshttps://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.21/locales-all.global.min.jsBrowsers viewing the dashboard need to reach that CDN. If your network blocks it, upload the bundle as a ThingsBoard JavaScript resource and point the widget’s resources at your copy. When the library cannot load, the widget shows the URL and a retry button rather than spinning indefinitely.
Month names follow the dashboard language automatically. Weekday names need the second resource above; without it they fall back to English. Alarm types and severity labels are not translated — types render exactly as the platform reports them.
Share Your Widget with the Community
Built a custom widget? Export it as a JSON from ThingsBoard and publish it to the IoT Hub through a simple 4-step wizard (Upload, Listing, Readme, Review & Submit). Share it with thousands of ThingsBoard developers worldwide and get featured in the catalog.