Browse Source

New UI

* with Vue.js
* add .group to device
master
sipp11 7 years ago
parent
commit
bb9e457b89
  1. 1
      helper.go
  2. 286
      html/home.html
  3. 14
      main.go
  4. 3
      model.go

1
helper.go

@ -34,6 +34,7 @@ func GetDevices(svc *d.DynamoDB) (*d.ScanOutput, error) {
proj := expression.NamesList(
expression.Name("device"),
expression.Name("group"),
expression.Name("name"))
expr, _ := expression.NewBuilder().WithProjection(proj).Build()

286
html/home.html

@ -6,204 +6,106 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="IoT around the house">
<title>Life Around</title>
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css" integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous">
<!--[if lte IE 8]>
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/grids-responsive-old-ie-min.css">
<![endif]-->
<!--[if gt IE 8]><!-->
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/grids-responsive-min.css">
<!--<![endif]-->
<!--[if lte IE 8]>
<link rel="stylesheet" href="/combo/1.18.13?/css/layouts/blog-old-ie.css">
<![endif]-->
<!--[if gt IE 8]><!-->
<link rel="stylesheet" href="http://hq.10ninox.com/styles/blog.css">
<!--<![endif]-->
<!--[if lt IE 9]>
<script src="http://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7/html5shiv.js"></script>
<![endif]-->
<style>
path {
stroke: rgb(237, 40, 1);
stroke-width: 2;
fill: none;
}
.dash {
padding: 1em;
font-size: 16px;
}
.dash-container {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
.graph {
position: relative;
width: 250px;
height: 50px;
}
.dash-one {
width: 250px;
text-align: center;
font-size: 1em;
background: #33333322;
padding: 10px 0;
overflow: wrap;
}
.dash-one p {
font-size: 4em;
margin: 0em;
}
.dash-one h2 {
font-size: 1.1em;
margin: 2px 5px;
}
h1 small {
font-size: 0.8rem;
margin: 0 10px;
color: #666;
}
</style>
<link rel="stylesheet" href="https://static.10ninox.com/css/bulma.min.css" />
<style>
h1.subtitle>small {
font-size: 0.83rem;
}
</style>
</head>
<body>
<div id="layout" class="pure-g">
<!-- <div class="sidebar pure-u-1 pure-u-md-1-4">
<ul class="device-list"></ul>
</div> -->
<div id="content" class="pure-u-1"></div>
<div class="container" id="app">
<div class="notification" v-if="message.length > 0">
{{ message }}
</div>
<div v-for="group in groups">
<section class="hero is-light">
<div class="hero-body">
<div class="container">
<h1 class="title">
{{ group }}
</h1>
</div>
</div>
</section>
<div class="columns is-multiline" >
<div class="column is-one-third" v-for="device in devices[group]">
<div class="box has-ribbon">
<div class="ribbon is-danger" v-if="device.needWater">รดนำดวย!</div>
<h1 class="title">{{ device.info.name }}</h1>
<h2 class="subtitle is-6">{{ device.fromNow }}</h2>
<span class="tag is-light">{{ device.info.sensor }}</span>
<span class="tag is-danger" v-if="device.needBattery">เปลยน Battery</span>
<div class="columns">
<div class="column">
<p>Temperature <small>ºC</small></p>
<h1 class="subtitle is-1">{{ device.data.temperature }}</h1>
</div>
<div class="column" v-if="device.info.sensor !== 'Xiaomi Mi Flora'">
<p>ความชนอากาศ <small>%</small></p>
<h1 class="subtitle is-1">{{ device.data.humidity }} </h1>
</div>
<div class="column" v-if="device.info.sensor === 'Xiaomi Mi Flora'">
<p>ความชนของด<small>%</small></p>
<h1 class="subtitle is-1">{{ device.data.moisture }} </h1>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.14/moment-timezone.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.14/moment-timezone.mi
n.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script>
function displayGraph(id, data, width, height, interpolation) {
//, animate, updateDelay, transitionDelay) {
var animate = true, updateDelay = 1000, transitionDelay = 1000;
// create an SVG element inside the #graph div that fills 100% of the div
var graph = d3.select(id).append("svg:svg");
// X scale will fit values from 0-10 within pixels 0-100
var x = d3.scaleLinear().domain([0, 152]).range([0, width]); // starting point is -5 so the first value doesn't show and slides off the edge as part of the transition
// Y scale will fit values from 0-10 within pixels 0-100
var y = d3.scaleLinear().domain([0, 80]).range([0, height]);
// create a line object that represents the SVN line we're creating
var line = d3.line()
// assign the X function to plot our line as we wish
.x(function (d, i) {
// verbose logging to show what's actually being done
//console.log('Plotting X value for data point: ' + d + ' using index: ' + i + ' to be at: ' + x(i) + ' using our xScale.');
// return the X coordinate where we want to plot this datapoint
return x(i);
})
.y(function (d) {
// verbose logging to show what's actually being done
//console.log('Plotting Y value for data point: ' + d + ' to be at: ' + y(d) + " using our yScale.");
// return the Y coordinate where we want to plot this datapoint
return y(d);
})
if (interpolation == "basis") {
line.curve(d3.curveCatmullRom.alpha(0.5))
}
// display the line by appending an svg:path element with the data line we created above
graph.append("svg:path").attr("d", line(data));
// or it can be done like this
//graph.selectAll("path").data([data]).enter().append("svg:path").attr("d", line);
function redrawWithAnimation() {
// update with animation
graph.selectAll("path")
.attr("transform", "translate(" + x(1) + ")") // set the transform to the right by x(1) pixels (6 for the scale we've set) to hide the new value
.data([data]) // set the new data
.attr("d", line) // apply the new data values ... but the new value is hidden at this point off the right of the canvas
.transition() // start a transition to bring the new value into view
.ease(d3.easeLinear)
.duration(transitionDelay * 0.95) // for this demo we want a continual slide so set this to the same as the setInterval amount below
.attr("transform", "translate(" + x(0) + ")"); // animate a slide to the left back to x(0) pixels to reveal the new value
/* thanks to 'barrym' for examples of transform: https://gist.github.com/1137131 */
}
function redrawWithoutAnimation() {
// static update without animation
graph.selectAll("path")
.data([data]) // set the new data
.attr("d", line); // apply the new data values
}
redrawWithoutAnimation();
}
(function () {
var baseUrl = '',
allUrl = baseUrl + '/all',
$sidebar = $('.sidebar'),
$list = $sidebar.find('.device-list'),
$content = $('#content');
var addToList = function(item) {
$list.append(
`<li><a href="#${item['device']}">` +
`${item["device"]}</a></li>`);
};
var addEleToDashBoard = function(item) {
var data = item['data'];
var xid = item['device'].replace(/:/g, '_')
var tmsp = moment.tz(item['timestamp'], "utc").fromNow()
var str = `<div class="pure-u-md-1-2 dash" id="${xid}">` +
`<h1>${item['info']['name']}<small>${tmsp}</small></h1>` +
`<div class="dash-container">` +
`<div class="dash-one temperature"><h2>Temp</h2><p>${data['temperature']}</p> <div class="graph" id="${xid}-temperature"></div></div>` +
`<div class="dash-one moisture"><h2>Moisture</h2><p>${data['moisture']}</p> <div class="graph" id="${xid}-moisture"></div></div>` +
`<div class="dash-one humidity"><h2>Humidity</h2><p>${data['humidity']}</p> <div class="graph" id="${xid}-humidity"></div></div>` +
`</div>` +
`</div>`;
$content.append(str);
};
var reloadListener = function() {
$('.dash').on('click', function () {
var that = $(this),
id = that.attr('id').replace(/_/g, ':'),
oneUrl = `${baseUrl}/id/${id}?limit=150`;
that.off('click');
$.getJSON(oneUrl, function(data, textStatus, xhr) {
var isFlora = data[0]['info']['sensor'].toLowerCase().indexOf("flora") > -1;
var extra = (isFlora ? 'humidity' : 'moisture');
var ks = ['temperature', extra];
for (var ik in ks) {
var key = ks[ik];
var ll = data.map(function(ele, i) {
return ele['data'][key]
});
var tid = `#${that.attr('id')}-${key}`;
displayGraph(tid, ll, 250, 50, "basic");
}
});
});
}
$.getJSON(allUrl, function(data, textStatus, xhr) {
for (var i=0; i<data.length; i++) {
var x = data[i];
addToList(x);
addEleToDashBoard(x);
var isFlora = x['info']['sensor'].toLowerCase().indexOf("flora") > -1;
var toHide = (isFlora ? 'humidity' : 'moisture');
$('#' + x['device'].replace(/:/g, '_')).find('.'+ toHide).hide();
}
reloadListener();
});
})(jQuery);
var app = new Vue({
el: '#app',
data: {
url: { base: 'http://iot.10ninox.com', all: '/all' },
message: '',
groups: [],
devices: {},
},
created: function() {
this.getDevices()
},
methods: {
getDevices: function() {
var vm = this
axios.get(`${vm.url.base}${vm.url.all}`)
.then(function(resp) {
var data = resp.data
vm.groups = Array.from(new Set(resp.data.map((i) => i.group)))
vm.groups.forEach(ele => {
vm.devices[ele] = data.filter((i) => {
if (i.group !== ele) return
i.needWater = false
i.needBattery = false
var sensor = i.info.sensor.toLowerCase()
if (sensor === 'xiaomi mi flora' || sensor === 'mijia') {
i.needBattery = i.data.battery < 10
}
if (sensor === 'Xiaomi Mi Flora') {
i.needWater = i.data.moisture < 40 ? true : false
}
i.fromNow = moment.tz(i.timestamp, '').fromNow()
return i
})
})
})
.catch(function(error){
vm.message = 'Error! Could not reach the API. ' + error
})
},
}
})
</script>
</body>
</html>

14
main.go

@ -12,28 +12,19 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/flosch/pongo2"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
var tplHome = pongo2.Must(pongo2.FromFile("html/home.html"))
var sess *session.Session
var err error
var svc *dynamodb.DynamoDB
// func indexHandler(w http.ResponseWriter, r *http.Request) {
func indexHandler(c echo.Context) error {
// Execute the template per HTTP request
out, err := tplHome.Execute(pongo2.Context{"query": "dddd"})
if err != nil {
return err
}
return c.HTML(http.StatusOK, out)
return c.File("html/home.html")
}
func healthCheckHandler(c echo.Context) error {
// A very simple health check.
return c.JSON(http.StatusOK, `{"alive": true}`)
}
@ -49,9 +40,10 @@ func overallHandler(c echo.Context) error {
}
data, _ := device.History(svc, 1)
one := data[0]
one.Group = device.Group
one.Info.Name = device.Name
one.Info.Model = device.Model
result = append(result, data[0])
result = append(result, one)
}
return c.JSON(http.StatusOK, result)
}

3
model.go

@ -8,6 +8,7 @@ import (
// Device struct for store device data from iot-item table
type Device struct {
Device string `json:"device"`
Group string `json:"group"`
Name string `json:"name"`
Model string `json:"model"`
}
@ -33,6 +34,7 @@ type SensorData struct {
// Item is an item to generalize every IoT devices
type Item struct {
Device string `json:"device"`
Group string `json:"group"`
Timestamp string `json:"timestamp"`
Info ItemInfo `json:"info"`
Data SensorData `json:"data"`
@ -47,6 +49,7 @@ func (d Device) History(svc *dynamodb.DynamoDB, limit int64) ([]Item, error) {
}
for _, i := range result.Items {
item := Item{}
item.Group = d.Group
err = dynamodbattribute.UnmarshalMap(i, &item)
item.Info.Name = d.Name
item.Info.Model = d.Model

Loading…
Cancel
Save