Ir al contenido principal
Todas las coleccionesConecta tus dispositivos
Conecta tus dispositivos Watersens a Ubidots a través de LoRaWAN
Conecta tus dispositivos Watersens a Ubidots a través de LoRaWAN

Aprende a conectar tu Watersens a Ubidots, personalizar un horario de riego y establecer un control automático o manual para tus válvulas.

Sergio M avatar
Escrito por Sergio M
Actualizado hace más de 2 meses

WaterSens es un control de agua inteligente, capaz de gestionar hasta 4 válvulas de cierre de forma remota a través de protocolos de comunicación inalámbrica de largo alcance y bajo consumo como Sigfox y LoRaWAN, otorgando al sensor un plus en diferentes sectores como agricultura, ciudades inteligentes, y mucho más.

Siguiendo esta guía, el usuario podrá conectar los dispositivos WaterSens a Ubidots, la plataforma de desarrollo de aplicaciones IoT, a través de The Things Network (TTN), un servidor de red LoRaWAN. Dando la opción al usuario de recibir y enviar datos desde el dispositivo final a través de una conectividad LPWAN como LoRaWAN a una plataforma IoT.

La figura a continuación muestra el ecosistema propuesto.

Requisitos

Paso a Paso

1. Configuración de la comunicación TTN
1.1. Definir el decodificador para las variables enviadas por WaterSens
1.2. Crear la integración de Ubidots para reenviar los datos recibidos del dispositivo (Gestión de Uplink)
1.3. Crear la integración HTTP para enviar datos de Ubidots al dispositivo (Gestión de Downlink)
2. Configuración de la comunicación Ubidots
2.1. Verificar la creación del dispositivo WaterSens en tu cuenta de Ubidots.
2.2. Agregar los widgets que te interesen. Batería, temperatura, estado de válvulas…
2.3. Crear tu propio widget 1: Widget de horario de riego
2.4. Crear tu propio widget 2: Control de electrovalvula automático - manual
3. Resumen

A. ANEXO 1 - Código del widget de horario de riego
A.1. Código HTML
A.2. Código CSS
A.3. Código JS
B. ANEXO 2 - Control de electrovalvula automático-manual
B.1. Código HTML
B.2. Código CSS
B.3. Código JS

1. Configuración de la comunicación TTN

El primer paso es configurar el dispositivo WaterSens en el servidor de red LoRaWAN abierto TTN.

Suponiendo que la instalación del WaterSens ya ha sido realizada por el usuario, y que el dispositivo está listo para enviar y recibir datos del servidor LoRaWAN de TTN, continuemos con la configuración de TTN para establecer una comunicación bidireccional con Ubidots.

1.1. Definir el decodificador para las variables enviadas por WaterSens

Primero, debes decodificar los datos recibidos por el WaterSens. Para hacer esto, sigue los siguientes pasos:

1. Ve a la "Consola" de tu cuenta TTN.

2. Abre la "Aplicación" donde tu WaterSens ya está registrado.

3. Selecciona la opción "Formatos de Carga". Luego, selecciona “Personalizado” como formato de carga, y asegúrate de que la opción "Decodificador" esté seleccionada.

4. Coloca el código a continuación en el campo de bloque de código disponible. Para guardar el formato de carga asignado, asegúrate de presionar el botón "Guardar":

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  }}

Ahora, los datos están en un formato legible por humanos. Si echas un vistazo a lo que está devolviendo el código del decodificador proporcionado, notarás que las variables estado de batería, temperatura, estado de válvulas, etc... están siendo devueltas en el formato JSON que es el permitido por la plataforma Ubidots.

Para establecer la comunicación con Ubidots, debes configurar una nueva integración en la misma aplicación utilizada en el paso anterior. Para hacer esto, sigue los siguientes pasos:

1. Desde la vista general de la aplicación, selecciona la opción "Integración", ubicada en la parte superior derecha de la página.

2. En la sección de integración, desplázate hacia abajo hasta el final y selecciona "Ubidots" como integración.

3. En la siguiente ventana, debes configurar los siguientes parámetros para establecer la integración de Ubidots:

  • ID de Proceso - ID definido por el usuario para identificar la integración de Ubidots en la plataforma TTN

  • Clave de Acceso - Clave de acceso de la aplicación (se crea automáticamente por TTN)

  • - Token de Ubidots. Consulta esta guía para saber cómo encontrarlo.

4. Con todos los parámetros ya asignados en la configuración de la integración, presiona el botón "Guardar Integración" para guardar todos los cambios.

Ahora, con esta integración única, podrás reenviar todos los datos recibidos de TTN (en la aplicación donde el dispositivo está registrado) a tu cuenta de Ubidots. Con esto, podrás comenzar a analizar tus datos con todas las características que ofrece Ubidots.

Para tu información: En caso de que tengas múltiples dispositivos WaterSens registrados en la misma aplicación, todos los dispositivos tomarán esta integración única para reenviar los datos a Ubidots.

Para más información sobre la integración TTN & Ubidots para la gestión de mensajes uplink, consulta esta guía.

Los mensajes de downlink comúnmente gestionan la activación de dispositivos, la frecuencia de actualización de los dispositivos, y más. En este caso, vamos a configurar mensajes de downlink para habilitar un cambio en el horario de riego a través de Ubidots.

Para poder hacer esto se requiere una segunda integración, pero en este caso una integración HTTP. Sigue los siguientes pasos para crear la integración:

1. Desde la vista general de la aplicación, selecciona la opción "Integración", ubicada en la parte superior derecha de la página.

2. En la sección de integración, desplázate hacia abajo hasta encontrar “Integración HTTP” y selecciona esta como integración.

3. En la siguiente ventana, debes configurar los siguientes parámetros para configurar la integración HTTP con Ubidots:

En este punto, tu aplicación es capaz de recibir datos de Ubidots. Ahora los mensajes recibidos serán programados por TTN, para enviar los datos (cambio en el horario de riego) al dispositivo una vez que se reciba un mensaje uplink.

Para más información sobre la integración TTN & Ubidots para la gestión de mensajes de downlink, consulta esta guía

2. Configuración de la comunicación Ubidots

Ahora, con todo configurado en TTN, puedes gestionar datos de manera bidireccional con Ubidots y tus dispositivos WaterSens a través de LoRaWAN.

Para aprovechar todas las características de Ubidots, profundicemos en cómo visualizar, controlar y gestionar tus datos.

2.1. Verificar la creación de dispositivos WaterSens en tu cuenta de Ubidots

Abre tu cuenta de Ubidots, y desde la parte superior central de la página selecciona la opción "Dispositivos > Dispositivos". En esta sección, verás un nuevo dispositivo creado que está asociado con el DeviceEUI asignado al dispositivo registrado en TTN.

Solución de problemas & Preguntas frecuentes: Si no puedes visualizar un nuevo dispositivo, asegúrate de que tu dispositivo esté enviando datos correctamente a TTN. Puedes verificar esto desde la opción de la pestaña "Datos" de tu aplicación en TTN.

Selecciona los dispositivos creados en la vista general de dispositivos para visualizar más detalles sobre las variables recibidas. Como puedes ver en la imagen a continuación, las variables decodificadas están siendo recibidas por Ubidots:

  • Posición en el mapa si la ubicación ha sido introducida por el usuario en el atributo de ubicación

  • Estado de la batería

  • Estado de la válvula

  • Hora / minuto

  • Contador de pulsos

  • Y más...

2.2. Agregar los widgets que te interesen. Batería, temperatura, estado de EVs…

Ubidots ofrece bonitos widgets predefinidos para agregar tus datos en un panel de control general. Estos widgets pueden asociarse con las variables definidas por el usuario para cada dispositivo.

De esta manera, los datos pueden mostrarse de forma gráfica e intuitiva.

Para aprender más sobre el panel de control y los widgets de Ubidots, te recomendamos consultar esta guía.

2.3. Crear tu propio widget 1: Widget de horario de riego

Así como la plataforma Ubidots tiene widgets predefinidos, existe la posibilidad de agregar widgets personalizados al panel de control.

En esta sección, se explicará cómo el usuario puede integrar una herramienta de configuración de horario de riego en el panel de control a través del widget de lienzo HTML.

El lienzo HTML permite a cualquier usuario agregar código HTML, CSS y JS para monitorear y controlar datos desde/hacia los dispositivos.

Para crear el Widget de Lienzo HTML, asegúrate de seguir cuidadosamente los siguientes pasos:

1. Ve a la sección "Datos > Panel de control" de tu cuenta de Ubidots.

2. Agrega un nuevo widget presionando el ícono de más (+) ubicado en la parte superior derecha de la página.

3. Selecciona el tipo de widget. En este caso, selecciona "HTML Canvas" como tipo de widget.

4. Para crear el widget con éxito, asegúrate de agregar los códigos HTML, CSS y JavaScript requeridos, además de la biblioteca necesaria.

4.1. [Opcional] En el campo "Nombre", agrega un nombre amigable para tu widget.

4.2. En la opción "Cuerpo", selecciona "abrir editor" y asigna los códigos respectivos. Los códigos HTML, CSS y JavaScript se pueden encontrar al final de esta guía (Anexo A).

NOTA IMPORTANTE: Los parámetros utilizados en el código JS TTN_KEY_APP, TTN_ID_APP y TTN_ID_DEVICE se obtienen de la Consola de Aplicaciones TTN como se muestra en las capturas de pantalla a continuación

4.3. En la sección "Bibliotecas de Terceros", asegúrate de agregar el siguiente enlace:

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

4.4. Para finalizar, selecciona el ícono verde de verificación.

En este punto, podrás ver un nuevo widget creado en tu Panel de Control de Ubidots. La apariencia del widget de horario de riego será:

2.4. Crear tu propio widget 2: Control de electrovalvula automático - manual

Para crear un widget para controlar electrovalvulas en modo automático o manual, sigue el mismo procedimiento explicado en sección 2.3, pero en este caso, el código a incluir en la creación del widget debe ser el código listado en Anexo B. Luego, el widget será similar al expuesto en la figura a continuación

3. Resumen

Ahora puedes gestionar datos de manera bidireccional con tus dispositivos WaterSens y Ubidots. Además, ¡puedes programar la operación automática de los programadores WaterSens a través del Panel de Control de Ubidots en solo unos minutos!

Otros desarrolladores también encontraron útil:

A. ANEXO 1 - Código del Widget de Horario de Riego

A.1. Código HTML

<div class="notification--container" id="notification">  <p>Valor enviado con éxito</p></div><div class="send-timetable--container">  <p>    <p>      <label class="irr-op--label"> Operación de Riego 1</label>      <p>        <label class="operation-type-start--label"> Inicio</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"> Detener</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"> Operación de Riego 2</label>      <p>        <label class="operation-type-start--label"> Inicio</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"> Detener</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"> ID de Horario</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">Enviar Horario</button>      </p>          </p></div>

A.2. Código CSS

.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. Código JS

/*  * --------------------------------------------------------------------------------------------------- * Definiciones del usuario. Estos datos están vinculados con los parámetros de The Things Network para el dispositivo y la aplicación * ---------------------------------------------------------------------------------------------------  */var TTN_KEY_APP = ''; /* Coloca aquí la AppKEY de la consola de aplicaciones TTN*/var TTN_ID_APP = ''; /* Coloca aquí el ID de la aplicación de la consola de aplicaciones TTN*/var TTN_ID_DEVICE = ''; /* Coloca aquí el ID del dispositivo de la consola de aplicaciones TTN*//*  * --------------------------------------------------------------------------------------------------- * POR FAVOR NO MODIFIQUES EL CÓDIGO A CONTINUACIÓN * ---------------------------------------------------------------------------------------------------  *//* Variables internas */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;/* Función POST */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);    }  });}/* Función de Listener del Botón */$sendScheduleTable.on('click', function () {  makePayload();    postValue(function (value) {    $notification.show();  });});/* Función para hacer el Payload de la Tabla de Riego */function makePayload() {  var j;  /* Tipo de marco de configuración */  downlinkPayload[0] = 1;  /* ID de Horario */  downlinkPayload[1] = parseInt($irrigationScheduleId.val()/256, 10);  downlinkPayload[2] = parseInt($irrigationScheduleId.val()%256, 10);  /* Válvulas a modificar (por defecto todas) */  downlinkPayload[3] = 15;  /* Copiar horario al Payload */  calcTable();  for(j=0;j<12;j++)  {    downlinkPayload[4+j] = tableIrrigation[j];  }  downlinkPayloadRaw = hexToBase64(toHexString(downlinkPayload));  /* Para propósitos de depuración */  //alert(downlinkPayload.valueOf().toString());   }/* Función para adaptar datos de entradas a array */function calcTable() {  var j;  var i;  /* Limpiar valores de la tabla */  for(j = 0; j < 12; j++)  {    tableIrrigation[j] = 0;  }  /* Obtener valores y calcular tiempo en minutos */  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);  /* Obtener valores en fracciones de 15 minutos */  timeStart1 /= 15;  timeStop1 /= 15;  timeStart2 /= 15;  timeStop2 /= 15;  /* Calcular valores para la tabla - Operación 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++;  }  /* Calcular valores para la tabla - Operación 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++;  }  }/* Función para adaptar datos del array a cadena HEX */function toHexString(byteArray) {  return Array.from(byteArray, function(byte) {    return ('0' + (byte & 0xFF).toString(16)).slice(-2);  }).join('')}/* Función para adaptar cadena HEX a cadena Base64*/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. ANEXO 2 - Control de electrovalvula automático-manual

B.1. Código HTML

<div class="ev-commander--container">  <p>    <label class="ev-op--label"> CONTROL EV1 <br></label>    <p>      <label class="auto-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;AUTOMÁTICO&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;APAGADO&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;ENCENDIDO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>    </p>        <p>    <label class="ev-op--label"> CONTROL EV2 <br></label>    <p>      <label class="auto-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;AUTOMÁTICO&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;APAGADO&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;ENCENDIDO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>    </p>       <p>    <label class="ev-op--label"> CONTROL EV3 <br></label>    <p>      <label class="auto-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;AUTOMÁTICO&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;APAGADO&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;ENCENDIDO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>    </p>    <p>    <label class="ev-op--label"> CONTROL EV4 <br></label>    <p>      <label class="auto-op--label"> &nbsp;&nbsp;&nbsp;&nbsp;AUTOMÁTICO&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;APAGADO&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;ENCENDIDO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>    </p>        <p>    <button type="button" class="send-ev_control--button" id="sendEvControl">Enviar control de EV</button>    </div>

B.2. Código CSS

.ev-commander--container {  	left: 50%;	  position: absolute;  	top: 50%;  	transform: translate(-50%, -50%);}/* Etiqueta */  .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";     }/* El interruptor - la caja alrededor del control deslizante */.switch {  position: relative;  display: inline-block;  width: 60px;  height: 34px;}/* Ocultar el checkbox HTML por defecto */.switch input {  opacity: 0;  width: 0;  height: 0;}/* El control deslizante */.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);}/* Interruptores redondeados */.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. Código JS

/*  * --------------------------------------------------------------------------------------------------- * Definiciones del usuario. Estos datos están vinculados con los parámetros de The Things Network para el dispositivo y la aplicación * ---------------------------------------------------------------------------------------------------  */var TTN_KEY_APP = ''; /* Coloca aquí la AppKEY de la consola de aplicaciones TTN*/var TTN_ID_APP = ''; /* Coloca aquí el ID de la aplicación de la consola de aplicaciones TTN*/var TTN_ID_DEVICE = ''; /* Coloca aquí el ID del dispositivo de la consola de aplicaciones TTN*//*  * --------------------------------------------------------------------------------------------------- * POR FAVOR NO MODIFIQUES EL CÓDIGO A CONTINUACIÓN * ---------------------------------------------------------------------------------------------------  *//* Variables internas */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;/* Función POST */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);    }  });}/* Función de Listener del Botón */$sendEvControl.on('click', function () {  makePayload();    postValue(function (value) {    $notification.show();  });});/* Función para hacer el Payload de control de EVs */function makePayload() {    /* Tipo de marco de configuración */  downlinkPayload[0] = 3;  /* Modo de operación de la válvula */  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;  }    /* Estado de la válvula en modo manual*/  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));  /* Para propósitos de depuración */  //alert(downlinkPayload.valueOf().toString());   }/* Función para adaptar datos del array a cadena HEX */function toHexString(byteArray) {  return Array.from(byteArray, function(byte) {    return ('0' + (byte & 0xFF).toString(16)).slice(-2);  }).join('')}/* Función para adaptar cadena HEX a cadena Base64*/function hexToBase64(str) {    return btoa(String.fromCharCode.apply(null,      str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "))    );}
¿Ha quedado contestada tu pregunta?