Majid Hajian
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
},
};
To show beyond the web and unleash the power of hardware connectivity on the progressive web and of course using Vue.js
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
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
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
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
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,
}
SKIP
The WebUSB API provides a way to safely expose USB device services to the web.
- webUSB spec
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
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
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
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 <-
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 <-
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 <-
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 <-
/*
vendorId
productId
classCode
subclassCode
protocolCode
serialNumber
*/
const options = {
filters: [
{
vendorId: 0x22b8,
productId: 0x2e76,
serialNumber: 'ZY223SD2TP'
}
]
};
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
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);
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);
System information / Chrome device info
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
await navigator.nfc.watch(
async function(message) {
if (message.records[0].recordType === 'empty') {
writeToTag();
}
processMessage(message);
},
{ mode: 'any' }
);
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)
}
}
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)
}
}
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
...
"Progressive Web Apps State of the Union" by Dominick Ng at BlinkOn 10
Demo source code
bit.ly/vueware
bit.ly/vueware-slides
Majid Hajian