Skip to content

Commit af03bd3

Browse files
committed
first release
1 parent 49e4f47 commit af03bd3

16 files changed

+138
-87
lines changed

README.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,28 @@
22
This is a community based opensource tool that is capable of fetching data of Awair Element devices from Awair servers and send them to a custom endpoint.
33
The idea behind this project is to send these data to PlanetWatch servers in order to be able to be still elibile for rewards.
44

5+
## First Release
6+
I've finally finished a first, basic version of this tool. I wanted to very hurry up cause i know the community needs this kind of functionality.
7+
I'm goint to upload two builds of this tool at the first very moment:
8+
- one for windows (x64)
9+
- one for MacOs (M1) (I've tested on M1 but i'm not sure it will work only on M1, but also on Intel, just drop a comment if you can't run this on Intel)
10+
11+
## DISCLAIMER AND WARNINGS
12+
This tool was built in very few days so it can be full of bugs. For the time being i've found one main issue while using this tool. Since the authentication is a tricky process, once you launch the application you have to force the reload when it got stucked on "Loading..." page using the "View" -> "Force Reload"
13+
14+
## How to use this tool
15+
1. Open the tool
16+
2. Log in with the PLANETWATCH CREDENTIALS (Read the disclaimer, if you got stucked, pls refresh with Force Reload (even several times))
17+
3. Open your Awair Home Application
18+
4. Click one of your sensors and press Awair+, Awair APIs Beta, Cloud API, Get API Token
19+
5. Once you have clicked the Get API Token you will be able to insert the AWAIR HOME Credentials. Then you will be able to grab the token (ex: eyJ0......)
20+
5. Once you got it insert it into the "Add sensor" section (into the search bar).
21+
6. Search
22+
7. Press "Add your token"
23+
8. Repeat from step 3 for EACH awair account you own
24+
9. Finish!
25+
26+
527
## Installation
628
Since the project is not yet approved by PlanetWatch Company, the software is basically only fetching data from Awair's servers. For the time being we discourage any usage of this project until PlanetWatch approves and shares the endpoints where to write our data.
729

@@ -20,6 +42,4 @@ npm run make
2042

2143
The project was developed on MacOS and the building section is only for MacOS. Feel free to contribute to the project by adding your distro building section (Windows/Linux) with a Pull Request.
2244

23-
We will appreciate any help to improve of the code.
24-
25-
Stay tuned for next updates coming in the next days!
45+
We will appreciate any help to improve of the code.

package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
"version": "0.0.1",
55
"description": "An Awair Element data uploader",
66
"main": ".webpack/main",
7+
"author": {
8+
"name": "Sheherezadhe and the lovely PW Comunity"
9+
},
710
"scripts": {
811
"start": "electron-forge start",
912
"package": "electron-forge package",
@@ -26,7 +29,9 @@
2629
{
2730
"name": "@electron-forge/maker-zip",
2831
"platforms": [
29-
"darwin"
32+
"darwin",
33+
"linux",
34+
"windows"
3035
]
3136
},
3237
{
@@ -46,7 +51,7 @@
4651
"@electron-forge/plugin-webpack",
4752
{
4853
"mainConfig": "./webpack.main.config.js",
49-
"devContentSecurityPolicy": "connect-src 'self' https://developer-apis.awair.is 'unsafe-eval'",
54+
"devContentSecurityPolicy": "connect-src 'self' http://wearableapi.test.planetwatch.io http://login.planetwatch.io https://developer-apis.awair.is 'unsafe-eval'",
5055
"renderer": {
5156
"config": "./webpack.renderer.config.js",
5257
"entryPoints": [
@@ -70,6 +75,7 @@
7075
"@electron-forge/maker-squirrel": "^6.0.0-beta.63",
7176
"@electron-forge/maker-zip": "^6.0.0-beta.63",
7277
"@electron-forge/plugin-webpack": "^6.0.0-beta.63",
78+
"@types/http-server": "^0.12.1",
7379
"@types/javascript-time-ago": "^2.0.3",
7480
"@types/luxon": "^2.3.1",
7581
"@types/react": "^17.0.38",
@@ -96,6 +102,8 @@
96102
"antd": "^4.19.3",
97103
"axios": "^0.26.1",
98104
"electron-squirrel-startup": "^1.0.0",
105+
"express": "^4.17.3",
106+
"http-server": "^14.1.0",
99107
"javascript-time-ago": "^2.3.13",
100108
"jwt-decode": "^3.1.2",
101109
"keycloak-js": "^17.0.0",

src/components/AvailableSensors.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import { Sensors } from '../types/planetWatch/sensors';
44
import './AvailableSensors.css';
55

66
const AvailableSensors = () => {
7-
const [sensors, setSensors] = useState<Sensors>([]);
7+
const [sensors, setSensors] = useState<Sensors>(undefined);
88

99
useEffect(() => {
1010
Scheduler.getInstance().loadPWSensorsList(() => { }).then((res) => {
11-
console.log(res);
1211
setSensors(res);
1312
});
1413
}, []);
@@ -19,18 +18,19 @@ const AvailableSensors = () => {
1918
<p className='titleAvailable'>
2019
Available PW Sensors
2120
</p>
22-
<div className='boxAvailable'>
23-
{
24-
sensors && sensors.map((sensor, index) => (
25-
<div className='list' style={{ backgroundColor: index % 2 ? 'rgba(211, 211, 211, 0.571)' : 'transparent' }}>
26-
Sensor name
21+
<div className='boxAvailable'>
22+
{
23+
sensors && sensors.data
24+
.filter((sensor) => sensor.sensorId.includes('awair-element'))
25+
.map((sensor, index) => (
26+
<div key={index} className='list' style={{ backgroundColor: index % 2 ? 'rgba(211, 211, 211, 0.571)' : 'transparent' }}>
2727
{
28-
// sensor.id??? Waiting for PW Specifications
28+
sensor.sensorId
2929
}
3030
</div>
3131
))
32-
}
33-
</div>
32+
}
33+
</div>
3434
</>
3535
);
3636
};

src/components/Log.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const Log = () => {
2525
<div className='scroll'>
2626
{
2727
data.map((log, index) => (
28-
<div style={{ backgroundColor: index % 2 ? 'white' : 'rgba(211, 211, 211, 0.571)', paddingLeft: 10 }}>
28+
<div key={index} style={{ backgroundColor: index % 2 ? 'white' : 'rgba(211, 211, 211, 0.571)', paddingLeft: 10 }}>
2929
{log}
3030
</div>
3131
))

src/components/ManageSensors.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525

2626
.sensorList {
2727
margin-top: 10px;
28+
margin-bottom: 10px;
2829
background-color: rgba(211, 211, 211, 0.571);
29-
height: 268px;
30+
height: 225px;
3031
overflow-y: scroll;
3132
scrollbar-width: thin;
3233
overflow: overlay;

src/components/ManageSensors.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const ManageSensors = () => {
2020
setRegisteredAccounts(Scheduler.getInstance().getAccounts());
2121
}, []);
2222

23-
const addAccount = async () => {
23+
const onAddAccount = async () => {
2424
setRegisteredAccounts(await Scheduler.getInstance().addSubscription({ jwt: tempJwt, devices }));
2525
};
2626

@@ -48,6 +48,10 @@ const ManageSensors = () => {
4848
setRegisteredAccounts(Scheduler.getInstance().removeSubscription(device));
4949
};
5050

51+
const onRemoveAllDevice = () => {
52+
setRegisteredAccounts(Scheduler.getInstance().removeAllSubscriptions());
53+
};
54+
5155
return (
5256
<>
5357
<p className='TitleManage'>Manage sensors</p>
@@ -71,26 +75,27 @@ const ManageSensors = () => {
7175
))
7276
}
7377
</div>
74-
<Button className='button' disabled={devices.length < 1} onClick={addAccount}>Add your token</Button>
78+
<Button className='button' disabled={devices.length < 1} onClick={onAddAccount}>Add your token</Button>
7579
<div className='title'>List of added sensors:</div>
7680
<div className='sensorList'>
7781
{
78-
registeredAccounts.map((account) => (
79-
<>
82+
registeredAccounts.map((account, index) => (
83+
<div key={index}>
8084
{
81-
account.devices.map((device, index) => (
82-
<div style={{ backgroundColor: index % 2 ? 'white' : 'transparent', padding: 5, display: 'flex' }}>
85+
account.devices.map((device, index2) => (
86+
<div key={index2} style={{ backgroundColor: index % 2 ? 'white' : 'transparent', padding: 5, display: 'flex' }}>
8387
<div className='deleteButton' onClick={() => onRemoveDevice(device)}>x</div>
8488
{
8589
device.deviceUUID
8690
}
8791
</div>
8892
))
8993
}
90-
</>
94+
</div>
9195
))
9296
}
9397
</div>
98+
<Button className='button' disabled={registeredAccounts.length < 1} onClick={onRemoveAllDevice}>Delete All sensors</Button>
9499
{
95100
error &&
96101
<div>Error while getting the devices of this jwt: {error}</div>

src/components/Status.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ const Status = () => {
2222
return (
2323
<div>
2424
{
25-
result.map((element) => (
26-
<>
27-
{element.calledAt !== '' && element.receivedAt !== '' && <div>
28-
{element.name}: {timeAgo.format(new Date(element.calledAt))} - {timeAgo.format(new Date(element.receivedAt))}
29-
</div>}
30-
</>
25+
result.map((element, index) => (
26+
<div key={index}>
27+
{element.calledAt !== '' && element.receivedAt !== '' &&
28+
<div>
29+
{element.name}: {timeAgo.format(new Date(element.calledAt))} - {timeAgo.format(new Date(element.receivedAt))}
30+
</div>
31+
}
32+
</div>
3133
))
3234
}
3335
</div>

src/constants.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ export const constants = {
33
getLatestData: 'https://developer-apis.awair.is/v1/users/self/devices/{device_type}/{device_id}/air-data/latest',
44
getDevices: 'https://developer-apis.awair.is/v1/users/self/devices'
55
},
6-
// Waiting for PW Specifications
76
planetWatch: {
8-
sensors: '',
9-
sendData: '',
7+
sensors: 'https://wearableapi.planetwatch.io/api/sensors',
8+
sendData: 'https://wearableapi.planetwatch.io/api/data/devicedata',
109
},
11-
// Waiting for PW Specifications
1210
identityProvider: {
13-
url: '',
14-
realm: '',
15-
clientId: '',
11+
url: 'https://login.planetwatch.io/auth',
12+
realm: 'Planetwatch',
13+
clientId: 'external-login',
1614
role: ''
1715
},
1816
routes: {

src/index.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { app, BrowserWindow } from 'electron';
2+
23
// This allows TypeScript to pick up the magic constant that's auto-generated by Forge's Webpack
34
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
45
// whether you're running in development or production).
@@ -15,11 +16,28 @@ const createWindow = (): void => {
1516
const mainWindow = new BrowserWindow({
1617
height: 600 * 1.2,
1718
width: 800 * 1.2,
19+
webPreferences: {
20+
devTools: false
21+
}
1822
});
1923

2024
// and load the index.html of the app.
2125
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
2226

27+
const { session: { webRequest } } = mainWindow.webContents;
28+
const filter = {
29+
urls: [
30+
'http://localhost:33333/keycloak-redirect*'
31+
]
32+
};
33+
webRequest.onBeforeRequest(filter, async ({ url }) => {
34+
console.log(url);
35+
const params = url.slice(url.indexOf('#'));
36+
console.log(params);
37+
console.log(MAIN_WINDOW_WEBPACK_ENTRY + params);
38+
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY + params);
39+
});
40+
2341
// Open the DevTools.
2442
// mainWindow.webContents.openDevTools();
2543
};

src/mappers/AwairToPlanetWatchMapper.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ import { AwairLatestData } from '../types/awair/latestData';
22
import { PlanetWatchDataPacket } from '../types/planetWatch/dataPacket';
33

44
const map = (sensorId: string, data: AwairLatestData): PlanetWatchDataPacket => {
5-
// Waiting for PW Specifications
65
const response: PlanetWatchDataPacket = {
7-
6+
deviceId: sensorId,
7+
...(data.data[0])
88
};
9-
109
return response;
1110
};
1211

src/scheduler.ts

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import AwairController from './services/awairService';
22
import PlanetWatchService from './services/planetWatchService';
3-
import { AwairLatestData } from './types/awair/latestData';
43
import { AwairDevice } from './types/awair/devices';
54
import { Sensors } from './types/planetWatch/sensors';
65
import { DateTime } from 'luxon';
@@ -20,11 +19,6 @@ export interface Statuses {
2019
}
2120
}
2221

23-
interface SensorData {
24-
sensorId: string;
25-
data: AwairLatestData;
26-
}
27-
2822
export class Scheduler {
2923

3024
private static instance: Scheduler;
@@ -109,6 +103,7 @@ export class Scheduler {
109103
public removeAllSubscriptions() {
110104
this.accounts = [];
111105
this.saveAwairAccountsToLocalStorage();
106+
return [...this.accounts];
112107
}
113108

114109
public async addSubscription(account: AwairAccount) {
@@ -118,18 +113,19 @@ export class Scheduler {
118113
const deviceRegistered: AwairDevice[] = [];
119114

120115
account.devices.forEach((device) => {
121-
// Check if the sensor is registered on PW systems and then push
122-
// TODO
123-
deviceRegistered.push(device);
116+
// Check if the sensor is registered on PW systems and then push
117+
if (this.planetWatchRegisteredSensors.data.findIndex((localDevice) => localDevice.sensorId === device.deviceUUID) !== -1) {
118+
deviceRegistered.push(device);
119+
}
124120
});
125121

126122
if (deviceRegistered.length > 0) {
127123
this.accounts.push({ jwt: account.jwt, devices: deviceRegistered });
128124
this.saveAwairAccountsToLocalStorage();
129125
}
130-
console.log(this.accounts);
131126
}
132127
}
128+
this.reset();
133129
return [...this.accounts];
134130
}
135131

@@ -162,17 +158,18 @@ export class Scheduler {
162158
this.planetWatchRegisteredSensorsPromise.then((res) => {
163159
this.planetWatchRegisteredSensors = res;
164160
callback(res);
165-
});
161+
})
162+
.catch((err) => console.log(err));
166163
}
167164
const ready = await this.isReady();
168165
if (ready) {
169166
return this.planetWatchRegisteredSensors;
170167
}
171168
}
172169

173-
private async fetchAndSendData() {
170+
private async fetchAndSendData(accounts: AwairAccounts) {
174171
// Grab all the data from awair and save them in a structure
175-
this.accounts.forEach((account) => {
172+
accounts .forEach((account) => {
176173
const jwt = account.jwt;
177174

178175
account.devices.forEach((device) => {
@@ -185,10 +182,11 @@ export class Scheduler {
185182

186183
AwairController.getLatestData(jwt, deviceType, deviceId)
187184
.then((res) => {
188-
PlanetWatchService.sendData(AwairToPlanetWatchMapper.map(device.deviceUUID, res));
185+
PlanetWatchService.sendData(AwairToPlanetWatchMapper.map(device.deviceUUID, res))
186+
.catch((err) => console.log(err));
189187
this.statuses[device.deviceUUID].receivedAt = DateTime.now().toISO();
190188
this.statusSubscribers.forEach((callback) => callback({ ...this.statuses }));
191-
this.logs.push('Retrieving data from ' + deviceId + '\n');
189+
this.logs.push(DateTime.now().toFormat('[HH:mm:ss]') + ' - Retrieving data from ' + deviceId + '\n');
192190
this.logSubscribers.forEach((callback) => callback([...this.logs]));
193191
})
194192
.catch((error) => {
@@ -199,8 +197,8 @@ export class Scheduler {
199197
}
200198

201199
public start() {
202-
this.fetchAndSendData();
203-
this.scheduler = setInterval(this.fetchAndSendData, constants.interval);
200+
this.fetchAndSendData(this.accounts);
201+
this.scheduler = setInterval(() => this.fetchAndSendData(this.accounts), constants.interval);
204202
}
205203

206204
public stop() {

0 commit comments

Comments
 (0)