Background

The last studio included a HTML UI for a Wi-Fi controlled smart lamp. You added JavaScript to it to show a proof-of-concept of how the UI will behave. You should have also gotten a sense of some significant elements of the “lifetime” of a User Interface, but the emulation of the lamp itself wasn’t very accurate.

Objectives

The objectives of today’s Studio are:

  • Continue to refine your understanding of User Interface (UI) development,
  • Use JavaScript objects,
  • Continue to study the use of JavaScript for event-driven programming, specifically creating an object that supports a “listener” (observer), and
  • Learn some common development patterns that allow development of complex projects to proceed despite interdependencies on different aspects of development.

1. Studio Setup

1.1. Find a Group & Select Roll Assignments

As usual, today’s studio will require groups of four.

Managers should be someone who has not been a manger before. The Manager should assign other roles and should assign them so that no individual has the same role they had in the last studio.

Each role has some specific responsibilities for the work, but everyone is expected to contribute to discussions and exploration of concepts.

Please take your role seriously. When you get your role, review the responsibilities and make sure you follow through on them.

Special Circumstances: Groups of three

Groups of three will be allowed if:

  • You plan to work with someone who is absent and all members of the group agree to work as a group of three for this studio.
  • There aren’t enough people present to form groups of four.

In groups of three the Manager must also take on the responsibilities of the Spokesperson.

1.2. Studio Distribution

As in the previous studios, all group members will need to be in a GitHub group (and repository)

  • The group’s Manager should create the repository. The GitHub group name should include the surnames of all group members.
  • All other members should join the group.

The link for this studio is: https://classroom.github.com/g/dK6ibM3h

Today both the Recorder and Technician will want to be able to work with the repository (i.e., it should be on at least two computers). The Technician will be working with JavaScript (and HTML+CSS). Chrome and Firefox have developer tools that are slightly better than Safari’s for today’s Studio, so the Technician should probably have access to Chrome/Firefox.

1.3. Recorder

The Recorder should update process/Roles.md to record everyone’s roles for this studio (use full names). In addition, the Recorder should take notes about the interaction of the overall session.

1.4. Prep Debrief

This is a little different than in previous studios

The Recorder should read the questions in process/Debrief.md to the group and the group should come to a consensus for each. The Technician should test any code, but the Recorder should enter the summary for each question.

2. Studio: JavaScript and UI Behavior

2.1. Overview

One of the greatest challenges in IoT (and many other computer science projects) is the complexity of a project. Often projects require UI (HTML+CSS & JavaScript), an embedded device (C/C++ programs are common), and interaction with cloud services (various protocols, data formats, and programming languages). It’s often difficult to test one component of a project until others that it depends on are completed. For example, it may be challenging to verify that your User Interface will work if you don’t yet have a device for it to work with, cloud services that connect the two together, and an agreement on the format and manner of data exchange.

One common way to overcome this dependency is through “stubs” or “proxies”. Usually the term “stub” refers to some temporary code in a function that will eventually be replaced with an actual implementation. The temporary code often allows partial testing of any callers that will be using the function. A “proxy” is a little more general, but here we’re referring to creating an object (as in a JavaScript Object). The object that will emulate some of the behavior of something else for the purposes of testing. (Eventually we may be able to update the code in the proxy to interact with the actual version rather than being merely a stand in)

In todays’ studio you will:

  • complete a proxy object that will emulate the behavior of a real lamp, including timing delays interacting with the lamp and other asynchronous behavior,
  • work with a CSS/JS library that provides a more satisfying UI, and
  • make the lifetime of our UI more realistic.

2.2 State & IoT

The concept of “state” or being “stateful” is relevant to almost all contemporary technologies in computer science. “State” refers to the current condition of some thing or system, and the current condition is usually a result of the history of things that have happened involving the thing/system. This week’s unit focuses on Objects in JavaScript and one of the primary concepts in Object-Oriented programming is “Encapsulation”, which is all about tracking and controlling state.

2.3 Physical Lamp State

An actual RGB lamp has to maintain state. In the coming weeks we will write the code that runs on the lamp and we will use variables to maintain this state.

Often the choice of how the state is implemented depends on how the state is expected to change over time. Consider our lamp’s main functions via these simple user stories:

  1. Users can turn it on/off
  2. Users can set it to a specific RGB color
  3. Users can set a feature that makes the light automatically turn off after a given time
  4. Users can adjust the time that will be used to turn the light off automatically

Complete README.md Q1

2.4 mainLightUI.html

Open mainLightUI.html in your browser. This UI relies on Material Design Lite, which was used because for styling. Note that there are two tabs, a couple of “switches”, some sliders, a few labels that will show the values of the sliders, some buttons, a few locations where a color can be shown, and a “loading” indicator (the element with id="loading").

Also open it in the Particle Desktop IDE. The switches and sliders are nested within corresponding label elements. For example,

<label id="onOffSwitchLabel" class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="onOffSwitch">
  <input id="onOffSwitch" type="checkbox" class="mdl-switch__input" >
  <span class="mdl-switch__label"></span>
</label>

The switch has an id of onOffSwitch, but it’s within a div named onOffSwitchLabel. This form of nesting is used by Material Design Lite to support interaction with the nested element. Switches have on() and off() methods that can be used to change their state via the label element (not the switch’s element). For example,

onOffSwitchLabel.MaterialSwitch.on()

Open the JavaScipt inspector and use the console to run the above command. Now issue a corresponding command to turn the switch off.

A slider’s label has a .MaterialSlider property that has a .change(X) method. The parameter to change() is the value to set the slider to.

Review mainLightUI.html and use the JavaScript console to change the values of sliders so you are aware of the structure/syntax needed to interact with them.

2.5. Lamp Communications

An IoT lamp will need some way to communicate with the outside world. Our lamp will use Wi-Fi and the cloud services from Particle.io. In coming studios we will use Sequence Diagrams to help us understand the interaction between different parts of our IoT lamp ecosystem, but for now there are a few key features that you need to be aware of:

  • Network speeds vary
  • Users want a responsive user interface

Consequently it will be important to avoid anything that blocks/waits for a response. Instead we will use an event-driven approach. Our code will often use a “listener”, comparable to the event listeners used so far. In previous work you’ve created listeners, but now it’s time to complete an object that supports a listener.

The state of the actual hardware-based light can be changed in a variety of ways:

  • It will have a physical on/off button that could be pressed
  • Multiple people could interact with it via an app
  • It will occasionally change state spontaneously due to internal conditions, like automatically turning off in response to the “auto-off” feature.

Users will want the UI to always be synchronized with the actual lamp. If the lamp is off, the switch on the UI should also be off. One of the easiest ways to achieve this sort of synchronization is via a listener. To keep things simple today we will only have one type of event and only allow a single listener for it. (The addEventListener adds an additional event listener. Essentially there is a list of event listeners that can be called, which allows a one-to-many distribution of information. It also uses names for events to allow selection of a specific type of event. We’ll encounter this pattern later in IoT too.)

There are two perspectives on the simpler listener we’ll create for this Studio:

The UI

The UI will provide the listener code to the light. The listener code will be run in response to any changes in the state of the light. Consequently, the parameter(s) to the listener should somehow describe the current state of the light and the listener should be able to extract these values and use them to update the UI.

The light

The light will need to support a listener. It will need to allow the UI to “supply” the code that should be run in response to any changes in state. Moreover, it will need to actually call that function whenever a change occurs and pass it parameters that correspond to the state.

To make matters a little simpler, the listener will also be run as soon as it is set. This will provide it with the initial state of the light (which could be used to set the initial condition of the UI)

2.6. Event Driven Issues

Review lightModel.js, which is a partially completed model of a light. It includes partial support for a single “listener”.

Complete README.md Q3

2.7 Completing the light

Review and complete the TODO items in lightModel.js. They are numbered and it’s best to do them in order. Be sure to test your work after each.

When you’re done with the light your UI should react to changes in the light’s state independent of what cause these changes. For example, if someone ran light.setPowered(false) in the console, this would simulate the light being turned off (perhaps by a physical switch on the light). This should cause the UI to update accordingly. Using the console directly interact with the features of the light and verify that your UI updates appropriately.

2.8 Simulating time

When you have gotten the basic UI working, update the way the listener is notified by using setTimeout.

Ensure that any notification will be delayed for 2s after the light’s state changed. This will allow you to simulate networking delays that may occur and to better test how “event-driven” your solution is.

Note that the this field in a method refers to the calling object, but when this is used within an anonymous function created within a method, it refers to the anonymous function, not the calling object! For example:

var myObject = {
  myData: 123,
  myFunction:  function() {
      setTimeout(function() {
          console.log(this.myData)   // This "this" is a problem!!! It refers to the
                                     // inner function, NOT myObject. There's no "myObject"
                                     // for the inner function, so it's "undefined", not 123.
        }, 1000);
  }
}

There are two approaches that avoid this problem. One is capturing the object in another local variable that can be accessed by the inner function:

var myObject = {
  myData: 123,
  myFunction:  function() {
      var capturedObject = this              // The capture to local variable
      setTimeout(function() {
          console.log(capturedObject.myData) // Access via the captured value
        }, 1000);
  }
}

Since we are using global object literals, another approach would use the name of the global.

var myObject = {
  myData: 123,
  myFunction:  function() {
      setTimeout(function() {
          console.log(myObject.myData)   // Note: myObject is used
        }, 1000);
  }
}

There are other approaches that may be better choices in some situations.

2.9 UI Initial State

Update the UI so that all the controls are hidden initially (i.e., the switches/sliders/etc., which, conveniently, are nested in divs) and are only revealed after their values are set for the first time. That will ensure that the screen is accurate when users can initially interact with it.

2.10 Full Functionality

When you’re done, the UI should:

  • Be hidden until the the initial state of all controls is set to correspond to the light.
  • Be changed whenever the light state changes, even if the changes occur due to function calls directly to the light object in the console.
  • Be changed when the light spontaneously changes dur to internal events, like the auto-off function activating.

Complete README.md Q4 and Q5

3. In-Class Checkout

Commit your work to GitHub:

  • The Recorder should complete changes, save them, commit them locally, then push to GitHub using GitHub desktop.
  • The Technician should save any changes, commit them locally, use GitHub desktop to Pull and merge any changes to the repo, and finally Commit/Push the additions back to GitHub.
  • Verify your commits from both Technician and Recorder on GitHub
  • Show a TA your progress

4. Post-Class Checkout

None for this!