Skip to content

Instantly share code, notes, and snippets.

@bitmorse
Created March 26, 2025 10:01
Show Gist options
  • Save bitmorse/836b895d04aa44996f29bf9ec3e9c1f1 to your computer and use it in GitHub Desktop.
Save bitmorse/836b895d04aa44996f29bf9ec3e9c1f1 to your computer and use it in GitHub Desktop.
InfluxDB Plot HTML to embed into Nocobase Iframe
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>OpenWRT Load Monitor</title>
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/@influxdata/giraffe"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
h1 {
color: #333;
}
.loading {
margin: 40px;
font-size: 18px;
color: #666;
}
.error {
margin: 40px;
font-size: 18px;
color: #d32f2f;
}
</style>
</head>
<body>
<h1>OpenWRT Load Monitor</h1>
<main id="root"></main>
<script type="text/javascript">
class PlotRenderer extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
error: null,
fluxResponse: null
};
}
componentDidMount() {
this.fetchData();
this.refreshInterval = setInterval(() => this.fetchData(), 60000);
}
componentWillUnmount() {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
}
}
fetchData() {
const url = 'YOUR INFLUX URL';
const org = 'YOUR INFLUX ORG';
const token = 'YOUR INFLUX RO TOKEN'; //READ ONLY!!!!!!
const query = `
from(bucket: "AVI")
|> range(start: -24h)
|> filter(fn: (r) => r._measurement == "openwrt_7628")
|> filter(fn: (r) => r._field == "load")
|> yield(name: "load")
`;
axios({
method: 'POST',
url: url,
headers: {
'Authorization': `Token ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/csv'
},
params: {
org
},
data: {
query
}
})
.then(response => {
console.log("Data received:", response.data);
// Add the missing headers that Giraffe expects
const headers = [
"#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string",
"#group,false,false,true,true,false,false,true,true,true,true",
"#default,_result,,,,,,,,,"
].join("\n");
// Combine headers with the data
const completeData = headers + "\n" + response.data;
console.log("Complete data with headers:", completeData);
this.setState({
loading: false,
fluxResponse: completeData
});
})
.catch(error => {
console.error("Error fetching data:", error);
this.setState({
loading: false,
error: `Error fetching data: ${error.message}`
});
});
}
render() {
const { loading, error, fluxResponse } = this.state;
if (loading) {
return React.createElement('div', { className: 'loading' }, 'Loading data...');
}
if (error) {
return React.createElement('div', { className: 'error' }, error);
}
const style = {
width: "calc(70vw - 20px)",
height: "calc(70vh - 20px)",
margin: "40px",
};
// If we have data, create a plot with it
if (fluxResponse) {
try {
// Create a simple line layer
const lineLayer = {
type: "line",
x: "_time",
y: "_value",
colors: ["#31C0F6"],
lineWidth: 2
};
// Create the config using the fluxResponse with added headers
const config = {
fluxResponse,
layers: [lineLayer]
};
// Create the Plot component
const Plot = React.createElement(Giraffe.Plot, {config}, null);
return React.createElement('div', {style}, Plot);
} catch (e) {
console.error("Error creating plot:", e);
return React.createElement('div', { className: 'error' },
`Error creating plot: ${e.message}. Check console for details.`);
}
}
return React.createElement('div', null, 'No data available');
}
}
// Render the component
ReactDOM.render(
React.createElement(PlotRenderer),
document.getElementById('root')
);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment