Skip to content

Commit

Permalink
first release
Browse files Browse the repository at this point in the history
  • Loading branch information
Sheherezadhe committed Apr 1, 2022
1 parent 49e4f47 commit af03bd3
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 87 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@
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.
The idea behind this project is to send these data to PlanetWatch servers in order to be able to be still elibile for rewards.

## First Release
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.
I'm goint to upload two builds of this tool at the first very moment:
- one for windows (x64)
- 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)

## DISCLAIMER AND WARNINGS
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"

## How to use this tool
1. Open the tool
2. Log in with the PLANETWATCH CREDENTIALS (Read the disclaimer, if you got stucked, pls refresh with Force Reload (even several times))
3. Open your Awair Home Application
4. Click one of your sensors and press Awair+, Awair APIs Beta, Cloud API, Get API Token
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......)
5. Once you got it insert it into the "Add sensor" section (into the search bar).
6. Search
7. Press "Add your token"
8. Repeat from step 3 for EACH awair account you own
9. Finish!


## Installation
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.

Expand All @@ -20,6 +42,4 @@ npm run make

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.

We will appreciate any help to improve of the code.

Stay tuned for next updates coming in the next days!
We will appreciate any help to improve of the code.
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"version": "0.0.1",
"description": "An Awair Element data uploader",
"main": ".webpack/main",
"author": {
"name": "Sheherezadhe and the lovely PW Comunity"
},
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
Expand All @@ -26,7 +29,9 @@
{
"name": "@electron-forge/maker-zip",
"platforms": [
"darwin"
"darwin",
"linux",
"windows"
]
},
{
Expand All @@ -46,7 +51,7 @@
"@electron-forge/plugin-webpack",
{
"mainConfig": "./webpack.main.config.js",
"devContentSecurityPolicy": "connect-src 'self' https://developer-apis.awair.is 'unsafe-eval'",
"devContentSecurityPolicy": "connect-src 'self' http://wearableapi.test.planetwatch.io http://login.planetwatch.io https://developer-apis.awair.is 'unsafe-eval'",
"renderer": {
"config": "./webpack.renderer.config.js",
"entryPoints": [
Expand All @@ -70,6 +75,7 @@
"@electron-forge/maker-squirrel": "^6.0.0-beta.63",
"@electron-forge/maker-zip": "^6.0.0-beta.63",
"@electron-forge/plugin-webpack": "^6.0.0-beta.63",
"@types/http-server": "^0.12.1",
"@types/javascript-time-ago": "^2.0.3",
"@types/luxon": "^2.3.1",
"@types/react": "^17.0.38",
Expand All @@ -96,6 +102,8 @@
"antd": "^4.19.3",
"axios": "^0.26.1",
"electron-squirrel-startup": "^1.0.0",
"express": "^4.17.3",
"http-server": "^14.1.0",
"javascript-time-ago": "^2.3.13",
"jwt-decode": "^3.1.2",
"keycloak-js": "^17.0.0",
Expand Down
20 changes: 10 additions & 10 deletions src/components/AvailableSensors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { Sensors } from '../types/planetWatch/sensors';
import './AvailableSensors.css';

const AvailableSensors = () => {
const [sensors, setSensors] = useState<Sensors>([]);
const [sensors, setSensors] = useState<Sensors>(undefined);

useEffect(() => {
Scheduler.getInstance().loadPWSensorsList(() => { }).then((res) => {
console.log(res);
setSensors(res);
});
}, []);
Expand All @@ -19,18 +18,19 @@ const AvailableSensors = () => {
<p className='titleAvailable'>
Available PW Sensors
</p>
<div className='boxAvailable'>
{
sensors && sensors.map((sensor, index) => (
<div className='list' style={{ backgroundColor: index % 2 ? 'rgba(211, 211, 211, 0.571)' : 'transparent' }}>
Sensor name
<div className='boxAvailable'>
{
sensors && sensors.data
.filter((sensor) => sensor.sensorId.includes('awair-element'))
.map((sensor, index) => (
<div key={index} className='list' style={{ backgroundColor: index % 2 ? 'rgba(211, 211, 211, 0.571)' : 'transparent' }}>
{
// sensor.id??? Waiting for PW Specifications
sensor.sensorId
}
</div>
))
}
</div>
}
</div>
</>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/Log.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const Log = () => {
<div className='scroll'>
{
data.map((log, index) => (
<div style={{ backgroundColor: index % 2 ? 'white' : 'rgba(211, 211, 211, 0.571)', paddingLeft: 10 }}>
<div key={index} style={{ backgroundColor: index % 2 ? 'white' : 'rgba(211, 211, 211, 0.571)', paddingLeft: 10 }}>
{log}
</div>
))
Expand Down
3 changes: 2 additions & 1 deletion src/components/ManageSensors.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@

.sensorList {
margin-top: 10px;
margin-bottom: 10px;
background-color: rgba(211, 211, 211, 0.571);
height: 268px;
height: 225px;
overflow-y: scroll;
scrollbar-width: thin;
overflow: overlay;
Expand Down
19 changes: 12 additions & 7 deletions src/components/ManageSensors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ManageSensors = () => {
setRegisteredAccounts(Scheduler.getInstance().getAccounts());
}, []);

const addAccount = async () => {
const onAddAccount = async () => {
setRegisteredAccounts(await Scheduler.getInstance().addSubscription({ jwt: tempJwt, devices }));
};

Expand Down Expand Up @@ -48,6 +48,10 @@ const ManageSensors = () => {
setRegisteredAccounts(Scheduler.getInstance().removeSubscription(device));
};

const onRemoveAllDevice = () => {
setRegisteredAccounts(Scheduler.getInstance().removeAllSubscriptions());
};

return (
<>
<p className='TitleManage'>Manage sensors</p>
Expand All @@ -71,26 +75,27 @@ const ManageSensors = () => {
))
}
</div>
<Button className='button' disabled={devices.length < 1} onClick={addAccount}>Add your token</Button>
<Button className='button' disabled={devices.length < 1} onClick={onAddAccount}>Add your token</Button>
<div className='title'>List of added sensors:</div>
<div className='sensorList'>
{
registeredAccounts.map((account) => (
<>
registeredAccounts.map((account, index) => (
<div key={index}>
{
account.devices.map((device, index) => (
<div style={{ backgroundColor: index % 2 ? 'white' : 'transparent', padding: 5, display: 'flex' }}>
account.devices.map((device, index2) => (
<div key={index2} style={{ backgroundColor: index % 2 ? 'white' : 'transparent', padding: 5, display: 'flex' }}>
<div className='deleteButton' onClick={() => onRemoveDevice(device)}>x</div>
{
device.deviceUUID
}
</div>
))
}
</>
</div>
))
}
</div>
<Button className='button' disabled={registeredAccounts.length < 1} onClick={onRemoveAllDevice}>Delete All sensors</Button>
{
error &&
<div>Error while getting the devices of this jwt: {error}</div>
Expand Down
14 changes: 8 additions & 6 deletions src/components/Status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ const Status = () => {
return (
<div>
{
result.map((element) => (
<>
{element.calledAt !== '' && element.receivedAt !== '' && <div>
{element.name}: {timeAgo.format(new Date(element.calledAt))} - {timeAgo.format(new Date(element.receivedAt))}
</div>}
</>
result.map((element, index) => (
<div key={index}>
{element.calledAt !== '' && element.receivedAt !== '' &&
<div>
{element.name}: {timeAgo.format(new Date(element.calledAt))} - {timeAgo.format(new Date(element.receivedAt))}
</div>
}
</div>
))
}
</div>
Expand Down
12 changes: 5 additions & 7 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ export const constants = {
getLatestData: 'https://developer-apis.awair.is/v1/users/self/devices/{device_type}/{device_id}/air-data/latest',
getDevices: 'https://developer-apis.awair.is/v1/users/self/devices'
},
// Waiting for PW Specifications
planetWatch: {
sensors: '',
sendData: '',
sensors: 'https://wearableapi.planetwatch.io/api/sensors',
sendData: 'https://wearableapi.planetwatch.io/api/data/devicedata',
},
// Waiting for PW Specifications
identityProvider: {
url: '',
realm: '',
clientId: '',
url: 'https://login.planetwatch.io/auth',
realm: 'Planetwatch',
clientId: 'external-login',
role: ''
},
routes: {
Expand Down
18 changes: 18 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { app, BrowserWindow } from 'electron';

// This allows TypeScript to pick up the magic constant that's auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
Expand All @@ -15,11 +16,28 @@ const createWindow = (): void => {
const mainWindow = new BrowserWindow({
height: 600 * 1.2,
width: 800 * 1.2,
webPreferences: {
devTools: false
}
});

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

const { session: { webRequest } } = mainWindow.webContents;
const filter = {
urls: [
'http://localhost:33333/keycloak-redirect*'
]
};
webRequest.onBeforeRequest(filter, async ({ url }) => {
console.log(url);
const params = url.slice(url.indexOf('#'));
console.log(params);
console.log(MAIN_WINDOW_WEBPACK_ENTRY + params);
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY + params);
});

// Open the DevTools.
// mainWindow.webContents.openDevTools();
};
Expand Down
5 changes: 2 additions & 3 deletions src/mappers/AwairToPlanetWatchMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import { AwairLatestData } from '../types/awair/latestData';
import { PlanetWatchDataPacket } from '../types/planetWatch/dataPacket';

const map = (sensorId: string, data: AwairLatestData): PlanetWatchDataPacket => {
// Waiting for PW Specifications
const response: PlanetWatchDataPacket = {

deviceId: sensorId,
...(data.data[0])
};

return response;
};

Expand Down
32 changes: 15 additions & 17 deletions src/scheduler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import AwairController from './services/awairService';
import PlanetWatchService from './services/planetWatchService';
import { AwairLatestData } from './types/awair/latestData';
import { AwairDevice } from './types/awair/devices';
import { Sensors } from './types/planetWatch/sensors';
import { DateTime } from 'luxon';
Expand All @@ -20,11 +19,6 @@ export interface Statuses {
}
}

interface SensorData {
sensorId: string;
data: AwairLatestData;
}

export class Scheduler {

private static instance: Scheduler;
Expand Down Expand Up @@ -109,6 +103,7 @@ export class Scheduler {
public removeAllSubscriptions() {
this.accounts = [];
this.saveAwairAccountsToLocalStorage();
return [...this.accounts];
}

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

account.devices.forEach((device) => {
// Check if the sensor is registered on PW systems and then push
// TODO
deviceRegistered.push(device);
// Check if the sensor is registered on PW systems and then push
if (this.planetWatchRegisteredSensors.data.findIndex((localDevice) => localDevice.sensorId === device.deviceUUID) !== -1) {
deviceRegistered.push(device);
}
});

if (deviceRegistered.length > 0) {
this.accounts.push({ jwt: account.jwt, devices: deviceRegistered });
this.saveAwairAccountsToLocalStorage();
}
console.log(this.accounts);
}
}
this.reset();
return [...this.accounts];
}

Expand Down Expand Up @@ -162,17 +158,18 @@ export class Scheduler {
this.planetWatchRegisteredSensorsPromise.then((res) => {
this.planetWatchRegisteredSensors = res;
callback(res);
});
})
.catch((err) => console.log(err));
}
const ready = await this.isReady();
if (ready) {
return this.planetWatchRegisteredSensors;
}
}

private async fetchAndSendData() {
private async fetchAndSendData(accounts: AwairAccounts) {
// Grab all the data from awair and save them in a structure
this.accounts.forEach((account) => {
accounts .forEach((account) => {
const jwt = account.jwt;

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

AwairController.getLatestData(jwt, deviceType, deviceId)
.then((res) => {
PlanetWatchService.sendData(AwairToPlanetWatchMapper.map(device.deviceUUID, res));
PlanetWatchService.sendData(AwairToPlanetWatchMapper.map(device.deviceUUID, res))
.catch((err) => console.log(err));
this.statuses[device.deviceUUID].receivedAt = DateTime.now().toISO();
this.statusSubscribers.forEach((callback) => callback({ ...this.statuses }));
this.logs.push('Retrieving data from ' + deviceId + '\n');
this.logs.push(DateTime.now().toFormat('[HH:mm:ss]') + ' - Retrieving data from ' + deviceId + '\n');
this.logSubscribers.forEach((callback) => callback([...this.logs]));
})
.catch((error) => {
Expand All @@ -199,8 +197,8 @@ export class Scheduler {
}

public start() {
this.fetchAndSendData();
this.scheduler = setInterval(this.fetchAndSendData, constants.interval);
this.fetchAndSendData(this.accounts);
this.scheduler = setInterval(() => this.fetchAndSendData(this.accounts), constants.interval);
}

public stop() {
Expand Down
Loading

0 comments on commit af03bd3

Please sign in to comment.