mirror of
https://github.com/GoogleChrome/chrome-extensions-samples.git
synced 2026-03-26 13:19:49 +07:00
* Initial README.md file * fix design doc link * Update README.md adding scheib * Update README.md * test * update README.md with alvinjiooo * add a ascii fish in README.md * Update README.md * skeloton extension * Add original co2meter.html * add buttons to settings page * Initial storage module stub. * Add storage module reference from settings page. * Fix settings JS to be module compatible. * Add stubs for settings storage writing * Call saveCO2Value() from onInputReport in co2meter.html * background page to read CO2 periodically based on the stored interval * verion 1.0 /module/co2_meter.js * Refactor settings.html and hook up CO2 driver * update v2 of /module/co2_meter.js which access all methods from CO2Meter class * Fix co2_meter import in background.js * background page to show CO2 meter disconnected icon * v2.1 update /module/co2_meter.js add support for virtual meter and connection listener also rquestPermission * v2.1 update /module/co2_meter.js * Import idb-keyval indexedDB library into storage.js, with trivial usage. * background page handle disconnect connect device * update v2.2 /module/co2_meter.js make non-static and add guarding flag for exccessive reading * update v2.3 /module/co2_meter.js await device to close before open device in * update v2.4 /module/co2_meter.js change to return reject promise instead of Error in * Add third-party/idb-keyval, use to set/get interval. * Simplify settings and storage. * handle background page device disconnect/connect * Rename *_script.js files to only *.js. * Add temp reading * Add chart to popup.html with chartjs etc. * Update extension tool tip to show connected/disconnected status. * use indexedDB to store CO2 reading * Renaming items in background.js Name methods more clearly for what they are doing. Collapse some code when possible to be inline. * add in Storage * add support for storing temperature and query temperature in range * background page boardcasting reading updated * Refactor chart out of popup into chart.html for iframing * Continued: chart out of popup into chart.html for iframing * Use message channel for message broadcasting and move common constant to constant.js * Work in progress for data into chart. * Use set for clients in background page * Add promise to storage constructor. * Change IDB version number due to schema change * Populate chart data. * check store name before creating when version changed * increase popup window and iframe size * Update chart upon new data. * Add calibration period and refactor inputReport * Use flexbox to layout pages. * move internal and temperature unit into setting store * move dbInitialized into transaction oncomplete event * remove reading out interval and temp unit log * Remove co2meter.html * Add .map files for third party code to remove devtools warnings. * Display chart in Fahrenheit * remove idb-keyvalue * prettier storage.js and add comments to public methods * Chart style: time axis respects time of data, no data points * Show dialog on the chart when device is disconnected * Add Example Data Button * Close dialog button * Implement toggling between Celsius / Fahrenheit * polish co2_meter.js and remove virtual device and change default interval to 30 sec * Change small icon to CO2 text * change reading interval default to 30 secs which helps the chart update faster for fresh load case * Fix lint error * Clean up console.log * Add chrome.alarm example * remove keep alive for reading alarm * refactor to keep connection open * add README.md * strip down * addressed comments * add link to README.md --------- Co-authored-by: Vincent Scheib <scheib@users.noreply.github.com> Co-authored-by: Alvin Ji <111466895+alvinjiooo@users.noreply.github.com> Co-authored-by: Alvin Ji <alvinji@chromium.org> Co-authored-by: Alpaca Jam <alvinji@google.com>
164 lines
4.5 KiB
JavaScript
164 lines
4.5 KiB
JavaScript
// Copyright 2023 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @filename co2_meter.js
|
|
*
|
|
* @description CO2Meter provides methods for accessing status and data of a
|
|
* CO2 meter. When creating a CO2Meter, it has to await `init()` to finish
|
|
* before quering device status.
|
|
*/
|
|
|
|
import { PERMISSION_GRANTED_MESSAGE } from './constant.js';
|
|
|
|
const key = new Uint8Array([0xc4, 0xc6, 0xc0, 0x92, 0x40, 0x23, 0xdc, 0x96]);
|
|
|
|
function KelvinToFahrenheit(k) {
|
|
return Math.trunc(((k - 273.15) * 9) / 5 + 32);
|
|
}
|
|
|
|
class CO2Meter {
|
|
constructor() {
|
|
this.device = null;
|
|
this.connectClientCB = null;
|
|
this.disconnectClientCB = null;
|
|
this.co2ReadingClientCB = null;
|
|
this.tempReadingClientCB = null;
|
|
this.connectHandler = this.connectHandler.bind(this);
|
|
this.disconnectHandler = this.disconnectHandler.bind(this);
|
|
this.onInputReport = this.onInputReport.bind(this);
|
|
}
|
|
|
|
/**
|
|
* @description This function initializes the CO2Meter object.
|
|
*/
|
|
async init(
|
|
connectCallback = null,
|
|
disconnectCallback = null,
|
|
co2ReadingCallback = null,
|
|
tempReadingCallback = null
|
|
) {
|
|
this.connectClientCB = connectCallback;
|
|
this.disconnectClientCB = disconnectCallback;
|
|
this.co2ReadingClientCB = co2ReadingCallback;
|
|
this.tempReadingClientCB = tempReadingCallback;
|
|
navigator.hid.addEventListener('connect', this.connectHandler);
|
|
navigator.hid.addEventListener('disconnect', this.disconnectHandler);
|
|
console.log('CO2Meter init() done');
|
|
}
|
|
|
|
async startReading() {
|
|
if (this.device) {
|
|
console.log('CO2 reading has already started!');
|
|
return;
|
|
}
|
|
const devices = await navigator.hid.getDevices();
|
|
if (devices.length == 0) {
|
|
throw 'No CO2 meter for reading!';
|
|
}
|
|
this.device = devices[0];
|
|
|
|
try {
|
|
await this.device.open();
|
|
await this.device.sendFeatureReport(0, key);
|
|
} catch (e) {
|
|
console.log('CO2 reading exception:', e);
|
|
await this.device.close();
|
|
this.device = null;
|
|
throw 'Fail to open CO2 meter for reading!';
|
|
}
|
|
|
|
this.device.addEventListener('inputreport', this.onInputReport);
|
|
}
|
|
|
|
async stopReading() {
|
|
if (this.device) {
|
|
this.device.removeEventListener('inputreport', this.onInputReport);
|
|
await this.device.close();
|
|
this.device = null;
|
|
}
|
|
}
|
|
|
|
onInputReport(report) {
|
|
let data = new Uint8Array(
|
|
report.data.buffer,
|
|
report.data.byteOffset,
|
|
report.data.byteLength
|
|
);
|
|
|
|
const op = data[0];
|
|
let val = (data[1] << 8) | data[2];
|
|
|
|
if (op == 0x50) {
|
|
console.log(`Current CO2 reading is ${val}`);
|
|
if (this.co2ReadingClientCB) {
|
|
this.co2ReadingClientCB(val);
|
|
}
|
|
} else if (op == 0x42) {
|
|
val = val / 16;
|
|
console.log(`Current Temp reading is ${val}`);
|
|
if (this.tempReadingClientCB) {
|
|
this.tempReadingClientCB(val);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description Request user to grant permission for using CO2 meter.
|
|
* The extension currently only support this model:
|
|
* https://www.co2meter.com/products/co2mini-co2-indoor-air-quality-monitor
|
|
*/
|
|
async requestPermission() {
|
|
const devices = await navigator.hid.requestDevice({
|
|
filters: [{ vendorId: 1241, productId: 41042 }]
|
|
});
|
|
console.log('CO2 meter permission granted!', devices[0]);
|
|
chrome.runtime.sendMessage(PERMISSION_GRANTED_MESSAGE);
|
|
}
|
|
|
|
connectHandler() {
|
|
if (this.connectClientCB && typeof this.connectClientCB === 'function') {
|
|
this.connectClientCB();
|
|
}
|
|
}
|
|
|
|
disconnectHandler() {
|
|
if (this.device) {
|
|
this.device.close();
|
|
}
|
|
this.device = null;
|
|
if (
|
|
this.disconnectClientCB &&
|
|
typeof this.disconnectClientCB === 'function'
|
|
) {
|
|
this.disconnectClientCB();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description Get Device connected status.
|
|
* @return {Boolean}
|
|
*/
|
|
async getDeviceStatus() {
|
|
const devices = await navigator.hid.getDevices();
|
|
return devices.length > 0;
|
|
}
|
|
|
|
tempReadingToFahrenheit(temp_reading) {
|
|
return KelvinToFahrenheit(temp_reading);
|
|
}
|
|
}
|
|
|
|
export default new CO2Meter();
|