Skip to content

Hook Programming Guide

This is a programming guide for writing hooks. See full JavaScript Hooks SDK API reference here.

Hook Signature

A hook has the following function signature (see: InvokeFn). An InvokeParams object with the below properties is passed into each invocation of a hook. A conventional way to access the params that the hooks needs is through destructuring the params object.

module.exports = async ({
  points,
  sdk,
  groupVariables,
  globalVariables,
  config,
  args,
}) => {
  // access destructured params in function body
};

points

Selected points for this hook in the form of a queryable interface, ResultSet. If grouping has been specified in the hook configuration, this will the points for the group.

Each item in the ResultSet is a Point object.

module.exports = async ({ points, sdk, config, args }) => {
  // points has all the native JavaScript array methods
  console.log(points.length); // 10
  // there are also additional methods for selecting points
  const AirFlowSetpoint = points.byLabel("discharge-air-flow-sp").first();
  const Floor2Points = points.where((p) => p.attrs.spaceRef === "Floor 2");
};

Points also have a latestValue property that exposed the current value of the point

module.exports = async ({ points, sdk, config, args }) => {
  const DamperPosition = points.byLabel("damper-sensor").first();
  DamperPosition.latestValue.value; // last value as integer
  DamperPosition.latestValue.value; // last value as integer
  DamperPosition.latestValue.ts; // timestamp of latest value
};

To query if the latest value was changed since the last invocation, use the isChanged() method

module.exports = async ({ points, sdk, config, args }) => {
  const DamperPosition = points.byLabel("damper-sensor").first();
  const isChanged = DamperPosition.isChanged();
};

Points have a method called trueFor to check how long a point value has been in a certain condition. This is helpful for writing control logic.

module.exports = async ({ points }) => {
  const DamperPosition = points.byLabel("damper-sensor").first();
  const damperHigh = await DamperPosition.trueFor(
    "1m",
    (v) => Number(v.value) > 95
  );
};

Command reads and writes can be made by using methods on the point object. If the hook is configured to use a command contest, writes made with this API will automatically be associated with that groups' context.

module.exports = async ({ points }) => {
  const DischargeAirTempSP = points
    .byLabel("discharge-air-temp-setpoint")
    .first();
  // read
  const [initialValue, readError] = await DischargeAirTempSP.read();
  // write
  const [success, writeError] = await DischargeAirTempSP.write({ real: 70 });
};

Variables

Variables provide the ability to acess state across hook runs. Variables are defined in the hook configuration. Additionally, variables are backed by a Point object, and can be updated internally, or set over Bacnet.

groupVariables

Group variables are scoped to a hook group. An instance of a variable with the same properties is created for each group invocation of the hook. This enables the user to write reusable logic across groups.

The easiest way to query a variable is by it's label.

module.exports = async ({ groupVariables }) => {
  // this is a different variable for each group
  const DamperLoop = groupVariables.byLabel("inDamperLoop");
};

globalVariables

Global variables are scoped to the hook itself. The same instance of a global variable is passed into each hook group.

module.exports = async ({ globalVariables }) => {
  // this is the same variable for all groups
  const DamperLoop = globalVariables.byLabel("minSetpoint");
};

config

Configuration options for the application.

module.exports = async ({ config }) => {
  const apiUrl = config["apiUrl"];
};

args

Args (if any) for this function invocation (mostly used in “On Request” run mode when invoked from the Normal API)

module.exports = async ({ config }) => {
  const startingTemp = args["startingTemp"];
};

sdk

An instance of the Normal Applications SDK (i.e. RunParams) configured for this hook.

sdk.groupKey

The key of the current group (if grouping is not specified, this is an empty string)

module.exports = async ({ sdk }) => {
  const group = sdk.groupKey;
};

sdk.logEvent

Write to the event log. This message will show up the in integrated console and also in the run logs.

module.exports = async ({ sdk }) => {
  sdk.logEvent("Something happened");
};

sdk.http

An Axios client for accessing the Normal HTTP API. This Axios instance is configured to point to the internal Normal API, and will also automatically authenticate itself based on the Application authentication settings.

module.exports = async ({ sdk }) => {
  const commands = await sdk.http(
    "/api/v2/command"
  );
};

sdk.sleep

Pause execution for a given number of milliseconds

module.exports = async ({ sdk }) => {
  await sdk.sleep(2000);
};