WellNuo/services/ble/webBluetooth.ts
Sergei c2064a76eb Add Web Bluetooth support for browser-based sensor setup
- Add webBluetooth.ts with browser detection and compatibility checks
- Add WebBLEManager implementing IBLEManager for Web Bluetooth API
- Add BrowserNotSupported component showing clear error for Safari/Firefox
- Update services/ble/index.ts to use WebBLEManager on web platform
- Add comprehensive tests for browser detection and WebBLEManager

Works in Chrome/Edge/Opera, shows user-friendly error in unsupported browsers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-01 10:48:01 -08:00

174 lines
5.2 KiB
TypeScript

// Web Bluetooth browser compatibility utilities
/**
* Web Bluetooth support status
*/
export interface WebBluetoothSupport {
supported: boolean;
browserName: string;
browserVersion: string;
reason?: 'unsupported_browser' | 'insecure_context' | 'api_unavailable';
helpUrl?: string;
}
/**
* Supported browsers for Web Bluetooth
* Chrome/Edge/Opera on desktop and Android support Web Bluetooth
* Safari and Firefox do NOT support Web Bluetooth
*/
export const SUPPORTED_BROWSERS = ['Chrome', 'Edge', 'Opera', 'Samsung Internet'];
/**
* URLs for browser help pages
*/
export const BROWSER_HELP_URLS: Record<string, string> = {
Chrome: 'https://www.google.com/chrome/',
Edge: 'https://www.microsoft.com/edge',
Opera: 'https://www.opera.com/',
'Samsung Internet': 'https://www.samsung.com/us/support/owners/app/samsung-internet',
};
/**
* Detect the current browser name and version
*/
export function detectBrowser(): { name: string; version: string } {
if (typeof navigator === 'undefined') {
return { name: 'Unknown', version: '0' };
}
const userAgent = navigator.userAgent;
let browserName = 'Unknown';
let browserVersion = '0';
// Edge (must be checked before Chrome as Edge contains "Chrome" in UA)
if (userAgent.includes('Edg/')) {
browserName = 'Edge';
const match = userAgent.match(/Edg\/(\d+)/);
browserVersion = match?.[1] || '0';
}
// Opera (must be checked before Chrome as Opera contains "Chrome" in UA)
else if (userAgent.includes('OPR/') || userAgent.includes('Opera')) {
browserName = 'Opera';
const match = userAgent.match(/(?:OPR|Opera)\/(\d+)/);
browserVersion = match?.[1] || '0';
}
// Samsung Internet (must be checked before Chrome)
else if (userAgent.includes('SamsungBrowser')) {
browserName = 'Samsung Internet';
const match = userAgent.match(/SamsungBrowser\/(\d+)/);
browserVersion = match?.[1] || '0';
}
// Chrome
else if (userAgent.includes('Chrome/')) {
browserName = 'Chrome';
const match = userAgent.match(/Chrome\/(\d+)/);
browserVersion = match?.[1] || '0';
}
// Safari (must be checked after Chrome-based browsers)
else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
browserName = 'Safari';
const match = userAgent.match(/Version\/(\d+)/);
browserVersion = match?.[1] || '0';
}
// Firefox
else if (userAgent.includes('Firefox/')) {
browserName = 'Firefox';
const match = userAgent.match(/Firefox\/(\d+)/);
browserVersion = match?.[1] || '0';
}
return { name: browserName, version: browserVersion };
}
/**
* Check if the current browser supports Web Bluetooth API
*/
export function checkWebBluetoothSupport(): WebBluetoothSupport {
const browser = detectBrowser();
// Check if we're in a secure context (HTTPS or localhost)
if (typeof window !== 'undefined' && !window.isSecureContext) {
return {
supported: false,
browserName: browser.name,
browserVersion: browser.version,
reason: 'insecure_context',
};
}
// Check if the Web Bluetooth API is available
if (typeof navigator === 'undefined' || !('bluetooth' in navigator)) {
// Determine reason based on browser
const isKnownUnsupportedBrowser =
browser.name === 'Safari' ||
browser.name === 'Firefox';
return {
supported: false,
browserName: browser.name,
browserVersion: browser.version,
reason: isKnownUnsupportedBrowser ? 'unsupported_browser' : 'api_unavailable',
helpUrl: BROWSER_HELP_URLS.Chrome, // Suggest Chrome as alternative
};
}
return {
supported: true,
browserName: browser.name,
browserVersion: browser.version,
};
}
/**
* Get a user-friendly error message for unsupported browsers
*/
export function getUnsupportedBrowserMessage(support: WebBluetoothSupport): {
title: string;
message: string;
suggestion: string;
} {
switch (support.reason) {
case 'unsupported_browser':
return {
title: 'Browser Not Supported',
message: `${support.browserName} does not support Web Bluetooth, which is required to connect to WellNuo sensors.`,
suggestion: 'Please use Chrome, Edge, or Opera browser to set up your sensors.',
};
case 'insecure_context':
return {
title: 'Secure Connection Required',
message: 'Web Bluetooth requires a secure connection (HTTPS).',
suggestion: 'Please access this page using HTTPS or localhost.',
};
case 'api_unavailable':
return {
title: 'Bluetooth Not Available',
message: 'Web Bluetooth API is not available in this browser.',
suggestion: 'Please use Chrome, Edge, or Opera browser to set up your sensors.',
};
default:
return {
title: 'Bluetooth Not Available',
message: 'Could not access Bluetooth functionality.',
suggestion: 'Please use Chrome, Edge, or Opera browser.',
};
}
}
/**
* Type guard to check if we're running in a web browser
*/
export function isWebPlatform(): boolean {
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
/**
* Type guard to check if Web Bluetooth is available
*/
export function hasWebBluetooth(): boolean {
return typeof navigator !== 'undefined' && 'bluetooth' in navigator;
}