Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/airqualityci/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.01: Initial clockinfo release
19 changes: 19 additions & 0 deletions apps/airqualityci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Air Quality Clock Info

Create a list of clockinfos, each displaying air quality and/or temperature, fetched from IQAir

Requires Gadgetbridge internet access

# Display modes

Tapping the clockinfo changes its mode.

Examples of possible modes:
- `45`
- `45 31°`
- `NYC 45`
- `NYC 45 31°`
- `NYC`


Not affiliated with IQAir.
Binary file added apps/airqualityci/app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 91 additions & 0 deletions apps/airqualityci/clkinfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
(function() {
let data = require("Storage").readJSON("airqualityci.json", 1) || {};
let config = require("Storage").readJSON("airqualityci.settings.json") || {};

function checkAQI(cb) {
let d = new Date();
if (config.apiKey && config.rows) {
config.rows.forEach((row) => {
if (row.url) {
let el = row.url.replace(/\/$/, "").split("/").reverse();
let url = `https://api.airvisual.com/v2/city?city=${el[0]}&state=${el[1]}&country=${el[2]}&key=${config.apiKey}`;
data[row.url] = data[row.url] || {};
// If neither attempt nor time are set, then we have never tried
// If attempt was set more than 1 minute ago, try again
// If time was set more than 1 hour ago, refresh
if ((!data[row.url].time && !data[row.url].attempt) ||
(data[row.url].attempt && data[row.url].attempt + 60000 < d.getTime()) ||
(data[row.url].time && data[row.url].time + 3600000 < d.getTime())) {
data[row.url].attempt = d.getTime();
Bangle.http(url).then((r) => {
let resp = JSON.parse(r.resp);
data[row.url] = {
aqius: resp.data.current.pollution.aqius,
temp: resp.data.current.weather.tp,
time: d.getTime()
}
cb();
require("Storage").writeJSON("airqualityci.json", data);
});
}
}
});
}
}

let locs = {
name: "AirQuality",
items: []
};

if (config.rows) {
config.rows.forEach((row, id) => {
locs.items.push({
name: "AQI " + row.name,
show: () => {},
hide: () => {},
run: function() {
if ( ! config.rows[id].mode ) {
config.rows[id].mode = 2;
} else if ( config.rows[id].mode === 5 ){
config.rows[id].mode = 1;
} else {
config.rows[id].mode += 1;
}
this.emit("redraw");
require("Storage").writeJSON("airqualityci.settings.json", config);
},
get: function() {
checkAQI(() => this.emit("redraw"));
let aqi = data[config.rows[id].url]
let txt = "";
let aqius = (aqi && typeof(aqi.aqius) == "number") ? aqi.aqius : "...";
let temp = (aqi && typeof(aqi.temp) == "number") ? aqi.temp : "...";
switch ( config.rows[id].mode) {
case 2:
txt = aqius + " " + temp + "°";
break;
case 3:
txt = row.name + " " + aqius;
break;
case 4:
txt = row.name + " " + aqius + " " + temp + "°";
break;
case 5:
txt = row.name;
break;
default:
txt = aqius;
}
return {
text: txt,
short: aqius,
img: atob("GBiBAAA4AAB8AAd+AA++QB/d8D/t8D/p+DvqcBUPsA7vgB9fwA7vgAXvgAH/AAHwgAD3wAbfwA9/wA+/gA/fAA/OAA/gAAfAAAAAAA==")
};
}
});
});
}

return locs;
})
116 changes: 116 additions & 0 deletions apps/airqualityci/custom.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<style>
input.offset {
width: 80px;
}
input.name {
width: 150px;
}
</style>
</head>
<body>
<p>The AQI and temp are sourced from IQAir, which requires "Allow Internet Access" to be enabled in Gadgetbridge.</p>
<p>To use AQI, you need an IQAir API key. To get one for free, create an IQAir account, then create an API key in <a target="_blank" href="https://dashboard.iqair.com/personal/api-keys">your user dashboard</a>.</p>
<p>To specify the IQAir source, find your city on the IQAir website, then copy and paste the URL. It should look something like https://www.iqair.com/usa/new-york/new-york-city</p>
<table id="rows" class="table table-scroll">
<tr>
<th>Name</th>
<th>IQAir URL</th>
</tr>
</table>

<button id="addrow" class="btn">+</button>
<button id="delrow" class="btn">-</button>
<br>
<input id="api-key" type="text" placeholder="IQAir API Key"></input>
<br>
<button id="upload" class="btn btn-primary">Upload</button>
<template id="row-template">
<tr class="row">
<td>
<input type="text" class="name"></input>
</td>
<td>
<input type="text" class="url"></input>
</td>
</tr>
</template>

<script src="../../core/lib/customize.js"></script>

<script>
let storedString = localStorage.getItem('airqualityclockinfo-config');
let stored = {};
if (storedString) {
stored = JSON.parse(storedString);
}

if (stored.apiKey) {
document.getElementById("api-key").value = stored.apiKey;
}

if (!stored.rows) {
stored.rows = [
{
name: "NYC",
url: "https://www.iqair.com/usa/new-york/new-york",
}
];
}

let tbl = document.getElementById("rows");
let tmp = document.getElementById("row-template");
for (let i = 0; i < stored.rows.length; i++) {
let row = tmp.content.cloneNode(true);
let storedRow = stored.rows[i]
row.querySelector(".name").value = storedRow.name;
row.querySelector(".url").value = storedRow.url;
tbl.append(row);
}

document.getElementById("addrow").addEventListener("click", function() {
let tbl = document.getElementById("rows");
let tmp = document.getElementById("row-template");
let row = tmp.content.cloneNode(true);
tbl.append(row);
});

document.getElementById("delrow").addEventListener("click", function() {
let tbl = document.getElementById("rows");
tbl.deleteRow(tbl.rows.length -1);
});

document.getElementById("upload").addEventListener("click", function() {
let config = {
rows: [],
apiKey: document.getElementById("api-key").value
};

document.querySelectorAll(".row").forEach((row) => {
let name = row.querySelector(".name").value;
let url = row.querySelector(".url").value;
if ( name != "" && url != "" ) {
config.rows.push({
name: name,
url: url,
});
}
});

localStorage.setItem('airqualityclockinfo-config', JSON.stringify(config));

// send finished app (in addition to contents of app.json)
sendCustomizedApp({
storage:[
{
name: "airqualityci.settings.json",
content: JSON.stringify(config)
},
]
});
});
</script>
</body>
</html>
18 changes: 18 additions & 0 deletions apps/airqualityci/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{ "id": "airqualityci",
"name": "Air Quality Clockinfo",
"version": "0.01",
"description": "Create clockinfos for air quality and temperature",
"icon": "app.png",
"type": "clkinfo",
"tags": "clkinfo",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"custom": "custom.html",
"storage": [
{"name":"airqualityci.clkinfo.js","url":"clkinfo.js"}
],
"data": [
{"name":"airqualityci.json"},
{"name":"airqualityci.settings.json"}
]
}