Implementation of level and color data types

This commit is contained in:
Ziver Koc 2023-05-06 01:11:10 +02:00
parent a89b418350
commit 234125bc35
30 changed files with 664 additions and 113 deletions

View file

@ -28,7 +28,7 @@
<td>
<form method="POST">
<input type="hidden" name="action" value="modify">
<input type="hidden" name="action_id" value="{{event.getId()}}">
<input type="hidden" name="action-id" value="{{event.getId()}}">
<div class="btn-toolbar pull-left">
<input class="toggle-switch" type="checkbox" name="enabled"

View file

@ -13,26 +13,6 @@
<th class="col-md-2">Last Update</th>
<th class="col-md-2 text-right">Actions</th>
</thead>
{{#events}}
<tr data-device-id="{{.getId()}}">
<td><a href="?id={{.getId()}}">{{.getName()}}</a></td>
<td>{{.getDeviceConfig().getClass().getSimpleName()}}</td>
<td>{{.getDeviceData()}}</td>
<td class="timestamp">{{.getDeviceData().getTimestamp()}}</td>
<td>
<form method="POST">
<input type="hidden" name="action" value="modify">
<input type="hidden" name="action_id" value="{{.getId()}}">
<div class="btn-toolbar pull-right">
<input class="toggle-switch" type="checkbox" name="enabled"
data-on-color="danger"
{{#.getDeviceData().getData()}}checked{{/.getDeviceData().getData()}} >
</div>
</form>
</td>
</tr>
{{/events}}
</table>
</div>
</div>
@ -45,24 +25,70 @@
});
});
updateDevices();
// Auto update
setInterval(function() {
updateDevices();
}, 3000);
function updateDevices() {
fetch('/api/event')
.then(response => response.json())
.then(data => {
var table = document.getElementById('event-device-table');
for (const row of table.rows) {
var dataItem = data.find(item => item.id == row.dataset.deviceId);
for (const deviceData of data) {
var row = Array.from(table.rows).find(row => row.dataset.deviceId == deviceData.id);
if (dataItem) {
row.cells[2].innerHTML = dataItem.data?.valueStr;
row.cells[3].innerHTML = dataItem.data?.timestamp;
$(row.cells[3]).relTimestamp();
$(row.cells[4].querySelector('[type="checkbox"]')).bootstrapSwitch('state', dataItem.data?.value === 1, true);
if (!row) {
row = table.insertRow();
row.insertCell(0);
row.insertCell(1);
row.insertCell(2);
row.insertCell(3);
row.insertCell(4);
}
// Update Cells
row.dataset.deviceId = deviceData.id
row.cells[0].innerHTML = deviceData.name;
row.cells[1].innerHTML = deviceData.config?.typeConfig;
row.cells[2].innerHTML = deviceData.data?.valueStr;
row.cells[3].innerHTML = deviceData.data?.timestamp;
$(row.cells[3]).relTimestamp();
var actionHtml = "";
switch (deviceData.config?.typeData) {
case "ColorEventData":
actionHtml =
'<input type="hidden" name="type" value="color">' +
'<input type="color" name="data" onchange="this.form.submit()" value="' + deviceData.data?.valueStr + '">';
break;
case "LevelEventData":
actionHtml =
'<input type="hidden" name="type" value="level">' +
'<input type="range" name="data" min="0" max="100" step="10" onchange="this.form.submit()" value="' + (deviceData.data?.value * 100) + '">';
break;
case "OnOffEventData":
actionHtml =
'<input type="hidden" name="type" value="on-off">' +
'<input class="toggle-switch" type="checkbox" name="data" data-on-color="danger" onchange="this.form.submit()" ' + (deviceData.data?.valueStr=="ON" ? "checked" : "") + '>';
//$(row.cells[4].querySelector('[type="checkbox"]')).bootstrapSwitch('state', deviceData.data?.value === 1, true);
break;
}
row.cells[4].innerHTML = '<form method="POST">' +
'<input type="hidden" name="action" value="modify">' +
'<input type="hidden" name="action-id" value="' + deviceData.id + '">' +
'<div class="btn-toolbar pull-right">' + actionHtml + '</div>';
$(".toggle-switch").bootstrapSwitch({inverse: true, size: "mini"});
}
});
}, 3000);
}
</script>

View file

@ -58,6 +58,7 @@ function updateAlerts() {
.then(data => {
data.forEach(alert => {
var alertElement = $("#alert-id-" + alert.id);
if (alertElement.length <= 0) {
alertElement = $(alertTemplate[alert.level]);
$("#" + alertDivId).append(alertElement);

View file

@ -41,11 +41,11 @@
var table = document.getElementById('sensor-device-table');
for (const row of table.rows) {
var dataItem = data.find(item => item.id == row.dataset.deviceId);
var deviceData = data.find(item => item.id == row.dataset.deviceId);
if (dataItem) {
row.cells[2].innerHTML = dataItem.data?.valueStr;
row.cells[3].innerHTML = dataItem.data?.timestamp;
if (deviceData) {
row.cells[2].innerHTML = deviceData.data?.valueStr;
row.cells[3].innerHTML = deviceData.data?.timestamp;
$(row.cells[3]).relTimestamp();
}

View file

@ -2,8 +2,11 @@ package se.hal.page;
import se.hal.EventControllerManager;
import se.hal.HalContext;
import se.hal.intf.HalEventData;
import se.hal.intf.HalWebPage;
import se.hal.struct.Event;
import se.hal.struct.devicedata.ColorEventData;
import se.hal.struct.devicedata.LevelEventData;
import se.hal.struct.devicedata.OnOffEventData;
import se.hal.util.DeviceNameComparator;
import se.hal.util.HistoryDataListSqlResult;
@ -41,24 +44,40 @@ public class EventOverviewWebPage extends HalWebPage {
DBConnection db = HalContext.getDB();
if (request.containsKey("action")) {
int id = (ObjectUtil.isEmpty(request.get("action_id")) ? -1 : Integer.parseInt(request.get("action_id")));
if ("modify".equals(request.get("action"))) {
int id = (ObjectUtil.isEmpty(request.get("action-id")) ? -1 : Integer.parseInt(request.get("action-id")));
// change event data
OnOffEventData eventData = new OnOffEventData();
if (request.containsKey("enabled") && "on".equals(request.get("enabled")))
eventData.setOn();
else
eventData.setOff();
HalEventData eventData = null;
logger.info("Modifying Event(" + id + ") state: " + eventData.toString());
Event event = Event.getEvent(db, id);
EventControllerManager.getInstance().send(event, eventData);
switch (request.get("type")) {
case "level":
eventData = new LevelEventData(Integer.parseInt(request.get("data")) / 100.0, 0);
break;
case "color":
eventData = new ColorEventData(request.get("data"), 0);
break;
case "on-off":
eventData = new OnOffEventData("on".equals(request.get("data")), 0);
break;
}
if (eventData != null) {
logger.info("Modifying Event(" + id + ") state: " + eventData.toString());
Event event = Event.getEvent(db, id);
EventControllerManager.getInstance().send(event, eventData);
} else {
logger.warning("Unable to process event change request, data type most likely not supported: " + request.get("type"));
}
}
int id = (ObjectUtil.isEmpty(request.get("id")) ? -1 : Integer.parseInt(request.get("id")));
// Save new input
// --------------------------------------
// Detailed page
// --------------------------------------
if (id >= 0) {
Event event = Event.getEvent(db, id);
@ -74,6 +93,9 @@ public class EventOverviewWebPage extends HalWebPage {
tmpl.set("history", history);
return tmpl;
}
// --------------------------------------
// Overview page
// --------------------------------------
else {
List<Event> events = Event.getLocalEvents(db);
Collections.sort(events, DeviceNameComparator.getInstance());

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.struct.devicedata;
import se.hal.intf.HalEventData;
import zutil.ColorUtil;
import zutil.converter.Converter;
import java.awt.*;
import java.awt.color.ColorSpace;
/**
* Represents a color based event.
*/
public class ColorEventData extends HalEventData {
private static final ColorSpace cieXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
private int red;
private int green;
private int blue;
public ColorEventData() { }
/**
* Create object based on sRGB.
*/
public ColorEventData(String hex, long timestamp) {
int[] rgb = ColorUtil.getRgbFromHexString(hex);
this.red = rgb[0];
this.green = rgb[1];
this.blue = rgb[2];
this.setTimestamp(timestamp);
}
/**
* Create object based on sRGB.
*/
public ColorEventData(int red, int green, int blue, long timestamp) {
this.red = red;
this.green = green;
this.blue = blue;
this.setTimestamp(timestamp);
}
/**
* Create object based on CIE XYZ.
*/
public static ColorEventData createFromCieXYZ(float x, float y, float z, long timestamp) {
float[] rgb = cieXYZ.toRGB(new float[]{x, y, z});
return new ColorEventData((int) rgb[0], (int) rgb[0], (int) rgb[0], timestamp);
}
/**
* Create object based on HLS.
*/
public static ColorEventData createFromHLS(float h, float l, float s, long timestamp) {
return null;
}
public int getRed() {
return red;
}
public int getGreen() {
return green;
}
public int getBlue() {
return blue;
}
public double getHue() {
return ColorUtil.getHue(red, green, blue);
}
public double getSaturation() {
return ColorUtil.getSaturation(red, green, blue);
}
public double getLightness() {
return ColorUtil.getLightness(red, green, blue);
}
public float[] getCieXYZ() {
return cieXYZ.fromRGB(new float[]{red, green, blue});
}
public String getHex() {
return ColorUtil.getHexString(red, green, blue);
}
@Override
public String toString(){
return "Red: " + red + ", Green: " + green + ", Blue: " + blue;
}
// ----------------------------------------
// Storage methods
// ----------------------------------------
@Override
public double getData() {
int rgb = 0;
rgb |= 0xFF0000 & (red << 16);
rgb |= 0x00FF00 & (green << 8);
rgb |= 0x0000FF & (blue);
return rgb;
}
@Override
public void setData(double data) {
int rgb = (int) data;
red = 0xFF & (rgb >> 16);
green = 0xFF & (rgb >> 8);
blue = 0xFF & (rgb);
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.struct.devicedata;
import se.hal.intf.HalEventData;
/**
* Represents a percentage based level event.
*/
public class LevelEventData extends HalEventData {
private double percent;
public LevelEventData() { }
public LevelEventData(double percent, long timestamp) {
setData(percent);
this.setTimestamp(timestamp);
}
@Override
public String toString(){
return Math.floor(percent * 100) + "%";
}
// ----------------------------------------
// Storage methods
// ----------------------------------------
@Override
public double getData() {
return percent;
}
@Override
public void setData(double data) {
this.percent = data;
}
}

View file

@ -24,7 +24,9 @@ package se.hal.struct.devicedata;
import se.hal.intf.HalEventData;
/**
* Represents a 'ON' or 'OFF' state based event.
*/
public class OnOffEventData extends HalEventData {
private boolean isOn;

View file

@ -25,6 +25,9 @@ package se.hal.struct.devicedata;
import se.hal.intf.HalEventData;
/**
* Represents an 'Opened' or 'Closed' state based event.
*/
public class OpenClosedEventData extends HalEventData {
private boolean isOpen;

View file

@ -0,0 +1,46 @@
package se.hal.struct.devicedata;
import org.junit.Test;
import static org.junit.Assert.*;
public class ColorEventDataTest {
@Test
public void getData() {
assertEquals(0.0, new ColorEventData(0, 0, 0, 0).getData(), 0);
assertEquals(6553600.0, new ColorEventData(100, 0, 0, 0).getData(), 0);
assertEquals(25600.0, new ColorEventData(0, 100, 0, 0).getData(), 0);
assertEquals(100.0, new ColorEventData(0, 0, 100, 0).getData(), 0);
assertEquals(16777215.0, new ColorEventData(255, 255, 255, 0).getData(), 0);
}
@Test
public void setData() {
ColorEventData colorData = new ColorEventData();
colorData.setData(0);
assertEquals(0, colorData.getRed());
assertEquals(0, colorData.getGreen());
assertEquals(0, colorData.getBlue());
colorData.setData(6553600.0);
assertEquals(100, colorData.getRed());
assertEquals(0, colorData.getGreen());
assertEquals(0, colorData.getBlue());
colorData.setData(25600.0);
assertEquals(0, colorData.getRed());
assertEquals(100, colorData.getGreen());
assertEquals(0, colorData.getBlue());
colorData.setData(100.0);
assertEquals(0, colorData.getRed());
assertEquals(0, colorData.getGreen());
assertEquals(100, colorData.getBlue());
colorData.setData(16777215.0);
assertEquals(255, colorData.getRed());
assertEquals(255, colorData.getGreen());
assertEquals(255, colorData.getBlue());
}
}