WaterSens is a Smart Water control, able to manage up to 4 latch valves remotely over long-range, low-power wireless communication protocols such as Sigfox and LoRaWAN, giving to the sensor a plus in different sectors such as agriculture, smart cities, and much more.

Following this guide, the user will be able to connect Watersens devices to Ubidots IoT application development platform through The Things Network (TTN), a LoRaWAN Network Server. Giving the option to the user of receive and send data from the end device over an LPWAN connectivity such as LoRaWAN to an IoT Platform.

The figure below shows the ecosystem proposed.

Requirements

Step-by-Step

1. Setting up TTN communication
    1.1. Define decoder for the variables uplinked by Watersens
    1.2. Create Ubidots integration to forward the data received from the device (Uplink Management)
    1.3. Create HTTP integration to send data from Ubidots to the device (Downlink Management)
2. Setting up Ubidots communication
    2.1. Verify WaterSens device creation in your Ubidots account.
    2.2. Add the widgets interesting for you. Battery, temperature, EVs status…
    2.3. Create your own widget 1: Irrigation schedule widget
    2.4. Create your own widget 2: Auto - Manual electrovalve control
3. Summary

A. ANNEX 1 - Irrigation schedule widget code
    A.1. HTML code
    A.2. CSS Code
   A.3. JS Code
B. ANNEX 2 - Auto-manual electrovalve control
    B.1. HTML code
    B.2. CSS code
    B.3. JS code

1. Setting up TTN communication

The first step is to set up the WaterSens device in the open LoRaWAN network server TTN.

Assuming the installation of the WaterSens is already done by the user, and the device is ready to send and receive data from TTN LoRaWAN server, let’s continue with the configuration of TTN to establish a bidirectional communication with Ubidots.

1.1. Define decoder for the variables uplinked by WaterSens

First, you have to decode the data received by the WaterSens. To do this, follow the next steps: 

1. Go to the "Console" of your TTN account.

2. Open the "Application" where your WaterSens device is already registered.

3. Select the "Payload Formats" option. Then, select “Custom” as payload format, and make sure the "Decoder" option is selected. 

4. Place the code below into the code block field available. To save the payload format assigned, make sure of press the "Save" button:

function Decoder(bytes, port) {
  var battery = ((bytes[0]>>7)*-100)+100;
  var temperature = bytes[0]-40;
  var rssi = bytes[1]-256;
  var snr = bytes[2];
  var valve_enabled = bytes[3]>>4;
  var valve_enabled_1 = (bytes[3]>>4)&0x01;
  var valve_enabled_2 = (bytes[3]>>5)&0x01;
  var valve_enabled_3 = (bytes[3]>>6)&0x01;
  var valve_enabled_4 = (bytes[3]>>7)&0x01;
  var valve_status = bytes[3]&0x0F;
  var valve_status_1 = bytes[3]&0x01;
  var valve_status_2 = (bytes[3]>>1)&0x01;
  var valve_status_3 = (bytes[3]>>2)&0x01;
  var valve_status_4 = (bytes[3]>>3)&0x01;
  var hour = bytes[4];
  var minute = bytes[5];
  var second = bytes[6];
  var pulse_counter = bytes[7]*256+bytes[8];
  var timetable_signature = bytes[9]*256+bytes[10];

  return {
    battery: battery,
    rssi: rssi,
    snr: snr,
    valve_enabled: valve_enabled,
    valve_enabled_1: valve_enabled_1,
    valve_enabled_2: valve_enabled_2,
    valve_enabled_3: valve_enabled_3,
    valve_enabled_4: valve_enabled_4,
    valve_status: valve_status,
    valve_status_1: valve_status_1,
    valve_status_2: valve_status_2,
    valve_status_3: valve_status_3,
    valve_status_4: valve_status_4,
    hour: hour,
    minute: minute,
    second: second,
    temperature: temperature,
    pulse_counter: pulse_counter,
    timetable_signature: timetable_signature
  }
}

Now, the data is in a human-readable format. If you take a look of what is returning the decoder code provided, you'll note that variables battery status, temperature, valves status, etc... are being returned in the JSON format which is the one allowed by Ubidots Platform. 

1.2. Create Ubidots integration to forward the data received from the device (Uplink Management)

To establish the communication with Ubidots, you have to set up a new integration in the same application used in the previous step. To do this, follow the next steps:

1. From the application overview, select the "Integration" option, located at the upper right side of the page.

2. Into the integration section, scroll down till the end and select "Ubidots" as integration. 

3. In the following window,  you have to configure the following parameters to  establish the Ubidots integration:

  • Process ID - ID defined by user to identify Ubidots integration in TTN platform
  • Access Key -  App access key (it is created automatically by TTN)
  •  - Ubidots Token. Refer to this guide, to know how to find it. 

 4. With all the parameters already assigned in the Integration setup, press the "Save Integration" button to save all the changes. 

Now, with this unique integration, you'll be able to forward all the data received from TTN (in the application where the device is registered) to your Ubidots account. With this, you'll be able to start analyzing your data with all the features offered by Ubidots. 

FYI: In case you have multiple WaterSens devices registered in the same application, all the devices will take this unique integration to forward the data to Ubidots. 

For more information about TTN & Ubidots integration for uplink messages management, check this guide.

1.3. Create HTTP integration To send data from Ubidots to the device (Downlink Management)

Downlink messages commonly manage devices' activation, update frequency rate of the devices, and more. In this case, we are going to setup downlink messages to enable an irrigation schedule changing via Ubidots. 

To be able to do this is required a second integration but in this case an HTTP integration. Follow the next steps to create the integration:

1. From the application overview, select the "Integration" option, located at the upper right side of the page.

2. Into the integration section, scroll down till find “HTTP Integration” and it as integration. 

3. In the following window, you have to configure the following parameters to setup the HTTP integration with Ubidots:

At this point, your application is able to receive data from Ubidots. Now the messages received are going to be scheduled by TTN, to send the data (irrigation schedule changing) to the device once an uplink message is received. 

For more information about TTN & Ubidots integration for downlink messages management, check this guide

2. Setting up Ubidots communication

Now with everything configured in TTN you're able to manage data bidirectionally with Ubidots and your WaterSens devices over LoRaWAN. 

To take advantage of all the Ubidots' features, let's get deep on how to visualize, control, and manage your data. 

2.1. Verify WaterSens devices creation in your Ubidots account

Open your Ubidots account, and from the upper central side of the page select the option "Devices > Devices". Into this section, you will see a new device created which is associated with the DeviceEUI assigned to the device registered in TTN. 

Troubleshooting & FAQs: If you're not able to visualize a new device, make sure your device is sending data properly to TTN. You can simply check this, from the "Data" tab option of your Application in TTN.

Select the devices created in the devices overview to visualize more details about the variables received. As you can see in the image below, the variables decoded are being received by Ubidots: 

  • Position in map if location has been introduced by user in location attribute
  • Battery status
  • Valve status
  • Hour / minute
  • Pulse counter 
  • And more...

2.2. Add the widgets interesting for you. Battery, temperature, EVs status…

Ubidots offers nice predefined widgets to add your data in a general dashboard. These widgets can be associated with the variables defined by the user for each device.

In this way, the data can be shown in an intuitive graphical mode.

To learn more about Ubidots Dashboard & Widgets, we highly recommend you check this guide.

2.3. Create your own widget 1: Irrigation schedule widget

As well as Ubidots platform has predefined widgets, there is the possibility to add custom widgets to the dashboard.

In this section, it will be explained how user can integrate an irrigation schedule configuration tool into the dashboard through the HTML canvas widget.

HTML canvas allows any user to add HTML, CSS, and JS code to monitor and control data from/to the devices.

To create the Widget HTML Canvas, make sure to follow carefully the next steps:

1. Go to "Data > Dashboard" section of your Ubidots account. 

2. Add a new widget by pressing the plus (+) icon located at the right upper side of the page.

3. Select the widget type. In this case, select "HTML Canvas" as a widget type. 

4. To create the widget successfully you make sure to add the HTML, CCS, and Javascript codes required, plus the library required. 

4.1. [Optional] In the "Name" field add a friendly name to your widget. 

4.2. In the "Body" option, select "open editor" and assign the respective codes. The HTML, CCS, and JavaScript codes can be found at the end of this guide (Annex A).

IMPORTANT NOTE: The parameters used in JS Code TTN_KEY_APP, TTN_ID_APP and TTN_ID_DEVICE are obtained from the TTN Application Console as it is shown in the screen captures below

4.3. In the "3rd Party Libraries" section, make sure of adding the following link: 

https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js

4.4. To finish, select the check green icon. 

At this point, you will be able to see a new widget created into your Ubidots Dashboard. The appearance of the irrigation schedule widget will be:

2.4. Create your own widget 2: Auto - Manual electrovalve control

To create a widget for controlling electrovalves in auto or manual mode, follow the same procedure explained in section 2.3, but in this case, the code to include in the widget creation must be the code listed in Annex B. Then, the widget will be similar than the exposed in the figure below

3. Summary

Now you're able to manage data in a bidirectional way with your WaterSens devices and Ubidots. Also, you are able to program automatic operation of the WaterSens schedulers through Ubidots Dashboard in just a few minutes!

Other developers also found helpful: 

A. ANNEX 1 - Irrigation Schedule Widget Code

A.1. HTML code

<div class="notification--container" id="notification">
  <p>Value sent successfully</p>
</div>

<div class="send-timetable--container">
  <p>
    <p>
      <label class="irr-op--label"> Irrigation Operation 1</label>
      <p>
        <label class="operation-type-start--label"> Start</label>
        <input type="number" max="23" min="0" placeholder="HH" class="time-input-hh" id="timeHhOp1Start" />
        <label class="clock-separator--label">:</label>
        <input type="number" step="15" max="45" min="0" placeholder="MM" class="time-input-mm" id="timeMmOp1Start" />
      </p>
    </p>
    <p>
      <label class="operation-type-stop--label"> Stop</label>
      <input type="number" max="23" min="0" placeholder="HH" class="time-input-hh" id="timeHhOp1Stop" />
      <label class="clock-separator--label">:</label>
      <input type="number" step="15" max="45" min="0" placeholder="MM" class="time-input-mm" id="timeMmOp1Stop" />
    </p>
  </p>

  <p>
    <p>
      <label class="irr-op--label"> Irrigation Operation 2</label>
      <p>
        <label class="operation-type-start--label"> Start</label>
        <input type="number" max="23" min="0" placeholder="HH" class="time-input-hh" id="timeHhOp2Start" />
        <label class="clock-separator--label">:</label>
        <input type="number" step="15" max="45" min="0" placeholder="MM" class="time-input-mm" id="timeMmOp2Start" />
      </p>
    </p>
    <p>
      <label class="operation-type-stop--label"> Stop</label>
      <input type="number" max="23" min="0" placeholder="HH" class="time-input-hh" id="timeHhOp2Stop" />
      <label class="clock-separator--label">:</label>
      <input type="number" step="15" max="45" min="0" placeholder="MM" class="time-input-mm" id="timeMmOp2Stop" />
    </p>
  </p>

  <p>
      <label class="irr-op--label"> Timetable ID</label>
      <p>
        <input type="number" max="65535" min="0" placeholder="ID" class="id-table--input" id="irrigationScheduleId" />  
      </p>
      <p>
        <button type="button" class="send-schedule--button" id="sendScheduleTable">Send Schedule</button>
      </p>        
  </p>
</div>

A.2. CSS code

.send-timetable--container {
  left: 50%;
 position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
}

.irr-op--label {
    color:white;
    clear:left;
    display: block;
    text-align:center;
    background-color: #2ccce4;
    font-size: 18px;
    font-family: "Open Sans";
}

.operation-type-start--label {
    color: white;
    background-color: #4ba651;
    clear:left;
    display: inline-block;
    text-align:right;
    padding-left:30px;
    font-family: "Open Sans";
}

.operation-type-stop--label {
    color: white;
    background-color: #f47373;  
    clear:left;
    display: inline-block;
    text-align:right;
    padding-left:30px;
    font-family: "Open Sans";
}

.clock-separator--label {
    font-family: "Open Sans";
}

.id-table--input {
border: none;
  border-bottom: 2px solid #28AECD;
    margin: 0 auto;
    padding-left:30px;
    width: 60px;
  display: block;
  font-size: 18px;
  outline: none;
}

.time-input-hh {
border: none;
  border-bottom: 2px solid #28AECD;
  display: inline-block;
  margin: 0;
  width: 45px;
  font-size: 18px;
  outline: none;
}

.time-input-mm {
border: none;
  border-bottom: 2px solid #28AECD;
  display: inline-block;
  margin: 0;
  width: 45px;
  font-size: 18px;
  outline: none;
}

.send-schedule--button {
    color: white;
   background: #ffae58;  
  border: 2px solid #ffae58;
  font-family: "Open Sans";
  font-size: 20px;
 margin-top: 20px;
 width: 100%;
}

.send-schedule--button:hover {
    color: #ffae58;
  background: white;  
  cursor: pointer;
}

.notification--container {
  display: none;
}

A.3. JS code

/* 
 * ---------------------------------------------------------------------------------------------------
 * User definitions. This data is linked with The Things Network parameters for device and application
 * ---------------------------------------------------------------------------------------------------
 */
var TTN_KEY_APP = ''; /* Put here the AppKEY from the TTN Application console*/
var TTN_ID_APP = ''; /* Put here the App ID from the TTN Application console*/
var TTN_ID_DEVICE = ''; /* Put here the Device ID from the TTN Application console*/

/*
 * ---------------------------------------------------------------------------------------------------
 * PLEASE DON'T MODIFY THE CODE BELOW
 * ---------------------------------------------------------------------------------------------------
 */

/* Internal variables */
var timeHhOp1Start = $ ('#timeHhOp1Start');
var timeMmOp1Start = $ ('#timeMmOp1Start');
var timeHhOp1Stop = $ ('#timeHhOp1Stop');
var timeMmOp1Stop = $ ('#timeMmOp1Stop');
var timeHhOp2Start = $ ('#timeHhOp2Start');
var timeMmOp2Start = $ ('#timeMmOp2Start');
var timeHhOp2Stop = $ ('#timeHhOp2Stop');
var timeMmOp2Stop = $ ('#timeMmOp2Stop');
var $sendScheduleTable = $ ('#sendScheduleTable');
var $irrigationScheduleId = $ ('#irrigationScheduleId');
var $notification = $('#notification');
var timeStart1;
var timeStop1;
var timeStart2;
var timeStop2;
var tableIrrigation = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var downlinkPayload = [1, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var downlinkPayloadRaw;

/* POST function */
function postValue(callback) {

  var url = "https://integrations.thethingsnetwork.org/ttn-eu/api/v2/down/" + TTN_ID_APP + "/html_integration_app_ubidots?key=" + TTN_KEY_APP;

  $notification.hide();
  $.ajax({
    method: "POST",
    url: url,
    data: JSON.stringify({
      "dev_id":TTN_ID_DEVICE,
      "port":1,
      "confirmed":false,
      "payload_raw": downlinkPayloadRaw.valueOf().toString()
    }),
    success: function (res) {
      callback(res.value);
    }
  });
}

/* Button Listener function */
$sendScheduleTable.on('click', function () {
  makePayload();  
  postValue(function (value) {
    $notification.show();
  });
});

/* Function to make Irrigation Table Payload */
function makePayload() {
  var j;
  /* Type of config frame */
  downlinkPayload[0] = 1;
  /* Timetable ID */
  downlinkPayload[1] = parseInt($irrigationScheduleId.val()/256, 10);
  downlinkPayload[2] = parseInt($irrigationScheduleId.val()%256, 10);
  /* Valves to modify (default all) */
  downlinkPayload[3] = 15;
  /* Copy timetable to Payload */
  calcTable();
  for(j=0;j<12;j++)
  {
    downlinkPayload[4+j] = tableIrrigation[j];
  }
  downlinkPayloadRaw = hexToBase64(toHexString(downlinkPayload));
  /* For debugging purpose */
  //alert(downlinkPayload.valueOf().toString());  
}

/* Function to adapt data from inputs to array */
function calcTable() {
  var j;
  var i;
  /* Clear table values */
  for(j = 0; j < 12; j++)
  {
    tableIrrigation[j] = 0;
  }
  /* Get values and calculate time in minutes */
  timeStart1 = parseInt(timeHhOp1Start.val(), 10)*60 +  parseInt(timeMmOp1Start.val(), 10);
  timeStop1 = parseInt(timeHhOp1Stop.val(), 10)*60 +  parseInt(timeMmOp1Stop.val(), 10);
  timeStart2 = parseInt(timeHhOp2Start.val(), 10)*60 +  parseInt(timeMmOp2Start.val(), 10);
  timeStop2 = parseInt(timeHhOp2Stop.val(), 10)*60 +  parseInt(timeMmOp2Stop.val(), 10);
  /* Get values in 15 minutes fractions */
  timeStart1 /= 15;
  timeStop1 /= 15;
  timeStart2 /= 15;
  timeStop2 /= 15;
  /* Calculate values for table - Operation 1 */    
  j=parseInt(timeStart1 / 8);
  i=parseInt(timeStart1 % 8);
  while((j <= parseInt(timeStop1/8)) && (j < 12))
  {
    while(((i < 8) && (j < parseInt(timeStop1/8))) || ((i < parseInt(timeStop1%8)) && (j == parseInt(timeStop1/8))))
    {
      tableIrrigation[j] |= (1<<i);
      i++;
    }
    i=0;
    j++;
  }
  /* Calculate values for table - Operation 2 */    
  j=parseInt(timeStart2 / 8);
  i=parseInt(timeStart2 % 8);
  while((j <= parseInt(timeStop2/8)) && (j < 12))
  {
    while(((i < 8) && (j < parseInt(timeStop2/8))) || ((i < parseInt(timeStop2%8)) && (j == parseInt(timeStop2/8))))
    {
      tableIrrigation[j] |= (1<<i);
      i++;
    }
    i=0;
    j++;
  }  
}

/* Function to adapt data array in HEX string */
function toHexString(byteArray) {
  return Array.from(byteArray, function(byte) {
    return ('0' + (byte & 0xFF).toString(16)).slice(-2);
  }).join('')
}

/* Function to adapt HEX string to Base64 string*/
function hexToBase64(str) {
    return btoa(String.fromCharCode.apply(null,
      str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))
    );
}

B. ANNEX 2 - Auto-manual electrovalve control

B.1. HTML code

<div class="ev-commander--container">
  <p>
    <label class="ev-op--label"> EV1 CONTROL <br></label>
    <p>
      <label class="auto-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;AUTO&nbsp;&nbsp;&nbsp; </label>
      <label class="switch">
        <input type="checkbox" id="ev1_mode">
        <span class="slider round"></span>
      </label>
      <label class="manual-op--label"> &nbsp;MANUAL&nbsp;</label>
    </p>
    <p>
      <label class="off-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OFF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
      <label class="switch">
        <input type="checkbox" id="ev1_status">
        <span class="slider round"></span>
      </label>
      <label class="on-op--label">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
    </p>  
   
  <p>
    <label class="ev-op--label"> EV2 CONTROL <br></label>
    <p>
      <label class="auto-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;AUTO&nbsp;&nbsp;&nbsp; </label>
      <label class="switch">
        <input type="checkbox" id="ev2_mode">
        <span class="slider round"></span>
      </label>
      <label class="manual-op--label"> &nbsp;MANUAL&nbsp;</label>
    </p>
    <p>
      <label class="off-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OFF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
      <label class="switch">
        <input type="checkbox" id="ev2_status">
        <span class="slider round"></span>
      </label>
      <label class="on-op--label">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
    </p>
   
  <p>
    <label class="ev-op--label"> EV3 CONTROL <br></label>
    <p>
      <label class="auto-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;AUTO&nbsp;&nbsp;&nbsp; </label>
      <label class="switch">
        <input type="checkbox" id="ev3_mode">
        <span class="slider round"></span>
      </label>
      <label class="manual-op--label"> &nbsp;MANUAL&nbsp;</label>
    </p>
    <p>
      <label class="off-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OFF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
      <label class="switch">
        <input type="checkbox" id="ev3_status">
        <span class="slider round"></span>
      </label>
      <label class="on-op--label">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
    </p>  

  <p>
    <label class="ev-op--label"> EV4 CONTROL <br></label>
    <p>
      <label class="auto-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;AUTO&nbsp;&nbsp;&nbsp; </label>
      <label class="switch">
        <input type="checkbox" id="ev4_mode">
        <span class="slider round"></span>
      </label>
      <label class="manual-op--label"> &nbsp;MANUAL&nbsp;</label>
    </p>
    <p>
      <label class="off-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OFF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
      <label class="switch">
        <input type="checkbox" id="ev4_status">
        <span class="slider round"></span>
      </label>
      <label class="on-op--label">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
    </p>  
   
  <p>
    <button type="button" class="send-ev_control--button" id="sendEvControl">Send EV control</button>
   
</div>

B.2. CSS code

.ev-commander--container {
  left: 50%;
 position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
}

/* Label */
 .ev-op--label {
    color:white;
    clear:left;
    display: block;
    text-align:center;
    background-color: #2ccce4;
    font-size: 18px;
    font-family: "Open Sans";
}

.auto-op--label{
    position: relative;
    display: inline-block;
    top:7px;
    color:white;
    background-color: #697689;
    font-size: 18px;
    font-family: "Open Sans";
}

.manual-op--label{
    position: relative;
    display: inline-block;
    top:7px;  
    color:white;
    clear:left;
    background-color: #ffae58;
    font-size: 18px;
    font-family: "Open Sans";  
}

.on-op--label{
    position: relative;
    display: inline-block;
    top:7px;
    color:white;
    clear:left;
    background-color: #37d67a;
    font-size: 18px;
    font-family: "Open Sans";  
    visibility:visible; /* hidden */
}

.off-op--label{
    position: relative;
    display: inline-block;
    top:7px;  
    color:white;
    clear:left;
    background-color: #f47373;
    font-size: 18px;
    font-family: "Open Sans";    
}

/* The switch - the box around the slider */
.switch {
  position: relative;
  display: inline-block;
  width: 60px;
  height: 34px;
}

/* Hide default HTML checkbox */
.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

/* The slider */
.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #697689;
  -webkit-transition: .4s;
  transition: .4s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  left: 4px;
  bottom: 4px;
  background-color: white;
  -webkit-transition: .4s;
  transition: .4s;
}

input:checked + .slider {
  background-color: #697689;
}

input:focus + .slider {
  box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
  -webkit-transform: translateX(26px);
  -ms-transform: translateX(26px);
  transform: translateX(26px);
}

/* Rounded sliders */
.slider.round {
  border-radius: 34px;
}

.slider.round:before {
  border-radius: 50%;
}

.send-ev_control--button {
    color: white;
   background: #ffae58;  
  border: 2px solid #ffae58;
  font-family: "Open Sans";
  font-size: 20px;
 margin-top: 20px;
 width: 100%;
}

.send-ev_control--button:hover {
    color: #ffae58;
  background: white;  
  cursor: pointer;
}

B.3. JS code

/* 

 * ---------------------------------------------------------------------------------------------------

 * User definitions. This data is linked with The Things Network parameters for device and application

 * ---------------------------------------------------------------------------------------------------

 */

var TTN_KEY_APP = ''; /* Put here the AppKEY from the TTN Application console*/

var TTN_ID_APP = ''; /* Put here the App ID from the TTN Application console*/

var TTN_ID_DEVICE = ''; /* Put here the Device ID from the TTN Application console*/

/*

 * ---------------------------------------------------------------------------------------------------

 * PLEASE DON'T MODIFY THE CODE BELOW

 * ---------------------------------------------------------------------------------------------------

 */

/* Internal variables */

var ev1_mode = document.getElementById("ev1_mode");

var ev2_mode = document.getElementById("ev2_mode");

var ev3_mode = document.getElementById("ev3_mode");

var ev4_mode = document.getElementById("ev4_mode");

var ev1_status = document.getElementById("ev1_status");

var ev2_status = document.getElementById("ev2_status");

var ev3_status = document.getElementById("ev3_status");

var ev4_status = document.getElementById("ev4_status");

var $sendEvControl = $ ('#sendEvControl');

var $notification = $('#notification');

var downlinkPayload = [3, 0];

var downlinkPayloadRaw;

/* POST function */

function postValue(callback) {

  var url = "https://integrations.thethingsnetwork.org/ttn-eu/api/v2/down/" + TTN_ID_APP + "/html_integration_app_ubidots?key=" + TTN_KEY_APP;

  $notification.hide();

  $.ajax({

    method: "POST",

    url: url,

    data: JSON.stringify({

      "dev_id":TTN_ID_DEVICE,

      "port":1,

      "confirmed":false,

      "payload_raw": downlinkPayloadRaw.valueOf().toString()

    }),

    success: function (res) {

      callback(res.value);

    }

  });

}

/* Button Listener function */

$sendEvControl.on('click', function () {

  makePayload();  

  postValue(function (value) {

    $notification.show();

  });

});

/* Function to make controle EVs Payload */

function makePayload() {

 

  /* Type of config frame */

  downlinkPayload[0] = 3;

  /* Mode operation valve */

  downlinkPayload[1] = 0;

  if(ev1_mode.checked)

  {

    downlinkPayload[1] += 16;

  }

  if(ev2_mode.checked)

  {

    downlinkPayload[1] += 32;

  }

  if(ev3_mode.checked)

  {

    downlinkPayload[1] += 64;

  }

  if(ev4_mode.checked)

  {

    downlinkPayload[1] += 128;

  }

 

  /* Status valve in manual mode*/

  if(ev1_status.checked)

  {

    downlinkPayload[1] += 1;

  }

  if(ev2_status.checked)

  {

    downlinkPayload[1] += 2;

  }

  if(ev3_status.checked)

  {

    downlinkPayload[1] += 4;

  }

  if(ev4_status.checked)

  {

    downlinkPayload[1] += 8;

  }

 

  downlinkPayloadRaw = hexToBase64(toHexString(downlinkPayload));

  /* For debugging purpose */

  //alert(downlinkPayload.valueOf().toString());  

}

/* Function to adapt data array in HEX string */

function toHexString(byteArray) {

  return Array.from(byteArray, function(byte) {

    return ('0' + (byte & 0xFF).toString(16)).slice(-2);

  }).join('')

}

/* Function to adapt HEX string to Base64 string*/

function hexToBase64(str) {

    return btoa(String.fromCharCode.apply(null,

      str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))

    );

}
Did this answer your question?