Skip to content

Commit 1ee0669

Browse files
committed
Final fixes and test pages.
1 parent 39b9e01 commit 1ee0669

File tree

9 files changed

+672
-27
lines changed

9 files changed

+672
-27
lines changed

README.md

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,73 @@
1-
# WebUSB
2-
HTML5 WebUSB APIs for Microsoft ASP.Net Core Blazor
1+
# Canvas
2+
HTML5 WebUSB API implementation for Microsoft Blazor
3+
4+
[![Build status](https://dotnet-ci.visualstudio.com/DotnetCI/_apis/build/status/Blazor-Extensions-WebUSB-CI?branch=master)](https://dotnet-ci.visualstudio.com/DotnetCI/_build/latest?definitionId=18&branch=master)
5+
[![Package Version](https://img.shields.io/nuget/v/Blazor.Extensions.WebUSB.svg)](https://www.nuget.org/packages/Blazor.Extensions.WebUSB)
6+
[![NuGet Downloads](https://img.shields.io/nuget/dt/Blazor.Extensions.WebUSB.svg)](https://www.nuget.org/packages/Blazor.Extensions.WebUSB)
7+
[![License](https://img.shields.io/github/license/BlazorExtensions/WebUSB.svg)](https://github.com/BlazorExtensions/WebUSB/blob/master/LICENSE)
8+
9+
# Blazor Extensions
10+
11+
Blazor Extensions are a set of packages with the goal of adding useful things to [Blazor](https://blazor.net).
12+
13+
# Blazor Extensions WebUSB
14+
15+
This package wraps [HTML5 WebUSB](https://wicg.github.io/webusb/) APIs.
16+
17+
# Installation
18+
19+
```
20+
Install-Package Blazor.Extensions.WebUSB
21+
```
22+
23+
# Sample
24+
25+
## Usage
26+
27+
- First add the USB services on Blazor `IServiceCollection`:
28+
29+
```c#
30+
public void ConfigureServices(IServiceCollection services)
31+
{
32+
services.UseWebUSB(); // Makes IUSB available to the DI container
33+
}
34+
```
35+
36+
### To consume on your `.cshtml`:
37+
38+
- On your `_ViewImports.cshtml` add the using entry:
39+
40+
```c#
41+
@using Blazor.Extensions.WebUSB
42+
```
43+
44+
- Then, on your `.cshtml` inject the `IUSB`:
45+
46+
```c#
47+
@inject IUSB usb
48+
```
49+
50+
And then use the `usb` object to interact with connected USB devices thru your Blazor application.
51+
52+
### To inject on a `BlazorComponent` class:
53+
54+
Define a property of type `IUSB` and mark it as `[Injectable]`:
55+
56+
```c#
57+
[Inject] private IUSB _usb { get; set; }
58+
```
59+
60+
Then use the `_usb` variable to interact with the connected USB devices.
61+
62+
**Note**: For now, you have to call `await IUSB.Initialize()` once in your application. This is a temporary requirement and we are looking on a better way to automatically register to _Connect/Disconnect_ events.
63+
64+
# Contributions and feedback
65+
66+
Please feel free to use the component, open issues, fix bugs or provide feedback.
67+
68+
# Contributors
69+
70+
The following people are the maintainers of the Blazor Extensions projects:
71+
72+
- [Attila Hajdrik](https://github.com/attilah)
73+
- [Gutemberg Ribiero](https://github.com/galvesribeiro)

src/Blazor.Extensions.WebUSB.JS/src/USBManager.ts

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { USBDeviceFound, USBRequestDeviceOptions, ParseUSBDevice, USBConfiguration, USBInterface, USBDirection, USBInTransferResult, USBOutTransferResult, USBTransferStatus } from "./USBTypes";
1+
import { USBDeviceFound, USBRequestDeviceOptions, ParseUSBDevice, USBConfiguration, USBInterface, USBDirection, USBInTransferResult, USBOutTransferResult, USBTransferStatus, USBControlTransferParameters } from "./USBTypes";
22

33
type DotNetReferenceType = {
44
invokeMethod<T>(methodIdentifier: string, ...args: any[]): T,
@@ -171,21 +171,21 @@ export class USBManager {
171171
}
172172

173173
public TransferIn = (device: USBDeviceFound, endpointNumber: number, length: number): Promise<USBInTransferResult> => {
174-
175174
let usbDevice = this.GetUSBDevice(device);
176175
return new Promise<USBInTransferResult>((resolve, reject) => {
177176
if (!usbDevice) return reject("Device not connected");
178-
console.log("In called:");
179177

180178
usbDevice.transferIn(endpointNumber, length)
181179
.then(out => {
182-
console.log(out.data.buffer);
183180
resolve({
184-
data: Array.prototype.slice.call(new Uint8Array(out.data.buffer)),
181+
data: Array.prototype.slice.call(new Uint8Array(out.data.buffer)), // Hack to support Uint8Array to byte[] serialization.
185182
status: out.status
186183
});
187184
})
188-
.catch(err => reject(err));
185+
.catch(err => {
186+
console.error(err);
187+
reject(err);
188+
});
189189
});
190190
}
191191

@@ -197,9 +197,54 @@ export class USBManager {
197197

198198
usbDevice.transferOut(endpointNumber, buffer)
199199
.then(out => {
200-
resolve({ bytesWritten: out.bytesWritten, status: out.status });
200+
resolve({
201+
bytesWritten: out.bytesWritten,
202+
status: out.status
203+
});
201204
})
202-
.catch(err => reject(err));
205+
.catch(err => {
206+
console.error(err);
207+
reject(err);
208+
});
209+
});
210+
}
211+
212+
public ControlTransferIn = (device: USBDeviceFound, setup: USBControlTransferParameters, length: number): Promise<USBInTransferResult> => {
213+
let usbDevice = this.GetUSBDevice(device);
214+
return new Promise<USBInTransferResult>((resolve, reject) => {
215+
if (!usbDevice) return reject("Device not connected");
216+
217+
usbDevice.ControlTransferIn(setup, length)
218+
.then(out => {
219+
resolve({
220+
data: Array.prototype.slice.call(new Uint8Array(out.data.buffer)), // Hack to support Uint8Array to byte[] serialization.
221+
status: out.status
222+
});
223+
})
224+
.catch(err => {
225+
console.error(err);
226+
reject(err);
227+
});
228+
});
229+
}
230+
231+
public ControlTransferOut = (device: USBDeviceFound, setup: USBControlTransferParameters, data: any): Promise<USBOutTransferResult> => {
232+
let usbDevice = this.GetUSBDevice(device);
233+
return new Promise<USBOutTransferResult>((resolve, reject) => {
234+
if (!usbDevice) return reject("Device not connected");
235+
const buffer = data ? Uint8Array.from(data) : undefined;
236+
237+
usbDevice.controlTransferOut(setup, buffer)
238+
.then(out => {
239+
resolve({
240+
bytesWritten: out.bytesWritten,
241+
status: out.status
242+
});
243+
})
244+
.catch(err => {
245+
console.error(err);
246+
reject(err);
247+
});
203248
});
204249
}
205250

src/Blazor.Extensions.WebUSB.JS/src/USBTypes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ export interface USBEndpoint { endpointNumber: number, direction: USBDirection,
2626
export interface USBTransferResult { status: USBTransferStatus }
2727
export interface USBInTransferResult extends USBTransferResult { data: any }
2828
export interface USBOutTransferResult extends USBTransferResult { bytesWritten: number }
29+
export interface USBControlTransferParameters { requestType: USBRequestType, recipient: USBRecipient, request: number, value: number, index: number }
2930

3031
export enum USBDirection { "in", "out" }
3132
export enum USBEndpointType { "bulk", "interrupt", "isochronous" }
3233
export enum USBTransferStatus { "ok", "stall", "babble" }
34+
export enum USBRequestType { "standard", "class", "vendor" }
35+
export enum USBRecipient { "device", "interface", "endpoint", "other" }
3336

3437
export function ParseUSBDevice(rawDevice: any): USBDeviceFound {
3538
return {

src/Blazor.Extensions.WebUSB/USB.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class USB : IUSB
1212
private const string REMOVE_USB_METHOD = "BlazorExtensions.WebUSB.RemoveUSBEvents";
1313
private const string REQUEST_DEVICE_METHOD = "BlazorExtensions.WebUSB.RequestDevice";
1414
private const string GET_DEVICES_METHOD = "BlazorExtensions.WebUSB.GetDevices";
15-
15+
private bool _initialized = false;
1616
public event Action<USBDevice> OnDisconnect;
1717
public event Action<USBDevice> OnConnect;
1818

@@ -65,6 +65,13 @@ public Task Disconnected(USBDevice device)
6565
}
6666

6767
// TODO: Find out a more smart way to register to global connect/disconnect events from the WebUSB API regardless if there are subscribers to the events.
68-
public Task Initialize() => JSRuntime.Current.InvokeAsync<object>(REGISTER_USB_METHOD, new DotNetObjectRef(this));
68+
public async Task Initialize()
69+
{
70+
if (!this._initialized)
71+
{
72+
await JSRuntime.Current.InvokeAsync<object>(REGISTER_USB_METHOD, new DotNetObjectRef(this));
73+
this._initialized = true;
74+
}
75+
}
6976
}
7077
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace Blazor.Extensions.WebUSB
2+
{
3+
public static class USBRequestType
4+
{
5+
public const string Standard = "standard";
6+
public const string Class = "class";
7+
public const string Vendor = "vendor";
8+
}
9+
10+
public static class USBRecipient
11+
{
12+
public const string Device = "device";
13+
public const string Interface = "interface";
14+
public const string Endpoint = "endpoint";
15+
public const string Other = "other";
16+
}
17+
18+
public class USBControlTransferParameters
19+
{
20+
public string RequestType { get; private set; }
21+
public string Recipient { get; private set; }
22+
public byte Request { get; private set; }
23+
public int Value { get; private set; }
24+
public int Index { get; private set; }
25+
}
26+
}

src/Blazor.Extensions.WebUSB/USBDevice.Methods.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public static class USBDeviceMethods
1818
private const string SELECT_ALTERNATE_INTERFACE_METHOD = "BlazorExtensions.WebUSB.SelectAlternateInterface";
1919
private const string TRANSFER_OUT_METHOD = "BlazorExtensions.WebUSB.TransferOut";
2020
private const string TRANSFER_IN_METHOD = "BlazorExtensions.WebUSB.TransferIn";
21+
private const string CONTROL_TRANSFER_OUT_METHOD = "BlazorExtensions.WebUSB.ControlTransferOut";
22+
private const string CONTROL_TRANSFER_IN_METHOD = "BlazorExtensions.WebUSB.ControlTransferIn";
2123

2224
internal static void AttachUSB(this USBDevice device, USB usb) => device.USB = usb;
2325
public static Task<USBDevice> Open(this USBDevice device) => JSRuntime.Current.InvokeAsync<USBDevice>(OPEN_DEVICE_METHOD, device);
@@ -54,6 +56,13 @@ public static Task<USBDevice> ClaimBulkInterface(this USBDevice device)
5456
return device.ClaimInterface(bulkInterface.InterfaceNumber);
5557
}
5658

59+
public static Task<USBDevice> ReleaseBulkInterface(this USBDevice device)
60+
{
61+
var bulkInterface = device.Configuration.Interfaces.FirstOrDefault(i => i.Alternates.Any(a => a.Endpoints.Any(e => e.Type == USBEndpointType.Bulk)));
62+
if (bulkInterface == null) throw new InvalidOperationException("This devices doesn't have a Bulk interface");
63+
return device.ReleaseInterface(bulkInterface.InterfaceNumber);
64+
}
65+
5766
public static Task<USBDevice> ReleaseInterface(this USBDevice device, USBInterface usbInterface)
5867
{
5968
if (usbInterface == null) throw new ArgumentNullException(nameof(usbInterface));
@@ -111,5 +120,15 @@ public static Task<USBOutTransferResult> TransferOut(this USBDevice device, byte
111120
{
112121
return JSRuntime.Current.InvokeAsync<USBOutTransferResult>(TRANSFER_OUT_METHOD, device, endpointNumber, data);
113122
}
123+
124+
public static Task<USBInTransferResult> ControlTransferIn(this USBDevice device, USBControlTransferParameters setup, long length)
125+
{
126+
return JSRuntime.Current.InvokeAsync<USBInTransferResult>(CONTROL_TRANSFER_IN_METHOD, device, setup, length);
127+
}
128+
129+
public static Task<USBOutTransferResult> ControlTransferOut(this USBDevice device, USBControlTransferParameters setup, byte[] data)
130+
{
131+
return JSRuntime.Current.InvokeAsync<USBOutTransferResult>(CONTROL_TRANSFER_OUT_METHOD, device, setup, data);
132+
}
114133
}
115134
}

0 commit comments

Comments
 (0)