with

A taste of hardware connectivity on the progressive web 

Majid Hajian

<ME />

import HelloWorld from "@/components/HelloWorld.vue";
export default {
  name: "whoAmI",
  data: function() {
    return {
      me: {
        name: "Majid Hajian",
        location: "Oslo, Norway",
        description: "Passionate Web Developer and Community Leader",
        main: "Javascripter",
        homepage: "https://www.majidhajian.com",
        socials: {
          twitter: "https://www.twitter.com/mhadaily",
          github: "https://www.github.com/mhadaily"
        },
        books: {
          "Progressive Web App with Angular": {
             version: "1.0.0", 
             publishedBy: "Apress", 
             foundOn: "Amazon",
          }
        },
        devDependencies: {
          coffee: "2.5.0", mac: "10.14.4",
        },
        engines: {
          VueJsOslo: "Orginizer",
          MobileEraConference: "Orginizer",
          ngVikingsConference: "Orginizer",
          MobileMeetupOslo: "Co-Orginizer",
        }
      }
    };
  },
  components: {
    HelloWorld
  },
};

The Talk's Aim

To show beyond the web and unleash the power of hardware connectivity on the progressive web and of course using Vue.js

SIMPLIFIED

Topics I'm covering

WebBluetooth
WebUSB
WebNFC
Future
Progressive Web Apps

Always start with why ?

Faster JavaScript engines

Going beyond online

Operating system Integration

Access to hardware

Authentication & Payments

Reliable

Fast

Engaging

Secure

Progressively

Enhancement

Native-like

User Experince

Service Worker

Web App Manifest

Yesssss,

Web is capable of doing that

Web Bluetooth

Wait a sec...

Bluetooth???

Classic Bluetooth

Classic Bluetooth

sucks

Classic Bluetooth

sucks

Bluetooth Smart

Classic Bluetooth

Bluetooth Smart

Bluetooth Low Energy
Bluetooth4, 5

BLE, Bluetooth LE

sucks

Central

Peripheral

Central

Generic attribute profile

Generic attribute profile

GATT

Central

Peripheral

Client

Server

Server

Server

Service

Service

Server

Service

Service

Characteristic

Characteristic

Characteristic

Characteristic

Server

Service

Service

Characteristic

properties

Value

Characteristic

properties

Value

Characteristic

properties

Value

Characteristic

properties

Value

Server

Service

Service

Characteristic

properties

Value

Characteristic

properties

Value

Characteristic

properties

Value

Characteristic

properties

Value

Services and characteristics are identified by 16 or 128 bit UUIDs.  

characteristics have different properties such as read, write, write without response, notify, indicate and ...

Values are just an array of bytes

Please show me code ! 

Before that...

requestHTMLButton.addEventListener('click', async function() {

    // Logic
  
});
<template>
  <a @click="discover">Click me!</a>
</template>

<script>
export default {
  methods: {
    discover: async function(event) {

      // Logic

    }
  }
}
</script>

OR

requestHTMLButton.addEventListener('click', async function() {

    // Logic
  
});
<template>
  <a @click="discover">Click me!</a>
</template>

<script>
export default {
  methods: {
    discover: async function(event) {

      // Logic

    }
  }
}
</script>

OR

User Interaction

HTTPS Only

  const options = {
    filters: [
      { namePrefix: 'Polar' }
    ],
  };
  const device = await navigator.bluetooth.requestDevice(options);
  const server = await device.gatt.connect();
  const service = await server.getPrimaryService(SERVICE);
  const character = await service.getCharacteristic(CHARACTERISTIC);
  
  character.oncharacteristicvaluechanged = () => {
    const value = event.target.value;
    const currentHeartRate = parseHeartRate(value);
    // Update UI or do your logic 
  };

  await character.startNotifications();
  

Request the device

  const options = {
    filters: [
      { namePrefix: 'Polar' }
    ],
  };
  const device = await navigator.bluetooth.requestDevice(options);
  const server = await device.gatt.connect();
  const service = await server.getPrimaryService(SERVICE);
  const character = await service.getCharacteristic(CHARACTERISTIC);
  
  character.oncharacteristicvaluechanged = () => {
    const value = event.target.value;
    const currentHeartRate = parseHeartRate(value);
    // Update UI or do your logic 
  };

  await character.startNotifications();
  

Request the device

  const options = {
    filters: [
      { namePrefix: 'Polar' }
    ],
  };
  const device = await navigator.bluetooth.requestDevice(options);
  const server = await device.gatt.connect();
  const service = await server.getPrimaryService(SERVICE);
  const character = await service.getCharacteristic(CHARACTERISTIC);
  
  character.oncharacteristicvaluechanged = () => {
    const value = event.target.value;
    const currentHeartRate = parseHeartRate(value);
    // Update UI or do your logic 
  };

  await character.startNotifications();
  

Connect to Gatt

  const options = {
    filters: [
      { namePrefix: 'Polar' }
    ],
  };
  const device = await navigator.bluetooth.requestDevice(options);
  const server = await device.gatt.connect();
  const service = await server.getPrimaryService(SERVICE);
  const character = await service.getCharacteristic(CHARACTERISTIC);
  
  character.oncharacteristicvaluechanged = () => {
    const value = event.target.value;
    const currentHeartRate = parseHeartRate(value);
    // Update UI or do your logic 
  };

  await character.startNotifications();
  

Get a Service

  const options = {
    filters: [
      { namePrefix: 'Polar' }
    ],
  };
  const device = await navigator.bluetooth.requestDevice(options);
  const server = await device.gatt.connect();
  const service = await server.getPrimaryService(SERVICE);
  const character = await service.getCharacteristic(CHARACTERISTIC);
  
  character.oncharacteristicvaluechanged = () => {
    const value = event.target.value;
    const currentHeartRate = parseHeartRate(value);
    // Update UI or do your logic 
  };

  await character.startNotifications();
  

Get a Characteristic

  const options = {
    filters: [
      { namePrefix: 'Polar' }
    ],
  };
  const device = await navigator.bluetooth.requestDevice(options);
  const server = await device.gatt.connect();
  const service = await server.getPrimaryService(SERVICE);
  const character = await service.getCharacteristic(CHARACTERISTIC);
  
  character.oncharacteristicvaluechanged = () => {
    const value = event.target.value;
    const currentHeartRate = parseHeartRate(value);
    // Update UI or do your logic 
  };

  await character.startNotifications();
  

Start Notifying 

Listen to value change and read value

  const characteristic = await service.getCharacteristic(`heart_rate_control_point`);
  
  console.log('Writing Heart Rate Control Point Characteristic...');

  // Writing 1 is the signal to reset energy expended.
  const resetEnergyExpended = Uint8Array.of(1);
  await characteristic.writeValue(resetEnergyExpended);
  
  const characteristic = await service.getCharacteristic(`battery_level`);
  

  const value = await characteristic.readValue();
  let batteryLevel = value.getUint8(0);
  console.log('> Battery Level is ' + batteryLevel + '%');
  
// Discovery options match any devices advertising:
// . The standard heart rate service.
// . Both 16-bit service IDs 0x1802 and 0x1803.
// . A proprietary 128-bit UUID service c48e6067-5295-48d3-8d5c-0395f61792b1.
// . Devices with name "ExampleName".
// . Devices with name starting with "Prefix".
//
// And enables access to the battery service if devices
// include it, even if devices do not advertise that service.

const options = {
  filters: [
    {services: ['heart_rate']},
    {services: [0x1802, 0x1803]},
    {services: ['c48e6067-5295-48d3-8d5c-0395f61792b1']},
    {name: 'Polar H7'},
    {namePrefix: 'Polar'}
  ],
  optionalServices: ['battery_service']
}

const options2 = {
    acceptAllDevices: true, 
}

USB

WEB

USB History

USB History

SKIP

The WebUSB API provides a way to safely expose USB device services to the web.

 

 

- webUSB spec

https://wicg.github.io/webusb/

It provides an API familiar to developers who have used existing native USB libraries and exposes the device interfaces defined by existing specifications.

 

- webUSB spec

https://wicg.github.io/webusb/

Cross-platform Javascript SDKs

Hot patch easily

rapidly prototype

Combine with other web tech

Fast to Make

HTTPS only

No Native Code Needed

User Gesture required

FACTS

Device Descriptor 

Device Descriptor 

Config Descriptor

Config Descriptor

Device Descriptor 

Config Descriptor

Config Descriptor

Interface Descriptor

Interface Descriptor

Interface Descriptor

Interface Descriptor

Device Descriptor 

Config Descriptor

Config Descriptor

Interface Descriptor

Interface Descriptor

Interface Descriptor

Interface Descriptor

Endpoint Descriptor

Endpoint Descriptor

Endpoint Descriptor

Endpoint Descriptor

Endpoint Descriptor

Endpoint Descriptor

Endpoint Descriptor

Endpoint Descriptor

requestHTMLButton.addEventListener('click', async function() {

    // Logic
  
});
<template>
  <a @click="discover">Click me!</a>
</template>

<script>
export default {
  methods: {
    discover: async function(event) {

      // Logic

    }
  }
}
</script>

OR

User Interaction

 const options = {
    filters: [{ vendorId: 0x22b8, productId: 0x2e76 }]
  };
  const _device = await navigator.usb.requestDevice(options);
  const device = await _device.open();
  await device.selectConfiguration(0);
  await device.claimInterface(0);

  // Transfer Out ->
  // Transfer In <-

Device Descriptor 

 const options = {
    filters: [{ vendorId: 0x22b8, productId: 0x2e76 }]
  };
  const _device = await navigator.usb.requestDevice(options);
  const device = await _device.open();
  await device.selectConfiguration(0);
  await device.claimInterface(0);

  // Transfer Out ->
  // Transfer In <-

Device Descriptor 

 const options = {
    filters: [{ vendorId: 0x22b8, productId: 0x2e76 }]
  };
  const _device = await navigator.usb.requestDevice(options);
  const device = await _device.open();
  await device.selectConfiguration(0);
  await device.claimInterface(0);

  // Transfer Out ->
  // Transfer In <-

Device Descriptor 

 const options = {
    filters: [{ vendorId: 0x22b8, productId: 0x2e76 }]
  };
  const _device = await navigator.usb.requestDevice(options);
  const device = await _device.open();
  await device.selectConfiguration(0);
  await device.claimInterface(0);

  // Transfer Out ->
  // Transfer In <-

Device Descriptor 

    /*
        vendorId
        productId
        classCode
        subclassCode
        protocolCode
        serialNumber
    */

 const options = {
    filters: [
       { 
            vendorId: 0x22b8, 
            productId: 0x2e76, 
            serialNumber: 'ZY223SD2TP' 
       }
    ]
  };

Transfer Types

INTERRUPT 
CONTROL
IN
OUT

Non-periodic, small,

device "initiated communication

-> small amount of time sensitive data


  // Out
  const endpointNumber = 1;
  const data = 'VueJs';
  await device.transferOut(endpointNumber, data);
  // In
  const length = 8;
  await device.trasnferIn(endpointNumber, length);

Interrupt transfer

Transfer Types

INTERRUPT 
CONTROL
IN
OUT

Non-periodic, small,

device "initiated communication

-> small amount of time sensitive data

Good for small configuration commands


  const data = 'VueJs';
  const length = 64;
  const setup = {
    requestType: 'class', // standard, vendor
    recipient: 'interface', // device, endpoint, other
    request: 0x22, // vendor-specefic command
    value: 0x01, // vendor-specefic request
    index: 0x02 // endpoint
  };
  // out
  await device.controlTransferOut(setup, data);
  // in
  await device.controlTransferIn(setup, length);

Control transfer

Transfer Types

INTERRUPT 
CONTROL
ISOCHRONOUS
IN
OUT

Good for small configuration commands

Non-periodic, small,

device "initiated communication

-> small amount of time sensitive data

used for streams of data like video and sound


  // Isochronous transfer
  const _data = ArrayBuffer(8);
  const endpointNumber = 1;
  const length = 64;
  const packetLength = 8;
  // out
  await device.isochronousTransferOut(endpointNumber, _data, length);

  // in
  await device.isochronousTransferIn(endpointNumber, packetLength);

Isochronous transfer

System information / Chrome device info 

Web NFC

low-level API allowing Web applications to access and exchange the data with the Near-Field Communication devices, such as NFC tags.

chrome://flags

"Experimental Web Platform Features"

"Web NFC"

Your Mobile should support NFC

Chrome

Android

await navigator.nfc.watch(
  async function(message) {
    if (message.records[0].recordType === 'empty') {
      writeToTag();
    }
    processMessage(message);
  },
  { mode: 'any' }
);

Watch

async function writeToTag() {
  try {
    await navigator.nfc.push(
      {
        records: [
          {
            recordType: 'json',
            mediaType: 'application/json',
            data: { name: 'Majid Hajian' }
          },
          {
            recordType: 'json',
            mediaType: 'application/json',
            data: {
              name: 'VueDay Verona'
            }
          }
        ]
      },
      {}
    );
  } catch (e) {
    // console.log(e)
  }
}

Push

async function writeToTag() {
  try {
    await navigator.nfc.push(
      {
        records: [
          {
            recordType: 'json',
            mediaType: 'application/json',
            data: { name: 'Majid Hajian' }
          },
          {
            recordType: 'json',
            mediaType: 'application/json',
            data: {
              name: 'VueDay Verona'
            }
          }
        ]
      },
      {}
    );
  } catch (e) {
    // console.log(e)
  }
}

Push

Future

Image Clipboard

Shape detection

App Icon badging 

Wake Lock

Contacts API 

Native File System Access

Web HID

Shortcuts

Biometrics 

and

...

Image Clipboard

Shape detection

App Icon badging 

Wake Lock

Contacts API 

Native File System Access

Web HID

Shortcuts

Biometrics 

and

...

Demo source code

bit.ly/vueware

bit.ly/vueware-slides

Majid Hajian

Hardware Connectivity on the progressive web with Vue

By Majid Hajian

Hardware Connectivity on the progressive web with Vue

  • 3,202