diff --git a/functional-samples/sample.co2meter/README.md b/functional-samples/sample.co2meter/README.md index 6ca1a4f1..20a6b5f7 100644 --- a/functional-samples/sample.co2meter/README.md +++ b/functional-samples/sample.co2meter/README.md @@ -5,3 +5,7 @@ Team: chengweih001, scheib, alvinjiooo. Design Doc: [go/fugu-exploration-sprint-2023-CO2-meter-extension](https://docs.google.com/document/d/1GX98Vfve1EvQ5BUajh4XE63-3PakTfxnZf_kKnl3U8o/edit?resourcekey=0-g2tQqNx2aKTYQRfXKHo9cA#) (Google internal only, this link can be removed when PR) [TODO add the reason of this project and how this sample can help developers] + + +## Known Issue +* The extension couldn't detect device status properly when the device permission is revoked. \ No newline at end of file diff --git a/functional-samples/sample.co2meter/background.js b/functional-samples/sample.co2meter/background.js index 75864ab5..cfa6d215 100644 --- a/functional-samples/sample.co2meter/background.js +++ b/functional-samples/sample.co2meter/background.js @@ -18,7 +18,8 @@ import icon from "./modules/icon.js"; import storage from "./modules/storage.js"; import CO2Meter from "./modules/co2_meter.js"; import { - CO2_READING_KEY, TEMPERATURE_READING_KEY, NEW_READING_SAVED_MESSAGE + CO2_READING_KEY, TEMPERATURE_READING_KEY, NEW_READING_SAVED_MESSAGE, + PERMISSION_GRANTED_MESSAGE, CO2_METER_UNAVAILABLE } from "./modules/constant.js"; var clients = new Set(); @@ -26,15 +27,24 @@ var clients = new Set(); async function co2MeterConnected() { icon.setConnected(); await CO2Meter.init(); - createAlarm(); + if (CO2Meter.getDeviceStatus()) { + createAlarm(); + } }; function co2MeterDisconnected() { + broadcastMessage(CO2_METER_UNAVAILABLE); icon.setDisconnected(); + clearAlarm(); +} + +function clearAlarm() { + console.log('Clear Alarm'); chrome.alarms.clearAll(); } async function createAlarm() { + console.log('Start Alarm'); chrome.alarms.create("getReadingAlarm", { delayInMinutes: 0, periodInMinutes: await storage.getIntervalInSeconds() / 60 @@ -43,28 +53,39 @@ async function createAlarm() { async function onAlarmGetReading(alarm) { if (!CO2Meter.getDeviceStatus()) { - chrome.alarms.clearAll(); - icon.setDisconnected(); + co2MeterDisconnected(); return; } try { + console.log('To read CO2'); var reading = await CO2Meter.getCO2Reading(); storage.setCO2Value(reading[CO2_READING_KEY]); storage.setTempValue(reading[TEMPERATURE_READING_KEY]); - await broadcastNewReading(); + await broadcastMessage(NEW_READING_SAVED_MESSAGE); } catch (e) { console.log('Exception when reading CO2!', e); } } -async function broadcastNewReading() { +async function broadcastMessage(message) { for (const client of clients.values()) { - client.postMessage(NEW_READING_SAVED_MESSAGE); + client.postMessage(message); } } +function onPermissionGranted() { + co2MeterConnected(); +} + async function initilize() { + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message === PERMISSION_GRANTED_MESSAGE) { + onPermissionGranted(); + broadcastMessage(PERMISSION_GRANTED_MESSAGE); + } + }); + chrome.runtime.onConnect.addListener(function (port) { console.log(`${port.name} connected`); port.onDisconnect.addListener(function (port) { diff --git a/functional-samples/sample.co2meter/chart-iframe.html b/functional-samples/sample.co2meter/chart-iframe.html index c9028492..bd999ae1 100644 --- a/functional-samples/sample.co2meter/chart-iframe.html +++ b/functional-samples/sample.co2meter/chart-iframe.html @@ -8,4 +8,9 @@ - \ No newline at end of file + + + +

Device is not detected, please make sure the CO2 meter is connected and the permission is granted!

+

Permission can be granted in the settings page.

+
\ No newline at end of file diff --git a/functional-samples/sample.co2meter/chart-iframe.js b/functional-samples/sample.co2meter/chart-iframe.js index fda1c8fe..366e4ce6 100644 --- a/functional-samples/sample.co2meter/chart-iframe.js +++ b/functional-samples/sample.co2meter/chart-iframe.js @@ -1,7 +1,8 @@ import storage from "./modules/storage.js"; -import { NEW_READING_SAVED_MESSAGE } from "./modules/constant.js"; +import { NEW_READING_SAVED_MESSAGE, PERMISSION_GRANTED_MESSAGE, CO2_METER_UNAVAILABLE } from "./modules/constant.js"; +import CO2Meter from "./modules/co2_meter.js"; -let lastChartUpdateTimeMs = +let lastChartUpdateTimeMs = new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000).getTime(); // Initialize to one week ago. let chart = null; @@ -54,14 +55,39 @@ window.onload = async e => { chart = new Chart(document.getElementById('chart'), chartConfig); await updateChart(); - + // Update when document becomes visible. document.onvisibilitychange = updateChart; // Register for messages to update chart upon new data readings. chrome.runtime.connect().onMessage.addListener((msg) => { - if (msg === NEW_READING_SAVED_MESSAGE) { updateChart() } + if (msg === NEW_READING_SAVED_MESSAGE) { updateChart(); } + else if (msg === PERMISSION_GRANTED_MESSAGE) { updateCO2MeterStatus(true); } + else if (msg === CO2_METER_UNAVAILABLE) { updateCO2MeterStatus(false); } }); + + await CO2Meter.init(); + updateCO2MeterStatus(CO2Meter.getDeviceStatus()); + CO2Meter.registerCallback(CO2MeterConnected, CO2MeterDisconnected); +} + +function updateCO2MeterStatus(connected) { + let dialog = document.getElementById('device-disconnected'); + if (connected) { + dialog.close(); + } else { + if (!dialog.open) { + dialog.showModal(); + } + } +} + +function CO2MeterConnected() { + updateCO2MeterStatus(true); +} + +function CO2MeterDisconnected() { + updateCO2MeterStatus(false); } async function updateChart() { diff --git a/functional-samples/sample.co2meter/modules/co2_meter.js b/functional-samples/sample.co2meter/modules/co2_meter.js index 846c7b2a..0b2786f7 100644 --- a/functional-samples/sample.co2meter/modules/co2_meter.js +++ b/functional-samples/sample.co2meter/modules/co2_meter.js @@ -16,7 +16,8 @@ Or it returns false instead. import icon from "./icon.js"; import { - CO2_READING_KEY, TEMPERATURE_READING_KEY + CO2_READING_KEY, TEMPERATURE_READING_KEY, + PERMISSION_GRANTED_MESSAGE } from "./constant.js"; const key = new Uint8Array([0xc4, 0xc6, 0xc0, 0x92, 0x40, 0x23, 0xdc, 0x96]); @@ -89,9 +90,9 @@ class CO2Meter { requestPermission() { // The extension currently only support this model: // https://www.co2meter.com/products/co2mini-co2-indoor-air-quality-monitor - navigator.hid.requestDevice({ filters: [{ vendorId: 1241, productId: 41042 }] }).then((device) => { + navigator.hid.requestDevice({ filters: [{ vendorId: 1241, productId: 41042 }] }).then((device) => { console.log('CO2 meter permission granted!', device[0]); - icon.setConnected(); + chrome.runtime.sendMessage(PERMISSION_GRANTED_MESSAGE); }) } @@ -105,7 +106,9 @@ class CO2Meter { } disconnectHandler() { - this.device.close(); + if (this.device) { + this.device.close(); + } this.device = null; this.reading = null; if (this.disconnectClientCB && @@ -155,7 +158,8 @@ class CO2Meter { this.device.addEventListener('inputreport', onInputReport); await this.device.open(); await this.device.sendFeatureReport(0, key); - } catch { + } catch (e) { + console.log('CO2 reading exception:', e); this.device.removeEventListener('inputreport', onInputReport); await this.device.close(); reject('Fail to open CO2 meter for reading'); diff --git a/functional-samples/sample.co2meter/modules/constant.js b/functional-samples/sample.co2meter/modules/constant.js index 88a377a4..60a7d654 100644 --- a/functional-samples/sample.co2meter/modules/constant.js +++ b/functional-samples/sample.co2meter/modules/constant.js @@ -15,4 +15,6 @@ export const CO2_READING_KEY = 'CO2'; export const TEMPERATURE_READING_KEY = 'Temperature'; -export const NEW_READING_SAVED_MESSAGE = 'new reading saved'; \ No newline at end of file +export const NEW_READING_SAVED_MESSAGE = 'new reading saved'; +export const PERMISSION_GRANTED_MESSAGE = 'permission granted'; +export const CO2_METER_UNAVAILABLE = 'co2 meter unavailable'; \ No newline at end of file