Skip to content

Commit

Permalink
Added example of sensor sampling rate
Browse files Browse the repository at this point in the history
Displays Sensor rate and status data
Can Update status and sampling period configuration
Uses "SamplingRate", "Values" interfaces in this example
Fix astarte-platform#99

Signed-off-by: Shubham Wadhwa <shubham.wadhwa@ispirata.com>
  • Loading branch information
Shubham Wadhwa committed May 8, 2020
1 parent a2b2688 commit ec2c9e9
Show file tree
Hide file tree
Showing 10 changed files with 15,718 additions and 0 deletions.
14,832 changes: 14,832 additions & 0 deletions examples/sensor-samplingrate/package-lock.json

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions examples/sensor-samplingrate/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "sensor-samplingrate",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.19.2",
"named-urls": "^2.0.0",
"bootstrap": "^4.4.1",
"react": "^16.12.0",
"react-bootstrap": "^1.0.1",
"react-dom": "^16.12.0",
"react-scripts": "3.4.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"format-js": "prettier --write \"src/**/*.js\""
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"prettier": "^1.19.1"
}
}
12 changes: 12 additions & 0 deletions examples/sensor-samplingrate/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>SENSOR SAMPLING VIEWER</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
106 changes: 106 additions & 0 deletions examples/sensor-samplingrate/src/apiHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { reverse } from "named-urls";
import axios from "axios";

const ENDPOINT = {
device_alias: "devices-by-alias/:device_alias/",
device_id: "devices/:id?",
interface_by_alias: "devices/:device_alias/interfaces/:interface/",
interface_by_id: "devices/:device_id/interfaces/:interface/",
interface_id_path: "devices/:device_id/interfaces/:interface/:sensor_id/:key"
};

export default class ApiHandler {
constructor({ endpoint, realm, token, version = "v1" }) {
this.token = token;
this.endpoint = new URL(endpoint);
this.realm = realm;
this.version = version;
this.socket = null;
}

getDevice(device) {
if (this.isDeviceId(device)) {
return this.getDeviceDataById(device);
} else {
return this.getDeviceDataByAlias(device);
}
}

getHeaders() {
return {
"Content-Type": "application/json",
Authorization: `Bearer ${this.token}`
};
}

getAPIUrl(endPoint, params) {
const path = reverse(ENDPOINT[endPoint], params);
return new URL(
`/appengine/${this.version}/${this.realm}/${path}`,
this.endpoint
);
}

GET(url, params) {
return axios.get(url, { headers: this.getHeaders(), params: params });
}

PUT(url, params) {
return axios.put(url, params, { headers: this.getHeaders() });
}

DELETE(url) {
return axios.delete(url, { headers: this.getHeaders() });
}

isDeviceId(value) {
const expression = new RegExp(/^[a-zA-Z0-9-_]{22}$/g);
return expression.test(value);
}

getDeviceDataById(device, params = {}) {
const URL = this.getAPIUrl("device_id", { id: device });
return this.GET(URL, params)
.then(response => Promise.resolve(response.data.data))
.catch(err => Promise.reject(err));
}

getDeviceDataByAlias(alias, params = {}) {
const URL = this.getAPIUrl("device_alias", { device_alias: alias });
return this.GET(URL, params)
.then(response => Promise.resolve(response.data.data))
.catch(err => Promise.reject(err));
}

getSensorValueById(id, interfaces, sensor_id, key, params = {}) {
const URL = this.getAPIUrl("interface_id_path", {
device_id: id,
interface: interfaces,
sensor_id: sensor_id,
key: key
});
return this.GET(URL, params);
}

setSensorSamplingById(data) {
const { id, interfaces, sensor_id, key, unset = false, params = {} } = data;
const URL = this.getAPIUrl("interface_id_path", {
device_id: id,
interface: interfaces,
sensor_id: sensor_id,
key: key
});
if (unset) {
return this.DELETE(URL);
}
return this.PUT(URL, params);
}

getInterfaceById(device_id, interface_id, params = {}) {
const URL = this.getAPIUrl("interface_by_id", {
device_id: device_id,
interface: interface_id
});
return this.GET(URL, params).then(response => response.data.data);
}
}
74 changes: 74 additions & 0 deletions examples/sensor-samplingrate/src/assets/css/cast.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
:root {
--gray-light-dark: #ced4da;
--lighter-gray: #f8f8f8;
}
body {
font-family: "Montserrat", sans-serif;
}
.sensor-id-search-div,
.main-modal .form-control,
.main-modal .form-control:focus,
.main-card .list-group {
background: var(--lighter-gray);
}
.btn.btn-primary,
.main-modal .form-control,
.main-row .input-group .form-control {
height: 2.5rem;
}
.device-status-div .status-tag {
width: 0.625rem;
height: 0.625rem;
}
.temperature-list h1 {
text-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.2);
}
.main-card.card .btn {
font-size: 1rem;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 57px;
}
.main-card.card .btn:focus,
.main-card.card .btn:hover,
.main-modal .form-control:focus,
.btn.btn-primary:focus,
.btn.btn-primary:hover {
box-shadow: none;
}
.main-card.card .btn img {
height: 0.9375rem;
}
.main-card .list-group-item,
.main-modal .form-label,
.main-modal .form-control,
.btn.btn-primary,
.main-row .input-group .form-control {
font-size: 0.875rem;
}
.main-modal .form-control:focus {
border-color: var(--gray-light-dark);
}
.main-card.card {
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.09);
}
.bg-sensor-theme,.btn-primary {
background-color: #6aa8d8 !important;
border: #6aa8d8;
}

.connected {
background-color: #008000;
}

.disconnected {
background-color: #ff0000;
}
.sensor-sampling-div .form-group{
margin: 1rem 0;
}
.border-separate{
border-top: 1px solid #cccccc;
padding-top: 0.9rem;
}
109 changes: 109 additions & 0 deletions examples/sensor-samplingrate/src/components/CredentialsModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, { Component } from "react";
import { Col, Form, Modal } from "react-bootstrap";

class CredentialsModal extends Component {
state = {
realm: undefined,
token: undefined,
endpoint: undefined
};

handleValue = e => {
e.preventDefault();
this.setState({ [e.target.name]: e.target.value });
};

handleSubmit = e => {
const form = e.currentTarget;
e.preventDefault();
if (form.checkValidity() === false) {
e.stopPropagation();
}
this.props.setCredentials(this.state);
};

componentDidUpdate(prevProps) {
const { visible } = this.props;
if (visible && prevProps.visible !== visible) {
const config = localStorage.AstarteConfig;
if (config) {
this.setState(JSON.parse(config));
}
}
}

render() {
const { visible } = this.props;
const { realm, endpoint, token } = this.state;
return (
<Modal
show={visible}
animation={true}
centered={true}
dialogClassName="main-modal"
backdrop="static"
>
<Modal.Body className="p-5">
<Col xs={12}>
<h6 className="text-center mb-4">
<b>Enter Your Details</b>
</h6>
</Col>
<Form onSubmit={this.handleSubmit}>
<Form.Group controlId="endPoint">
<Form.Label className="mb-1">
<b>Endpoint URL</b>
</Form.Label>
<Form.Control
value={endpoint}
required
name="endpoint"
onChange={this.handleValue}
type="text"
placeholder="Enter EndPoint"
className="rounded"
/>
</Form.Group>
<Form.Group controlId="realmName">
<Form.Label className="mb-1">
<b>Realm Name</b>
</Form.Label>
<Form.Control
value={realm}
required
name="realm"
onChange={this.handleValue}
type="text"
placeholder="Enter Realm Name"
className="rounded"
/>
</Form.Group>

<Form.Group controlId="token">
<Form.Label className="mb-1">
<b>Token</b>
</Form.Label>
<Form.Control
value={token}
required
onChange={this.handleValue}
name="token"
type="text"
placeholder="Enter Token Number"
className="rounded"
/>
</Form.Group>
<button
className="mt-3 bg-sensor-theme font-14 text-white py-2 text-uppercase px-4 rounded text-decoration-none"
type="submit"
>
Submit
</button>
</Form>
</Modal.Body>
</Modal>
);
}
}

export default CredentialsModal;
Loading

0 comments on commit ec2c9e9

Please sign in to comment.