Build a Monitoring Dashboard#

In this tutorial, we will construct a display-only dashboard that refreshes the entire page periodically.

These types of dashboards are suitable for displaying on a large screen running continuously, such as in a call center, control room, or trading area.

Monitoring Dashboard

Dependencies
panel scipy
Code
import numpy as np
import panel as pn
from scipy.interpolate import interp1d

pn.extension()

ACCENT = "#008080"  # teal

WIND_SPEED_STD_DEV = 2.0
WIND_SPEED_MEAN = 8.0

WIND_SPEEDS = np.array([0, 3, 6, 9, 12, 15, 18, 21])  # Wind speed (m/s)
POWER_OUTPUTS = np.array([0, 0.03, 0.20, 0.6, 1.0, 1.0, 0, 0])  # Power output (MW)

# Extract Data

def get_wind_speed():
    # Replace with your wind speed source
    return round(np.random.normal(WIND_SPEED_MEAN, WIND_SPEED_STD_DEV), 1)

wind_speed = get_wind_speed()

# Transform Data

def get_power_output(wind_speed):
    # Replace with your power output calculation
    power_interpolation = interp1d(
        WIND_SPEEDS, POWER_OUTPUTS, kind="linear", fill_value="extrapolate"
    )
    return np.round(power_interpolation(wind_speed), 2)

power_output = get_power_output(wind_speed)

# View Data

wind_speed_view = pn.ui.Number(
    label="Wind Speed",
    value=wind_speed,
    format="{value} m/s",
    colors=[(10, ACCENT), (100, "red")],
)
power_output_view = pn.ui.Number(
    label="Power Output",
    value=power_output,
    format="{value} MW",
    colors=[(0.8, ACCENT), (1.0, "red")],
)
indicators = pn.ui.FlexBox(wind_speed_view, power_output_view)

# Layout and style with a Page

pn.ui.Page(
    title="WTG Monitoring Dashboard",
    main=[indicators],
    theme_config={"palette": {"primary": {"main": ACCENT}}},
    theme="dark",
    theme_toggle=False,
    meta_refresh="2",  # Automatically refresh every 2 seconds
).servable();  # The ; is only needed if used in a notebook

Install the dependencies#

Please ensure that Panel and SciPy are installed.

pip install panel scipy
conda install -y -c conda-forge panel scipy

Explanation#

Let’s dissect the code that brings our dashboard to life:

import numpy as np
import panel as pn
from scipy.interpolate import interp1d

We import the necessary libraries: NumPy for numerical computations, Panel for building interactive web apps, and SciPy for interpolation.

pn.extension()

We load the JavaScript dependencies required by our app.

ACCENT = "#008080"  # teal

WIND_SPEED_STD_DEV = 2.0
WIND_SPEED_MEAN = 8.0

WIND_SPEEDS = np.array([0, 3, 6, 9, 12, 15, 18, 21])  # Wind speed (m/s)
POWER_OUTPUTS = np.array([0, 0.03, 0.20, 0.6, 1.0, 1.0, 0, 0])  # Power output (MW)

We set some constants related to wind speed and power output data.

def get_wind_speed():
    return round(np.random.normal(WIND_SPEED_MEAN, WIND_SPEED_STD_DEV), 1)

wind_speed = get_wind_speed()

A function get_wind_speed() generates a random wind speed value based on a normal distribution.

def get_power_output(wind_speed):
    power_interpolation = interp1d(
        WIND_SPEEDS, POWER_OUTPUTS, kind="linear", fill_value="extrapolate"
    )
    return np.round(power_interpolation(wind_speed), 2)

power_output = get_power_output(wind_speed)

Another function get_power_output(wind_speed) calculates the corresponding power output based on the provided wind speed using linear interpolation.

wind_speed_view = pn.ui.Number(
    label="Wind Speed",
    value=wind_speed,
    format="{value} m/s",
    colors=[(10, ACCENT), (100, "red")],
)
power_output_view = pn.ui.Number(
    label="Power Output",
    value=power_output,
    format="{value} MW",
    colors=[(0.8, ACCENT), (1.0, "red")],
)
indicators = pn.ui.FlexBox(wind_speed_view, power_output_view)
indicators

We create Number indicators to display the wind speed and power output. We keep their large default font size so they can be read from across the room. The colors argument is a list of (threshold, color) pairs: the value is shown in the first color whose threshold it does not exceed, so the wind speed turns red above 10 m/s and the power output turns red above 0.8 MW, as the turbine approaches its rated output. The FlexBox layout will arrange the indicators horizontally or vertically depending on the screen size.

pn.ui.Page(
    title="WTG Monitoring Dashboard",
    main=[indicators],
    theme_config={"palette": {"primary": {"main": ACCENT}}},
    theme="dark",
    theme_toggle=False,
    meta_refresh="2",  # Automatically refresh every 2 seconds
).servable()
pn.ui.Page(
    title="WTG Monitoring Dashboard",
    main=[indicators],
    theme_config={"palette": {"primary": {"main": ACCENT}}},
    theme="dark",
    theme_toggle=False,
    meta_refresh="2",  # Automatically refresh every 2 seconds
).servable();  # The ; is only needed if used in a notebook

Finally, we construct the dashboard using a Page. We set the accent color via theme_config, fix the theme to "dark" with the theme toggle hidden, and enable automatic refreshing every 2 seconds with meta_refresh.

Note

In the example, we use a meta_refresh rate of 2 for illustration purposes. For real use cases, we recommend meta_refresh rates of 15 or above. For lower refresh rates we would be using a Periodic Callback or generators in combination with a meta_refresh rate of 900 or above.

Serve the App#

Now serve the app with:

panel serve app.py --dev
panel serve app.ipynb --dev

Open http://localhost:5006/app

It should resemble

Monitoring Dashboard

Recap#

In this tutorial, we have built a basic monitoring dashboard that refreshes the entire page periodically. We have used the Number indicator, the FlexBox layout and the Page.

We used the meta_refresh argument of the Page to automatically refresh the dashboard periodically.