291
|
1 <!doctype html>
|
|
2 <html>
|
|
3 <head>
|
|
4 <title>beaconmap</title>
|
|
5 <meta charset="utf-8" />
|
|
6 <script src="/lib/polymer/1.0.9/webcomponentsjs/webcomponents-lite.min.js"></script>
|
|
7 <script>
|
|
8 window.Polymer = {
|
|
9 dom: 'shadow',
|
|
10 };
|
|
11 </script>
|
|
12 <link rel="import" href="/lib/polymer/1.0.9/polymer/polymer.html">
|
|
13 <link rel="import" href="/lib/polymer/1.0.9/iron-ajax/iron-ajax.html">
|
|
14 <link rel="import" href="/lib/polymer/1.0.9/iron-list/iron-list.html">
|
|
15 <link rel="import" href="/lib/polymer/1.0.9/paper-header-panel/paper-header-panel.html">
|
|
16 <link rel="import" href="/lib/polymer/1.0.9/iron-flex-layout/iron-flex-layout-classes.html">
|
|
17 <style is="custom-style" include="iron-flex iron-flex-alignment iron-positioning"></style>
|
|
18
|
|
19 <script src="dat.gui.min.js"></script>
|
|
20 <link rel="import" href="house-model.html">
|
|
21 </head>
|
|
22 <body class="fullbleed layout vertical">
|
|
23 <dom-module id="beacon-devices">
|
|
24 <template>
|
|
25
|
|
26 <style>
|
|
27 iron-list { height: 100%; }
|
|
28 .row { border: 1px outset #dadada; margin: 2px; background: #f7f7f7;}
|
310
|
29 .selected {
|
|
30 background: #9191ff;
|
|
31 }
|
291
|
32 </style>
|
|
33 <iron-ajax url="devices"
|
|
34 auto
|
|
35 last-response="{{response}}"></iron-ajax>
|
|
36 <iron-list items="[[response.devices]]"
|
|
37 selection-enabled="true"
|
310
|
38 selected-item="{{selected}}"
|
|
39 selected-as="isSelected"
|
|
40 >
|
291
|
41 <template>
|
310
|
42 <div class$="row {{rowClass(isSelected)}}">
|
|
43 {{item.addr}} {{item.name}}
|
291
|
44 </div>
|
|
45 </template>
|
|
46 </iron-list>
|
|
47 </template>
|
|
48 <script>
|
|
49 HTMLImports.whenReady(function () {
|
|
50 Polymer({
|
|
51 is: "beacon-devices",
|
|
52 properties: {
|
|
53 response: { type: Object, notify: true },
|
|
54 selected: { type: Object, notify: true },
|
310
|
55 },
|
|
56 rowClass: function (isSelected) {
|
|
57 return isSelected ? 'selected' : '';
|
291
|
58 }
|
|
59 });
|
|
60 });
|
|
61 </script>
|
|
62 </dom-module>
|
|
63
|
|
64 <script src="/lib/rickshaw/90852d8/vendor/d3.min.js"></script>
|
|
65 <script>
|
|
66 var hashCode = function(s){
|
|
67 var hash = 0;
|
|
68 if (s.length == 0) return hash;
|
|
69 for (var i = 0; i < s.length; i++) {
|
|
70 var character = s.charCodeAt(i);
|
|
71 hash = ((hash<<5)-hash)+character;
|
|
72 hash = hash & hash; // Convert to 32bit integer
|
|
73 }
|
|
74 return hash;
|
|
75 };
|
|
76 var colorForAddr = function(addr) {
|
|
77 var hue = hashCode(addr) % 360;
|
|
78 return d3.hsl(hue, 1, 0.5) + "";
|
|
79 };
|
|
80 var colorForSensor = function(from) {
|
|
81 var hue = hashCode(from) % 360;
|
|
82 return d3.hsl(hue, .7, 0.7) + "";
|
|
83 };
|
|
84 </script>
|
|
85
|
|
86 <dom-module id="beacon-sensor-graph">
|
|
87 <link rel="import" type="css" href="/lib/rickshaw/90852d8/src/css/graph.css">
|
|
88 <link rel="import" type="css" href="/lib/rickshaw/90852d8/src/css/detail.css">
|
|
89 <link rel="import" type="css" href="/lib/rickshaw/90852d8/src/css/legend.css">
|
|
90
|
|
91 <template>
|
|
92 <iron-ajax id="get" url="sensor" params='{{params}}' auto last-response="{{response}}"></iron-ajax>
|
|
93 <div>{{sensor.from}}</div>
|
|
94 <div id="chart_container" on-click="onClick">
|
|
95 <div id="chart"></div>
|
|
96 </div>
|
|
97
|
|
98 </template>
|
|
99 <script src="/lib/jquery-2.0.3.min.js"></script>
|
|
100 <script src="/lib/rickshaw/90852d8/vendor/d3.min.js"></script>
|
|
101 <script src="/lib/rickshaw/90852d8/rickshaw.min.js"></script>
|
|
102 <script>
|
|
103 HTMLImports.whenReady(function () {
|
|
104 Polymer({
|
|
105 is: "beacon-sensor-graph",
|
|
106 properties: {
|
|
107 sensor: {type: Object},
|
|
108 response: {type: Object, notify: true},
|
|
109 params: {computed: '_params(sensor.from)'}
|
|
110 },
|
|
111 _params: function(from) {
|
|
112 return {from: from, secs: 60*5};
|
|
113 },
|
|
114 observers: [
|
|
115 'onResponse(response)'
|
|
116 ],
|
|
117 ready: function() {
|
|
118 this.scopeSubtree(this.$.chart_container, true);
|
|
119
|
|
120 },
|
|
121 onClick: function() {
|
|
122 this.$.get.generateRequest();
|
|
123 },
|
|
124 redraw: function() {
|
|
125 var serieses = [];
|
|
126
|
|
127 for (var addr of Object.keys(this.points)) {
|
|
128 var pts = this.points[addr];
|
|
129 var transformed = pts.map(function(p) {
|
|
130 return {x: p[0] + this.startTime,
|
|
131 y: .5 * (p[1]-(-120)) / (-60+120)};
|
|
132 }.bind(this));
|
|
133 serieses.push({
|
|
134 name: addr,
|
|
135 data: transformed,
|
|
136 color: colorForAddr(addr),
|
|
137 });
|
|
138 }
|
|
139
|
|
140 this.$.chart.innerHTML = '';
|
|
141 var graph = new Rickshaw.Graph( {
|
|
142 element: this.$.chart,
|
|
143 width: 400,
|
|
144 height: 60,
|
|
145 renderer: 'line',
|
|
146 series: serieses,
|
|
147 } );
|
|
148
|
|
149 graph.render();
|
|
150
|
|
151 var hoverDetail = new Rickshaw.Graph.HoverDetail( {
|
|
152 graph: graph
|
|
153 } );
|
|
154
|
|
155 var axes = new Rickshaw.Graph.Axis.Time( {
|
|
156 graph: graph
|
|
157 } );
|
|
158 axes.render();
|
|
159
|
|
160
|
|
161 var yAxis = new Rickshaw.Graph.Axis.Y( {
|
|
162 graph: graph,
|
|
163 tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
|
|
164 } );
|
|
165
|
|
166 yAxis.render();
|
|
167
|
|
168 this.graph = graph;
|
|
169 },
|
|
170 onResponse: function(response) {
|
|
171 this.points = response.points;
|
|
172 this.startTime = response.startTime;
|
|
173 this.redraw();
|
|
174
|
|
175 }
|
|
176 });
|
|
177 });
|
|
178 </script>
|
|
179 </dom-module>
|
|
180
|
|
181 <dom-module id="beacon-device-graph">
|
|
182 <link rel="import" type="css" href="/lib/rickshaw/90852d8/src/css/graph.css">
|
|
183 <link rel="import" type="css" href="/lib/rickshaw/90852d8/src/css/detail.css">
|
|
184 <link rel="import" type="css" href="/lib/rickshaw/90852d8/src/css/legend.css">
|
|
185 <template>
|
|
186
|
|
187 <div>{{addr}} (continuous update)</div>
|
|
188 <div id="chart_container" on-click="onClick">
|
|
189 <div id="chart"></div>
|
|
190 </div>
|
|
191 <div>{{latest}}</div>
|
|
192 </template>
|
|
193 <script src="/lib/jquery-2.0.3.min.js"></script>
|
|
194 <script src="/lib/rickshaw/90852d8/vendor/d3.min.js"></script>
|
|
195 <script src="/lib/rickshaw/90852d8/rickshaw.min.js"></script>
|
|
196 <script>
|
|
197 HTMLImports.whenReady(function () {
|
|
198 Polymer({
|
|
199 is: "beacon-device-graph",
|
|
200 properties: {
|
|
201 addr: {type: String, notify: true},
|
|
202 params: {computed: '_params(addr)'}
|
|
203
|
|
204 },
|
|
205 _params: function(from) {
|
|
206 return {addr: this.addr};
|
|
207 },
|
|
208 observers: [
|
|
209 'onResponse(response)',
|
|
210 'startEvents(addr)',
|
|
211 ],
|
|
212 ready: function() {
|
|
213 this.scopeSubtree(this.$.chart_container, true);
|
|
214 },
|
|
215 startEvents: function(addr) {
|
|
216 if (this.events) {
|
|
217 this.events.close();
|
|
218 }
|
|
219 if (addr) {
|
|
220 console.log('new es', addr);
|
|
221 this.events = new EventSource('points?addr=' + encodeURIComponent(addr));
|
|
222 this.events.addEventListener('message', function(e) {
|
|
223 var body = JSON.parse(e.data);
|
|
224 this.points = body.points;
|
|
225 this.startTime = body.startTime;
|
|
226 this.redraw();
|
|
227 }.bind(this));
|
|
228 }
|
|
229 },
|
|
230
|
|
231 onClick: function() {
|
|
232 this.$.get.generateRequest();
|
|
233 },
|
|
234 redraw: function() {
|
|
235 var serieses = [];
|
|
236 var latestForSensor = {};
|
|
237 for (var row of this.points) {
|
|
238 var transformed = row.points.map(function(p) {
|
|
239 return {x: p[0] + this.startTime,
|
|
240 y: .5 * (p[1]-(-120)) / (-60+120)};
|
|
241 }.bind(this));
|
|
242 serieses.push({
|
|
243 name: row.from,
|
|
244 data: transformed,
|
|
245 color: colorForSensor(row.from),
|
|
246 });
|
|
247 latestForSensor[row.from] = row.points[row.points.length - 1][1];
|
|
248 }
|
|
249 this.latest = JSON.stringify(latestForSensor);
|
|
250 this.$.chart.innerHTML = '';
|
|
251 var graph = new Rickshaw.Graph( {
|
|
252 element: this.$.chart,
|
|
253 width: 640,
|
|
254 height: 300,
|
|
255 renderer: 'line',
|
|
256 series: serieses,
|
|
257 } );
|
|
258
|
|
259 graph.render();
|
|
260
|
|
261 var hoverDetail = new Rickshaw.Graph.HoverDetail( {
|
|
262 graph: graph
|
|
263 } );
|
|
264
|
|
265 var axes = new Rickshaw.Graph.Axis.Time( {
|
|
266 graph: graph
|
|
267 } );
|
|
268 axes.render();
|
|
269
|
|
270 var yAxis = new Rickshaw.Graph.Axis.Y( {
|
|
271 graph: graph,
|
|
272 tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
|
|
273 } );
|
|
274
|
|
275 yAxis.render();
|
|
276
|
|
277 this.graph = graph;
|
|
278 },
|
|
279
|
|
280 });
|
|
281 });
|
|
282 </script>
|
|
283 </dom-module>
|
|
284
|
|
285 <dom-module id="beacon-page">
|
|
286 <template>
|
|
287 <link rel="import" href="/lib/polymer/1.0.9/iron-flex-layout/iron-flex-layout-classes.html">
|
|
288 <style is="custom-style" include="iron-flex iron-flex-alignment iron-flex-factors"></style>
|
|
289 <style>
|
|
290 </style>
|
|
291 <paper-header-panel class="flex">
|
|
292 <div class="paper-header">beacon map</div>
|
|
293 <div class="layout horizontal">
|
|
294
|
|
295 <beacon-devices style="height: 500px"
|
310
|
296 class="layout justified flex-2"
|
291
|
297 selected="{{sel}}"></beacon-devices>
|
|
298 <div class="layout vertical" style="flex-grow: 2">
|
|
299 <house-model position-estimates="{{positionEstimates}}" beacons="{{beacons}}"></house-model>
|
|
300 <beacon-device-graph addr="{{sel.addr}}"></beacon-device-graph>
|
|
301 <div>
|
|
302 save white levels:
|
|
303 <iron-ajax url="save"
|
|
304 method="POST"
|
|
305 id="save"
|
|
306 verbose="true"
|
|
307 handle-as="text"
|
|
308 last-response="{{saveResponse}}"></iron-ajax>
|
|
309 <button on-click="onSave" style="padding: 20px">save</button> {{saveResponse}}
|
|
310 </div>
|
|
311
|
|
312 <div id="sensors">
|
|
313 <iron-ajax url="sensors"
|
|
314 auto
|
|
315 last-response="{{sensorsResponse}}"></iron-ajax>
|
|
316 <template is="dom-repeat" items="{{sensorsResponse.sensors}}">
|
310
|
317 <div>sensor detail {{item}}</div>
|
291
|
318 </template>
|
|
319 </div>
|
310
|
320
|
291
|
321 </div>
|
|
322 </div>
|
|
323 </paper-header-panel>
|
|
324 beacon page gets this
|
|
325
|
|
326 </template>
|
|
327 <script>
|
|
328 HTMLImports.whenReady(function () {
|
|
329 Polymer({
|
|
330 is: "beacon-page",
|
|
331 properties: {
|
|
332 sel: { type: Object, notify: true },
|
|
333 saveResponse: { type: String, notify: true},
|
|
334 },
|
|
335 onSave: function() {
|
|
336 this.$.save.generateRequest();
|
|
337 },
|
|
338 ready: function() {
|
|
339
|
|
340 this.sel = {addr: '00:ea:23:23:c6:c4'};
|
|
341
|
|
342 var events = new EventSource('positionEstimates?addr=' +
|
|
343 encodeURIComponent('00:ea:23:23:c6:c4'));
|
|
344 events.addEventListener('message', function(e) {
|
|
345 var body = JSON.parse(e.data);
|
|
346 this.positionEstimates = body.nearest;
|
|
347 this.set('beacons', [{label: 'apollo', pos: body.weightedCoord}]);
|
|
348 }.bind(this));
|
|
349 },
|
|
350 });
|
|
351 });
|
|
352
|
|
353 </script>
|
|
354 </dom-module>
|
|
355
|
|
356 <style>
|
|
357 .paper-header {
|
|
358 background-color: var(--paper-light-blue-500);
|
|
359 }
|
|
360 </style>
|
|
361
|
|
362 <beacon-page class="layout fit"></beacon-page>
|
|
363 </body>
|
|
364 </html>
|