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.
The objectives of today’s Studio are:
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.
Groups of three will be allowed if:
In groups of three the Manager must also take on the responsibilities of the Spokesperson.
As in the previous studios, all group members will need to be in a GitHub group (and repository)
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.
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.
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.
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:
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.
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:
Complete README.md
Q1
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.
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:
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:
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 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.
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)
Review lightModel.js
, which is a partially completed model of a light. It includes partial support for a single “listener”.
Complete README.md
Q3
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.
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.
Update the UI so that all the controls are hidden initially (i.e., the switches/sliders/etc., which, conveniently, are nested in div
s) 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.
When you’re done, the UI should:
light
object in the console.Complete README.md
Q4 and Q5
Commit your work to GitHub:
None for this!