3. Using the Dashboard

The nodeWire dashboard along with nwpython script forms an application development framework designed to support the NodeWire paradigm. The Dashboard supports a variety of user interface widgets that enables you monitor and control your appliances, including, Buttons, Sliders, Gauges, Charts, Text Labels, Tables, Forms and all html tags.

Each widget can be bound to a node and a port, allowing end-users to monitor and control the appliance associated with the node and port.

nwpython script is a version of python that has been modified to support dataflow programming. However it does not support python’s control flow statements or any of the numerous python libraries. It however introduces a new ‘when’ keyword which is used to define rules and a ‘sequencer’ construct for defining state machines. It also has a special syntax that automatically represent nodes as objects, using python’s object syntax. More details on NodeWire script will be covered in a next chapter.

Layouts can be defined using either json or xml format.

3.1. Hello World

Go to the NodeWire Dashboard,

On the Menu, select ‘Develop’:

_images/developmenu.png

This should open the NodeWire development environment

_images/develop.png

To create a new layout, click on ‘Layouts’ on the left hand side of the window. Add ‘.xml’ to the generated filename and then click on create.

_images/newlayout.png

Finally type the following code into the layout editor on the righthand side of the code editor tab

<Switch title="press" />
_images/developswitch.png

Connecting the Switch to a signal

In order to make our Switch useful, we will need to connect it to a port. A port is any exported variable from a script. Ports are attached to a node. You export a variable by putting it in the inputs or outputs array. The name of the node created by the script is the same as the project name. Create a script by clicking on the ‘Scripts’ icon on the left hand side of the window. Then paste the following code into the script:

inputs = ['pressed']
outputs = ['pressed']
pressed = 0

Save the project in order to name it and thereby name the node as well. Let’s assume you name it ‘mynode’:

Now on the menu bar at the top of the window, press the ‘run’ menu item.

Next, modify the Switch to link it to the signal we just created:

<Switch title="press" node="mynode" port="pressed" />

Press the switch and see how it responds

Using a Slider

To use a slider instead of the button, replace the layout with the following

<Slider title="press" node="mynode" port="pressed" />

Using Containers

In order to use more than one widget, you will need to place them inside containers. NodeWire supports several kinds of containers including Frame, Div, Column and Row. Column arranges containers in a column while Row arranges then in a Row. Containers can contain other containers. Thus a combination of Row(s) and Column(s) can be used to create grids and other complex layouts.

The Div container is one that can be arbitrarily styled, similar to the HTML div. The styling supported are based on Semantic-UI templates.

The Frame is a container that can contain only one widget. If you want to have multiple widgets in a grid, you can place another container in it.

A column with two widgets

<Column>
    <Slider title="press" node="mynode" port="pressed" />
    <Status node="mynode" port="pressed" />
</Column>

Putting everything in a Frame

<Frame title="Hello World">
  <Column>
      <Slider title="press" node="mynode" port="pressed" />
      <Status node="mynode" port="pressed" />
  </Column>
</Frame>
_images/frame.png

Indicator

<Indicator title="press" node="mynode" port="pressed" />

Toggle Switch

<Toggle title="press" node="mynode" port="pressed" options="json:['off','on']" />

Tabs

<Tabs active="one">
  <tab title="one">
     <Switch />
  </tab>
  <tab title="two">
     <Slider />
  </tab>
</Tabs>

Accordion

<Accordion>
  <item title="one">
     <Switch/>
  </item>
  <item title="two">
     <Slider />
  </item>
</Accordion>

3.2. Gauges

The Gauges are based on Canvas Gauge.

Radial Gauge

<Gauge node="me" port="pressed" value="90" width="200" height="200" />
_images/radialgauge.png

You can change the reading dynamically. Type the following into the interactive window:

ui = node('user@mail.com')
ui.pressed = 50

where 'user@mail.com‘ is the account name of the currently logged-in user.

Linear Gauge

<LinearGauge node="me" port="pressed" majorTicks="json:['0.0', '0.2', '0.4', '0.6', '0.8', '1.0']" minValue="0" maxValue="1" />
_images/lineargauge.png
<LinearGauge node="me" port="pressed" width="100" majorTicks="json:['0.0', '0.2', '0.4', '0.6', '0.8', '1.0']" minValue="0" maxValue="1" />
_images/lineargaugelong.png

Compass

<Gauge
    node="me"
    port="pressed"
    width="200"
    height="200"
    minvalue="0"
    maxValue="360"
    majorTicks="json:['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']"
    minorTicks="22"
    ticksAngle="360"
    startAngle="180"
    strokeTicks="false"
    highlights="false"
    colorPlate="#222"
    colorMajorTicks="#f5f5f5"
    colorMinorTicks="#ddd"
    colorNumbers="#ccc"
    colorNeedle="rgba(240, 128, 128, 1)"
    colorNeedleEnd="rgba(255, 160, 122, .9)"
    valueBox="false"
    valueTextShadow="false"
    colorCircleInner="#fff"
    colorNeedleCircleOuter="#ccc"
    needleCircleSize="15"
    needleCircleOuter="false"
    animationRule="linear"
    needleType="line"
    needleStart="75"
    needleEnd="99"
    needleWidth="3"
    borders="true"
    borderInnerWidth="0"
    borderMiddleWidth="0"
    borderOuterWidth="10"
    colorBorderOuter="#ccc"
    colorBorderOuterEnd="#ccc"
    colorNeedleShadowDown="#222"
    borderShadowWidth="0"
    animationDuration="1500"
/>
_images/compassgauge.png

Colorful Gauges

<LinearGauge
    node="me"
    port="pressed"
    width="120"
    height="400"
    units="°C"
    minValue="0"
    startAngle="90"
    ticksAngle="180"
    valueBox="false"
    maxValue="220"
    majorTicks="json:[0,20,40,60,80,100,120,140,160,180,200,220]"
    minorTicks="2"
    strokeTicks="true"
    highlights="json:[ {'from': 100, 'to': 220, 'color': 'rgba(200, 50, 50, .75)'} ]"
    colorPlate="#fff"
    borderShadow-width="0"
    borders="false"
    needleType="arrow"
    needleWidth="2"
    needleCircleSize="7"
    needleCircleOuter="true"
    needleCircleInner="false"
    animationDuration="1500"
    animationRule="linear"
    barWidth="10"
    value="35"
/>
_images/coloredlineargauge.png
<Gauge
  node="me"
  port="pressed"
  width="200"
  height="200"
  units="Km/h"
  minValue="0"
  maxValue="1"
  majorTicks="json:['0.0', '0.2', '0.4', '0.6', '0.8', '1.0']"
  minorTicks="2"
  strokeTicks="true"
  highlights="json:[
  {'from': 0.6, 'to': 1, 'color': 'rgba(250, 50, 50, .75)'}
  ]"
  colorPlate="#fff"
  borderShadowWidth="2"
  borders="false"
  needleType="arrow"
  needleWidth="2"
  needleCircleSize="7"
  needleCircleOuter="true"
  needleCircleInner="false"
  animationDuration="1500"
  animationRule="linear"
/>
_images/redgauge.png

3.3. Forms

NodeWire supports special widgets that can only be used inside Forms (not Form is different from form).

The following Form allows the user to enter an employee name and age.

<Form node="me" port="employee" >
    <header label="New Employee" />
    <text name="name" title="Name" />
    <text name="age" title="Age" />
</Form>

In the form tag, we bind the form elements to a Node and Port. This way the fields of the Form are data-bound to the respective attributes of the port. In our example, the employee port of the me node will expect an object of the following type:

{
   name: '',
   age: #
}

me is a special node that represents the current user. In order to see the value entered by the user we need to create an monitor the ports of this node, create a script and type the following:

emp = {}
when me.employee:
    emp = me.employee

Now run the script and then enter data into the form.

To see the data entered by the user, type the following into the interactive console:

>> emp

Try this one as well

<Frame title="The Form">
  <Form node="me" port="employee" >
      <header label="New Employee" />
      <fields>
        <text name="name" title="Names" />
        <text name="age" title="Age" />
      </fields>
      <inlinefields>
         <radio title="one" name="num" />
         <radio title="two" name="num" />
      </inlinefields>
      <dropdown label="choose number" name="numbers">
          <item id="one" val="one" />
          <item id="two" val="two" />
      </dropdown>
  </Form>
</Frame>

3.4. Charts

Create a json layout and paste the following:

{
  "node": "meter",
  "port": "graph",
  "title": "Volume",
  "Type": "Chart",
  "options": {
    "type": "bar",
    "data": {
        "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
        "datasets": [{
            "label": "# of Votes",
            "data": [12, 19, 3, 5, 2, 3],
            "backgroundColor": [
                "rgba(255, 99, 132, 0.2)",
                "rgba(54, 162, 235, 0.2)",
                "rgba(255, 206, 86, 0.2)",
                "rgba(75, 192, 192, 0.2)",
                "rgba(153, 102, 255, 0.2)",
                "rgba(255, 159, 64, 0.2)"
            ],
            "borderColor": [
                "rgba(255,99,132,1)",
                "rgba(54, 162, 235, 1)",
                "rgba(255, 206, 86, 1)",
                "rgba(75, 192, 192, 1)",
                "rgba(153, 102, 255, 1)",
                "rgba(255, 159, 64, 1)"
            ],
            "borderWidth": 1
        }]
    },
    "options": {
        "scales": {
            "yAxes": [{
                "ticks": {
                    "beginAtZero":true
                }
            }]
        }
    }
  }
}

Dynamically update the chart

run this from the interactive console:

app('meter')
outputs = ['graph']
graph = {labels:['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],  data:[[1,2,3,4,5,6]]}

3.5. Designing a Custom layout for an Appliance

Create the Node

First create an arduino node using the following code:

#include <nnode.h>
#include <nesp8266link.h>

#define LED LED_BUILTIN

Node<int> node;
Esp8266Link link;

void setup() {
    Serial.begin(38400);
    link.begin();

    node.inputs = "led button volume";
    node.init("node02");
    node.setLink(&link);

    node.on("led",
       [](nString val, nString sender) {
          digitalWrite(LED,(int)val);
       }
    );

    pinMode(LED, OUTPUT);
}

void loop() {
    node.run();
}

Create The Layout

<Frame title="Custom Layout">
  <Column>
      <Indicator title="led" node="node02" port="led" />
      <Switch title="button" node="node02" port="button" />
      <Slider title="volume"  node="node02" port="volume" />
  </Column>
</Frame>
_images/customlayout.png

3.6. HTML

You can use all html tags in your layout except the <body> and <html> tags. And you are allowed to use styling from Semantic-Ui. In fact you can copy samples from the semantic-ui documentation and past into your layout design.

Note that the top-level must have a single overall parent. Also all tags must be in the xml format. For example the image tag should be written as <img src=”” /> instead of <img src=”“>.

Example

Attaching Events to html tags

Binding port values to html content

Navigation with html elements

Displaying Lists and Tables

3.7. Using ListViews