forked from astarte-platform/astarte
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement sensor plot example with interval
Implement graph which enables choose a sensor and the display chart for the given interval Closes: astarte-platform#101 Signed-off-by: rifa sofic <rifa.sofic@secomind.com>
- Loading branch information
Showing
10 changed files
with
44,383 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{ | ||
"name": "sensor-interval", | ||
"version": "0.1.0", | ||
"private": true, | ||
"dependencies": { | ||
"axios": "^0.21.1", | ||
"bootstrap": "^4.6.0", | ||
"jquery": "^3.5.1", | ||
"moment": "^2.29.1", | ||
"named-urls": "^2.0.0", | ||
"plotly.js": "^1.58.4", | ||
"react": "^17.0.1", | ||
"react-bootstrap": "^1.5.0", | ||
"react-datepicker": "^4.16.0", | ||
"react-dom": "^17.0.1", | ||
"react-intl": "^6.4.4", | ||
"react-plotly.js": "^2.5.1", | ||
"react-scripts": "^4.0.2" | ||
}, | ||
"scripts": { | ||
"start": "react-scripts start", | ||
"build": "react-scripts build", | ||
"test": "react-scripts test", | ||
"eject": "react-scripts eject", | ||
"format": "prettier --write \"src/**/*.{js,jsx,css}\"" | ||
}, | ||
"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": "^2.2.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<title>Sensor Graph Example</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { reverse } from "named-urls"; | ||
import axios from "axios"; | ||
|
||
export const constant = { | ||
ID: "id", | ||
ALIAS: "alias", | ||
AVAILABLE_SENSORS: "AvailableSensors", | ||
VALUES: "Values", | ||
SAMPLING_RATE: "SamplingRate", | ||
REALM: "realm", | ||
TOKEN: "token", | ||
ENDPOINT: "endpoint", | ||
}; | ||
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/:path/value", | ||
interface_alias_path: | ||
"devices/:device_alias/interfaces/:interface/:path/value", | ||
interface_id_interval_path: "devices/:device_id/interfaces/:interface", | ||
}; | ||
|
||
function getAPIUrl(endPoint, params = null) { | ||
const path = reverse(Endpoint[endPoint], params); | ||
return getEndPoint() + "appengine/v1/" + getRealmName() + "/" + path; | ||
} | ||
|
||
function GET(url, params) { | ||
const headers = { | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${getAuthToken()}`, | ||
}; | ||
return axios.get(url, { headers: headers, params: params }); | ||
} | ||
|
||
// API Functions | ||
|
||
function getDeviceById(id, params = {}) { | ||
const URL = getAPIUrl("device_id", { id: id }); | ||
return GET(URL, params); | ||
} | ||
|
||
export function getAvailableSensors(device_id, interface_id) { | ||
// Construct the URL to fetch available sensors | ||
const URL = getAPIUrl("interface_id_interval_path", { | ||
device_id: device_id, | ||
interface: interface_id, | ||
}); | ||
|
||
// Use the GET function to fetch available sensors | ||
return GET(URL) | ||
.then((response) => { | ||
const availableSensors = response.data.data; | ||
return Promise.resolve(availableSensors); | ||
}) | ||
.catch((err) => { | ||
console.error("Error fetching available sensors:", err); | ||
return Promise.reject(err); | ||
}); | ||
} | ||
|
||
export const getDeviceDataById = (id, params = {}) => { | ||
return getDeviceById(id, params) | ||
.then((response) => { | ||
const data = response.data.data; | ||
const interfaces = Object.keys(data.introspection); | ||
const availableIndex = interfaces.findIndex( | ||
(key) => key.search(constant.AVAILABLE_SENSORS) > -1 | ||
); | ||
const valueIndex = interfaces.findIndex( | ||
(key) => key.search(constant.VALUES) > -1 | ||
); | ||
return Promise.resolve({ valueIndex, availableIndex, interfaces }); | ||
}) | ||
.catch((err) => { | ||
return Promise.reject(err); | ||
}); | ||
}; | ||
|
||
export const getDeviceDataByAlias = (alias, params = {}) => { | ||
return getDeviceByAlias(alias, params) | ||
.then((response) => { | ||
const data = response.data.data; | ||
const interfaces = Object.keys(data.introspection); | ||
const availableIndex = interfaces.findIndex( | ||
(key) => key.search(constant.AVAILABLE_SENSORS) > -1 | ||
); | ||
const valueIndex = interfaces.findIndex( | ||
(key) => key.search(constant.VALUES) > -1 | ||
); | ||
return Promise.resolve({ valueIndex, availableIndex, interfaces }); | ||
}) | ||
.catch((err) => { | ||
return Promise.reject(err); | ||
}); | ||
}; | ||
|
||
function getDeviceByAlias(alias, params = {}) { | ||
const URL = getAPIUrl("device_alias", { device_alias: alias }); | ||
return GET(URL, params); | ||
} | ||
|
||
export function getInterfaceById(device_id, interface_id, params = {}) { | ||
const URL = getAPIUrl("interface_by_id", { | ||
device_id: device_id, | ||
interface: interface_id, | ||
}); | ||
return GET(URL, params).then((response) => response.data.data); | ||
} | ||
|
||
export function getSensorValueById( | ||
device_id, | ||
interface_id, | ||
path, | ||
startDate, | ||
endDate, | ||
params = {} | ||
) { | ||
const URL = getAPIUrl("interface_id_path", { | ||
device_id: device_id, | ||
interface: interface_id, | ||
path: path, | ||
startDate: startDate, | ||
endDate: endDate, | ||
}); | ||
return GET(URL, params); | ||
} | ||
|
||
export function getInterfaceByAlias(device_alias, interface_id, params = {}) { | ||
const URL = getAPIUrl("interface_by_id", { | ||
device_alias: device_alias, | ||
interface: interface_id, | ||
}); | ||
return GET(URL, params).then((response) => response.data.data); | ||
} | ||
|
||
// LocalStorage Config | ||
|
||
export function setAuthToken(token) { | ||
localStorage.setItem(constant.TOKEN, token); | ||
} | ||
|
||
export function getAuthToken() { | ||
return localStorage.getItem(constant.TOKEN) || undefined; | ||
} | ||
|
||
export function setRealmName(realm_name) { | ||
localStorage.setItem(constant.REALM, realm_name); | ||
} | ||
|
||
export function getRealmName() { | ||
return localStorage.getItem(constant.REALM) || undefined; | ||
} | ||
|
||
export function setEndPoint(endpoint) { | ||
localStorage.setItem(constant.ENDPOINT, endpoint); | ||
} | ||
|
||
export function getEndPoint() { | ||
return localStorage.getItem(constant.ENDPOINT) || undefined; | ||
} | ||
|
||
export function isMissingCredentials() { | ||
return !(getEndPoint() && getAuthToken() && getRealmName()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
body { | ||
font-family: "Montserrat", sans-serif; | ||
} | ||
.sensor-id-search-div, | ||
.list-group-item { | ||
background: #f3f3f3; | ||
} | ||
.main-row .input-group .form-control { | ||
height: 2.5rem; | ||
} | ||
|
||
.main-card.card .btn img { | ||
height: 0.9375rem; | ||
} | ||
.main-row .input-group .form-control, | ||
.font-14 { | ||
font-size: 0.875rem; | ||
} | ||
.main-card { | ||
box-shadow: 0 0.0625rem 0.25rem 0 rgba(0, 0, 0, 0.09); | ||
} | ||
.list-div-main .list-group-item, | ||
.list-div-main .list-group-item:first-child { | ||
border-radius: 0; | ||
} | ||
.bg-sensor-theme { | ||
background-color: #6aa8d8; | ||
border: 1px solid #6aa8d8; | ||
} | ||
|
||
.font-14:hover { | ||
background-color: #6aa8d8; | ||
border: 1px solid #6aa8d8; | ||
} | ||
|
||
.font-14:focus { | ||
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); | ||
outline: 0; | ||
} | ||
|
||
.sensor-values-card{ | ||
width: 300px; | ||
} | ||
|
119 changes: 119 additions & 0 deletions
119
examples/sensor-interval/src/components/CredentialsModal.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import React, { Component } from "react"; | ||
import { Col, Form, Modal } from "react-bootstrap"; | ||
import { | ||
getAuthToken, | ||
getEndPoint, | ||
getRealmName, | ||
setAuthToken, | ||
setEndPoint, | ||
setRealmName, | ||
} from "../apiHandler"; | ||
|
||
class CredentialsModal extends Component { | ||
state = { | ||
realm_name: 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(); | ||
} | ||
const { token, realm_name, endpoint } = this.state; | ||
setAuthToken(token); | ||
setRealmName(realm_name); | ||
setEndPoint(new window.URL(endpoint).href); | ||
this.props.handleCredentialModal(); | ||
}; | ||
|
||
componentDidUpdate(prevProps) { | ||
const { visible } = this.props; | ||
if (visible && prevProps.visible !== visible) { | ||
this.setState({ | ||
realm_name: getRealmName(), | ||
token: getAuthToken(), | ||
endpoint: getEndPoint(), | ||
}); | ||
} | ||
} | ||
|
||
render() { | ||
const { visible } = this.props; | ||
const { realm_name, 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 font-weight-bold mb-4"> | ||
Enter Your Details | ||
</h6> | ||
</Col> | ||
<Form onSubmit={this.handleSubmit}> | ||
<Form.Group controlId="endPoint"> | ||
<Form.Label className="mb-1 font-weight-bold"> | ||
Endpoint URL | ||
</Form.Label> | ||
<Form.Control | ||
value={endpoint} | ||
required | ||
name="endpoint" | ||
onChange={this.handleValue} | ||
type="text" | ||
placeholder="Enter EndPoint" | ||
className="font-weight-normal rounded" | ||
/> | ||
</Form.Group> | ||
<Form.Group controlId="realmName"> | ||
<Form.Label className="mb-1 font-weight-bold"> | ||
Realm Name | ||
</Form.Label> | ||
<Form.Control | ||
value={realm_name} | ||
required | ||
name="realm_name" | ||
onChange={this.handleValue} | ||
type="text" | ||
placeholder="Enter Realm Name" | ||
className="font-weight-normal rounded" | ||
/> | ||
</Form.Group> | ||
<Form.Group controlId="token"> | ||
<Form.Label className="mb-1 font-weight-bold">Token</Form.Label> | ||
<Form.Control | ||
value={token} | ||
required | ||
onChange={this.handleValue} | ||
name="token" | ||
type="text" | ||
placeholder="Enter Token Number" | ||
className="font-weight-normal rounded" | ||
/> | ||
</Form.Group> | ||
<button | ||
className="mt-3 bg-sensor-theme font-14 text-white py-2 text-uppercase font-weight-normal px-4 rounded text-decoration-none" | ||
type="submit" | ||
> | ||
Submit | ||
</button> | ||
</Form> | ||
</Modal.Body> | ||
</Modal> | ||
); | ||
} | ||
} | ||
|
||
export default CredentialsModal; |
Oops, something went wrong.