diff --git a/README.md b/README.md index c225c84..8f907ec 100644 --- a/README.md +++ b/README.md @@ -1 +1,12 @@ -# alpdesk-automationservice \ No newline at end of file +# Alpdesk-Automationservice +Java-Service for Automation (like Homeautomation) + +To start on RaspberryPi with Shell-Script: + +#!/bin/sh +cd /home/pi/xhomeautomation +mount /dev/sda1 /media/xhomeautomationusb/ +sudo mount | grep sda1 +java -Xdebug -Xrunjdwp:transport=dt_socket,address=5001,server=y,suspend=n -jar XHomeautomation-Service-Deploy.jar /home/pi/xhomeautomation/homeautomation.properties --spring.profiles.active=prod --spring.datasource.url=jdbc:h2:file:/media/xhomeautomationusb/xhomeautomationdb_prod + + diff --git a/nb-configuration.xml b/nb-configuration.xml new file mode 100644 index 0000000..5b0f1f8 --- /dev/null +++ b/nb-configuration.xml @@ -0,0 +1,18 @@ + + + + + + true + + diff --git a/nbactions-build_jdk11.xml b/nbactions-build_jdk11.xml new file mode 100644 index 0000000..bcab3ff --- /dev/null +++ b/nbactions-build_jdk11.xml @@ -0,0 +1,49 @@ + + + + run + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.5.0:exec + + + -Dspring.profiles.active=dev -classpath %classpath x.main.XHomeautomationMain homeautomationtest.properties + java + ./src/main/resources/data + + + + debug + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.5.0:exec + + + -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} -Dspring.profiles.active=dev -classpath %classpath x.main.XHomeautomationMain homeautomationtest.properties + java + true + ./src/main/resources/data + + + + profile + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.5.0:exec + + + -Dspring.profiles.active=dev -classpath %classpath x.main.XHomeautomationMain homeautomationtest.properties + java + ./src/main/resources/data + + + diff --git a/nbactions.xml b/nbactions.xml new file mode 100644 index 0000000..bcab3ff --- /dev/null +++ b/nbactions.xml @@ -0,0 +1,49 @@ + + + + run + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.5.0:exec + + + -Dspring.profiles.active=dev -classpath %classpath x.main.XHomeautomationMain homeautomationtest.properties + java + ./src/main/resources/data + + + + debug + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.5.0:exec + + + -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} -Dspring.profiles.active=dev -classpath %classpath x.main.XHomeautomationMain homeautomationtest.properties + java + true + ./src/main/resources/data + + + + profile + + jar + + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.5.0:exec + + + -Dspring.profiles.active=dev -classpath %classpath x.main.XHomeautomationMain homeautomationtest.properties + java + ./src/main/resources/data + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0d775cf --- /dev/null +++ b/pom.xml @@ -0,0 +1,153 @@ + + + + 4.0.0 + x + Alpdesk-Automationservice + 2.2.0 + + jar + + + org.springframework.boot + spring-boot-starter-parent + 2.3.1.RELEASE + + + + + net.wimpi + jamod + 1.2 + + + commons-codec + commons-codec + 1.14 + + + com.typesafe.akka + akka-actor_2.13 + 2.6.6 + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + runtime + 1.4.200 + + + + + + build_jdk8 + + true + 1.8 + 1.8 + x.main.XHomeautomationMain + + + + + src/main/resources + + **/* + + + + + + org.springframework.boot + spring-boot-maven-plugin + + ${project.artifactId}-Deploy + true + true + x.main.XHomeautomationMain + + + + + repackage + + + + + + + + + build_jdk11 + + true + 11 + 11 + x.main.XHomeautomationMain + + + + + src/main/resources + + **/* + + + + + + org.springframework.boot + spring-boot-maven-plugin + + ${project.artifactId}-Deploy + true + true + x.main.XHomeautomationMain + + + + + repackage + + + + + + + + + + + UTF-8 + + + + + + src/main/java + + **/*.properties + **/*.txt + **/*.png + **/*.gif + **/*.GIF + **/*.dll + **/*.theme + + + + + Alpdesk-Automationservice + \ No newline at end of file diff --git a/src/main/java/x/DeviceEffects/BaseEffect.java b/src/main/java/x/DeviceEffects/BaseEffect.java new file mode 100755 index 0000000..b943084 --- /dev/null +++ b/src/main/java/x/DeviceEffects/BaseEffect.java @@ -0,0 +1,28 @@ +package x.DeviceEffects; + +public class BaseEffect { + + long startTime = 0; + int delay = 0; + public boolean idle = true; + public String[] outputs = null; + + public BaseEffect(int delay, String outputs) { + this.delay = delay; + this.outputs = outputs.split(";"); + } + + synchronized public void initEffect(long startTime) { + this.idle = false; + this.startTime = startTime; + } + + synchronized public void resetEffect() { + this.idle = true; + this.startTime = 0; + } + + synchronized public void trigger(boolean value) { + } + +} diff --git a/src/main/java/x/DeviceEffects/BridgeEffect.java b/src/main/java/x/DeviceEffects/BridgeEffect.java new file mode 100755 index 0000000..873538d --- /dev/null +++ b/src/main/java/x/DeviceEffects/BridgeEffect.java @@ -0,0 +1,43 @@ +package x.DeviceEffects; + +import x.DeviceUtils.DeviceListUtils; +import x.Devices.OutputDevice; + +public class BridgeEffect extends BaseEffect { + + boolean bridgeValid = false; + + public BridgeEffect(int delay, String outputs) { + super(delay, outputs); + } + + @Override + synchronized public void initEffect(long startTime) { + super.initEffect(startTime); + bridgeValid = false; + } + + @Override + synchronized public void trigger(boolean value) { + if (idle == false) { + if (value == true && (System.currentTimeMillis() - startTime) >= delay) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() != value) { + o.sendMessage(value); + } + } + bridgeValid = true; + } else if (bridgeValid && value == false) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() != value) { + o.sendMessage(value); + } + } + bridgeValid = false; + idle = true; + } + } + } +} diff --git a/src/main/java/x/DeviceEffects/DimmerEffect.java b/src/main/java/x/DeviceEffects/DimmerEffect.java new file mode 100755 index 0000000..3c73149 --- /dev/null +++ b/src/main/java/x/DeviceEffects/DimmerEffect.java @@ -0,0 +1,43 @@ +package x.DeviceEffects; + +import x.DeviceUtils.DeviceListUtils; +import x.Devices.DimmerDevice; + +public class DimmerEffect extends BaseEffect { + + boolean bridgeValid = false; + + public DimmerEffect(int delay, String outputs) { + super(delay, outputs); + } + + @Override + synchronized public void initEffect(long startTime) { + super.initEffect(startTime); + bridgeValid = false; + } + + @Override + synchronized public void trigger(boolean value) { + if (idle == false) { + if (value == true && (System.currentTimeMillis() - startTime) >= delay) { + for (String output : outputs) { + DimmerDevice o = DeviceListUtils.getInstance().getDimmerById(output); + if (o.isValue() != value) { + o.sendMessage(value); + } + } + bridgeValid = true; + } else if (bridgeValid && value == false) { + for (String output : outputs) { + DimmerDevice o = DeviceListUtils.getInstance().getDimmerById(output); + if (o.isValue() != value) { + o.sendMessage(value); + } + } + bridgeValid = false; + idle = true; + } + } + } +} diff --git a/src/main/java/x/DeviceEffects/OffEffect.java b/src/main/java/x/DeviceEffects/OffEffect.java new file mode 100755 index 0000000..cc35f64 --- /dev/null +++ b/src/main/java/x/DeviceEffects/OffEffect.java @@ -0,0 +1,26 @@ +package x.DeviceEffects; + +import x.DeviceUtils.DeviceListUtils; +import x.Devices.OutputDevice; + +public class OffEffect extends BaseEffect { + + public OffEffect(int delay, String outputs) { + super(delay, outputs); + } + + @Override + synchronized public void trigger(boolean value) { + if (idle == false) { + if (value == true && (System.currentTimeMillis() - startTime) >= delay) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() == true) { + o.sendMessage(false); + } + } + idle = true; + } + } + } +} diff --git a/src/main/java/x/DeviceEffects/OnEffect.java b/src/main/java/x/DeviceEffects/OnEffect.java new file mode 100755 index 0000000..fbc14ef --- /dev/null +++ b/src/main/java/x/DeviceEffects/OnEffect.java @@ -0,0 +1,59 @@ +package x.DeviceEffects; + +import x.DeviceUtils.DeviceListUtils; +import x.Devices.OutputDevice; + +public class OnEffect extends BaseEffect { + + int duration = 0; + long startTimeDuration = 0; + boolean durationValid = false; + + public OnEffect(int duration, int delay, String outputs) { + super(delay, outputs); + this.duration = duration; + } + + @Override + synchronized public void initEffect(long startTime) { + super.initEffect(startTime); + durationValid = false; + startTimeDuration = 0; + } + + @Override + synchronized public void trigger(boolean value) { + if (idle == false) { + if (value == true && (System.currentTimeMillis() - startTime) >= delay) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() == false) { + o.sendMessage(true); + } + } + if (duration != 0) { + if (startTimeDuration == 0) { + startTimeDuration = System.currentTimeMillis(); + durationValid = true; + } + } else { + idle = true; + } + } + if (durationValid) { + if ((System.currentTimeMillis() - startTimeDuration) >= duration) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() == true) { + o.sendMessage(false); + } + } + startTimeDuration = 0; + durationValid = false; + idle = true; + } + } + } + } + +} diff --git a/src/main/java/x/DeviceEffects/ToggleEffect.java b/src/main/java/x/DeviceEffects/ToggleEffect.java new file mode 100755 index 0000000..46f281f --- /dev/null +++ b/src/main/java/x/DeviceEffects/ToggleEffect.java @@ -0,0 +1,28 @@ +package x.DeviceEffects; + +import x.DeviceUtils.DeviceListUtils; +import x.Devices.OutputDevice; + +public class ToggleEffect extends BaseEffect { + + public ToggleEffect(int delay, String outputs) { + super(delay, outputs); + } + + @Override + synchronized public void trigger(boolean value) { + if (idle == false) { + if (value == true && (System.currentTimeMillis() - startTime) >= delay) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() == false) { + o.sendMessage(true); + } else { + o.sendMessage(false); + } + } + idle = true; + } + } + } +} diff --git a/src/main/java/x/DeviceUtils/DateUtils.java b/src/main/java/x/DeviceUtils/DateUtils.java new file mode 100755 index 0000000..4ed670f --- /dev/null +++ b/src/main/java/x/DeviceUtils/DateUtils.java @@ -0,0 +1,80 @@ +package x.DeviceUtils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +public class DateUtils { + + SimpleDateFormat dateFormat = null; + SimpleDateFormat dateFormatd = null; + SimpleDateFormat dateFormatt = null; + SimpleDateFormat dateFormatts = null; + Calendar now = null; + Calendar xmlDateStart = null; + Calendar xmlDateStop = null; + Calendar dateTemp = null; + + public DateUtils() { + this.dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); + this.dateFormatd = new SimpleDateFormat("dd.MM"); + this.dateFormatt = new SimpleDateFormat("HH:mm:ss"); + this.dateFormatts = new SimpleDateFormat("HH:mm"); + } + + public boolean checkStatus(String timeStart, String timeStop) { + try { + if (!timeStart.equals("") && !timeStop.equals("")) { + now = Calendar.getInstance(); + xmlDateStart = Calendar.getInstance(); + xmlDateStop = Calendar.getInstance(); + xmlDateStart.setTime(dateFormat.parse(dateFormatd.format(now.getTime()) + "." + now.get(Calendar.YEAR) + " " + timeStart)); + xmlDateStop.setTime(dateFormat.parse(dateFormatd.format(now.getTime()) + "." + now.get(Calendar.YEAR) + " " + timeStop)); + + if (xmlDateStop.compareTo(xmlDateStart) < 0) { + if (now.compareTo(xmlDateStop) < 0) { + now.add(Calendar.DATE, 1); + } + xmlDateStop.add(Calendar.DATE, 1); + } + + return now.after(xmlDateStart) && now.before(xmlDateStop); + } else { + return false; + } + } catch (ParseException ex) { + return false; + } + } + + public String[] getCompleteDateString() { + String[] data = new String[2]; + if (xmlDateStart != null) { + data[0] = dateFormat.format(xmlDateStart.getTime()); + } + if (xmlDateStop != null) { + data[1] = dateFormat.format(xmlDateStop.getTime()); + } + return data; + } + + public String increaseTime(String currentTime, int s) { + String returnvalue = currentTime; + Calendar temp = Calendar.getInstance(); + try { + temp.setTime(dateFormat.parse(dateFormatd.format(Calendar.getInstance().getTime()) + "." + Calendar.getInstance().get(Calendar.YEAR) + " " + currentTime)); + temp.add(Calendar.SECOND, s); + returnvalue = dateFormatt.format(temp.getTime()); + } catch (ParseException ex) { + } + return returnvalue; + } + + public String getHourMinuteString() { + return dateFormatts.format(Calendar.getInstance().getTime()); + } + + public String getTimeStampString() { + return dateFormat.format(Calendar.getInstance().getTime()); + } +} diff --git a/src/main/java/x/DeviceUtils/DeviceListUtils.java b/src/main/java/x/DeviceUtils/DeviceListUtils.java new file mode 100755 index 0000000..2a24d20 --- /dev/null +++ b/src/main/java/x/DeviceUtils/DeviceListUtils.java @@ -0,0 +1,322 @@ +package x.DeviceUtils; + +import java.util.ArrayList; +import java.util.List; +import x.Devices.BaseDevice; +import x.Devices.DHT22Device; +import x.Devices.DimmerDevice; +import x.Devices.HeatingPumpDevice; +import x.Devices.InputDevice; +import x.Devices.OutputDevice; +import x.Devices.SceneDevice; +import x.Devices.SensorTemperatureDevice; +import x.Devices.ShadingDevice; +import x.Devices.TemperatureDevice; +import x.Devices.TimeDevice; +import x.Devices.VentilationDevice; + +public class DeviceListUtils { + + List deviceList = null; + private int maxInputBusaddress = -1; + private int maxOutputBusaddress = -1; + private int maxAnalogInBusaddress = -1; + + private static DeviceListUtils singleton = null; + + public DeviceListUtils() { + } + + public static DeviceListUtils getInstance() { + if (singleton == null) { + singleton = new DeviceListUtils(); + } + return singleton; + } + + public void setDeviceList(List deviceList) { + this.deviceList = deviceList; + } + + public List getDeviceList() { + return this.deviceList; + } + + public ArrayList getInputDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof InputDevice) { + InputDevice temp = (InputDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getOutputDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof OutputDevice) { + OutputDevice temp = (OutputDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getDimmerDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof DimmerDevice) { + DimmerDevice temp = (DimmerDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getDHT22DevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof DHT22Device) { + DHT22Device temp = (DHT22Device) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getHeatingPumpDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof HeatingPumpDevice) { + HeatingPumpDevice temp = (HeatingPumpDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getVentilationDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof VentilationDevice) { + VentilationDevice temp = (VentilationDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getShadingDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof ShadingDevice) { + ShadingDevice temp = (ShadingDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getTemperatureDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof TemperatureDevice) { + TemperatureDevice temp = (TemperatureDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getSensorTemperatureDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof SensorTemperatureDevice) { + SensorTemperatureDevice temp = (SensorTemperatureDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getSceneDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof SceneDevice) { + SceneDevice temp = (SceneDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public ArrayList getTimeDevicesByCategorie(String categorie) { + ArrayList list = new ArrayList<>(); + for (Object device : deviceList) { + if (device instanceof TimeDevice) { + TimeDevice temp = (TimeDevice) device; + if (temp.categorie.equals(categorie)) { + list.add(temp); + } + } + } + return list; + } + + public OutputDevice getOutputById(String id) { + OutputDevice d = null; + for (Object device : deviceList) { + if (device instanceof OutputDevice) { + if (((OutputDevice) device).id.equals(id)) { + d = (OutputDevice) device; + break; + } + } + } + return d; + } + + public TemperatureDevice getTemperatureDeviceById(String id) { + TemperatureDevice d = null; + for (Object device : deviceList) { + if (device instanceof TemperatureDevice) { + if (((TemperatureDevice) device).id.equals(id)) { + d = (TemperatureDevice) device; + break; + } + } + } + return d; + } + + public SensorTemperatureDevice getSensorTemperatureDeviceById(String id) { + SensorTemperatureDevice d = null; + for (Object device : deviceList) { + if (device instanceof SensorTemperatureDevice) { + if (((SensorTemperatureDevice) device).id.equals(id)) { + d = (SensorTemperatureDevice) device; + break; + } + } + } + return d; + } + + public DimmerDevice getDimmerById(String id) { + DimmerDevice d = null; + for (Object device : deviceList) { + if (device instanceof DimmerDevice) { + if (((DimmerDevice) device).id.equals(id)) { + d = (DimmerDevice) device; + break; + } + } + } + return d; + } + + public SceneDevice getSceneDeviceBySpeechIdent(String ident) { + SceneDevice d = null; + for (Object device : deviceList) { + if (device instanceof SceneDevice) { + if (((SceneDevice) device).speechIdent.equals(ident)) { + d = (SceneDevice) device; + break; + } + } + } + return d; + } + + public SceneDevice getSceneDeviceByHandle(int deviceHandle) { + SceneDevice d = null; + for (Object device : deviceList) { + if (device instanceof SceneDevice) { + if (((SceneDevice) device).deviceHandle == deviceHandle) { + d = (SceneDevice) device; + break; + } + } + } + return d; + } + + public int getMaxInputBusAddress() { + if (maxInputBusaddress == -1) { + for (Object device : deviceList) { + if (device instanceof InputDevice) { + if (((InputDevice) device).busAddress > maxInputBusaddress) { + maxInputBusaddress = ((InputDevice) device).busAddress; + } + } + } + } + return maxInputBusaddress; + } + + public int getMaxOutputBusAddress() { + if (maxOutputBusaddress == -1) { + for (Object device : deviceList) { + if (device instanceof OutputDevice) { + if (((OutputDevice) device).busAddress > maxOutputBusaddress) { + maxOutputBusaddress = ((OutputDevice) device).busAddress; + } + } + } + } + return maxOutputBusaddress; + } + + public int getMaxAnalogInBusAddress() { + if (maxAnalogInBusaddress == -1) { + for (Object device : deviceList) { + if (device instanceof TemperatureDevice) { + if (((TemperatureDevice) device).busAddress > maxAnalogInBusaddress) { + maxAnalogInBusaddress = ((TemperatureDevice) device).busAddress; + } + } else if (device instanceof SensorTemperatureDevice) { + if (((SensorTemperatureDevice) device).busAddress > maxAnalogInBusaddress) { + maxAnalogInBusaddress = ((SensorTemperatureDevice) device).busAddress; + } + } + } + } + return maxAnalogInBusaddress; + } + + public boolean anyTemperatureDeviceIsActive() { + boolean state = false; + for (Object device : deviceList) { + if (device instanceof TemperatureDevice) { + if (((TemperatureDevice) device).isOutputState()) { + state = true; + break; + } + } + } + return state; + } + +} diff --git a/src/main/java/x/DeviceUtils/InputParameter.java b/src/main/java/x/DeviceUtils/InputParameter.java new file mode 100755 index 0000000..10beb83 --- /dev/null +++ b/src/main/java/x/DeviceUtils/InputParameter.java @@ -0,0 +1,14 @@ +package x.DeviceUtils; + +import x.DeviceEffects.BaseEffect; + +public class InputParameter { + + public int type; + public BaseEffect effect = null; + + public InputParameter(int type, BaseEffect effect) { + this.type = type; + this.effect = effect; + } +} diff --git a/src/main/java/x/DeviceUtils/PropertyChangedEvent.java b/src/main/java/x/DeviceUtils/PropertyChangedEvent.java new file mode 100755 index 0000000..7531b27 --- /dev/null +++ b/src/main/java/x/DeviceUtils/PropertyChangedEvent.java @@ -0,0 +1,111 @@ +package x.DeviceUtils; + +import java.lang.reflect.Field; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.Devices.BaseDevice; +import x.utils.PropertyInfo; + +public class PropertyChangedEvent { + + private final Logger logger = LoggerFactory.getLogger(PropertyChangedEvent.class); + + private final Field f; + private final PropertyInfo p; + private final String value; + private final boolean byDatabase; + + public PropertyChangedEvent(Field f, PropertyInfo p, String value, boolean byDatabase) { + this.f = f; + this.p = p; + this.value = value; + this.byDatabase = byDatabase; + } + + public void execute(BaseDevice d) throws Exception { + if (byDatabase == true) { + executeByDatabase(d); + } else { + executeByValue(d); + } + } + + private void executeByDatabase(BaseDevice d) throws Exception { + switch (p.type()) { + case Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE: { + logger.debug("DB-PROPERTY TYPE_PROPERTIEINFO_CHANGESOLLVALUE <" + d.id + "><" + p.displayName() + "/" + p.handle() + "> => <" + value + ">"); + if (f.getType().isAssignableFrom(Integer.TYPE)) { + f.set(d, Integer.parseInt(value)); + } else if (f.getType().isAssignableFrom(String.class)) { + f.set(d, value); + } + break; + } + case Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION: { + if (f.getType().isAssignableFrom(Boolean.TYPE)) { + f.set(d, value.equalsIgnoreCase("ON")); + logger.debug("DB-PROPERTY TYPE_PROPERTIEINFO_TOGGLEACTIVATION <" + d.id + "><" + p.displayName() + "/" + p.handle() + "> => <" + value.equalsIgnoreCase("ON") + ">"); + } + break; + } + default: + break; + } + } + + private void executeByValue(BaseDevice d) throws Exception { + switch (p.type()) { + case Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE: { + logger.debug("PROPERTY TYPE_PROPERTIEINFO_CHANGESOLLVALUE <" + d.id + "><" + p.displayName() + "/" + p.handle() + "> => <" + value + ">"); + switch (value) { + case "+": { + if (f.getType().isAssignableFrom(Integer.TYPE)) { + int temp = (int) f.get(d); + temp++; + f.set(d, temp); + } else if (f.getType().isAssignableFrom(String.class)) { + String temp = (String) f.get(d); + if (p.propertyType() == Types.PROPERTYTYPE_TIME) { + if (!temp.equals("-")) { + DateUtils dUtils = new DateUtils(); + temp = dUtils.increaseTime(temp, p.stepValue()); + } + } + f.set(d, temp); + } + break; + } + case "-": { + if (f.getType().isAssignableFrom(Integer.TYPE)) { + int temp = (int) f.get(d); + temp--; + f.set(d, temp); + } else if (f.getType().isAssignableFrom(String.class)) { + String temp = (String) f.get(d); + if (p.propertyType() == Types.PROPERTYTYPE_TIME) { + if (!temp.equals("-")) { + DateUtils dUtils = new DateUtils(); + temp = dUtils.increaseTime(temp, -(p.stepValue())); + } + } + f.set(d, temp); + } + break; + } + } + break; + } + case Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION: { + if (f.getType().isAssignableFrom(Boolean.TYPE)) { + boolean temp = (boolean) f.get(d); + temp = temp != true; + f.set(d, temp); + logger.debug("PROPERTY TYPE_PROPERTIEINFO_TOGGLEACTIVATION <" + d.id + "><" + p.displayName() + "/" + p.handle() + "> => <" + temp + ">"); + } + break; + } + default: + break; + } + } +} diff --git a/src/main/java/x/DeviceUtils/RecorderItem.java b/src/main/java/x/DeviceUtils/RecorderItem.java new file mode 100755 index 0000000..140c1a2 --- /dev/null +++ b/src/main/java/x/DeviceUtils/RecorderItem.java @@ -0,0 +1,201 @@ +package x.DeviceUtils; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Queue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RecorderItem { + + private final Logger logger = LoggerFactory.getLogger(RecorderItem.class); + public static final int LEGENDTYPE_DATE_HOUT_MINUTE = 1; + + private int legendType = 0; + private int maxEntries = 20; + private long triggerTime = 1000; + private int timeLaps = 0; + private Queue itemsFifo = null; + private DateUtils du = null; + private boolean active = false; + private boolean firstRun = true; + + public RecorderItem(int maxEntries, int legendType, int timeLaps) { + this.maxEntries = maxEntries; + this.legendType = legendType; + this.timeLaps = timeLaps; + this.itemsFifo = new LinkedList<>(); + this.du = new DateUtils(); + this.triggerTime = System.currentTimeMillis(); + this.active = false; + } + + public int getMaxEntries() { + return maxEntries; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + if (active == false && itemsFifo != null && itemsFifo.size() > 0) { + itemsFifo.clear(); + } + } + + synchronized public ArrayList getAllItems() { + ArrayList items = new ArrayList<>(); + if (active && itemsFifo != null) { + Object[] tempItems = itemsFifo.toArray(); + if (tempItems != null) { + for (Object o : tempItems) { + if (o != null) { + ResultItem ri = new ResultItem(((Item) o).getX(), ((Item) o).getY(), ((Item) o).getTimeStamp(), ((Item) o).getDateTime()); + items.add(ri); + } + } + } + } + return items; + } + + synchronized public void clearItems() { + if (itemsFifo != null) { + itemsFifo.clear(); + } + } + + synchronized public void addItem(InsertItem temp, String parentId) { + if (active && checkValid()) { + if (itemsFifo != null) { + if (itemsFifo.size() >= maxEntries) { + itemsFifo.poll(); + } + Item i = new Item(temp.getY()); + itemsFifo.add(i); + logger.debug("RECORD <" + parentId + ">"); + } + } + } + + private boolean checkValid() { + boolean returnvalue = false; + if (System.currentTimeMillis() >= (triggerTime + timeLaps) || firstRun) { + returnvalue = true; + firstRun = false; + triggerTime = System.currentTimeMillis(); + } + return returnvalue; + } + + public static class InsertItem { + + private Object[] y = null; + + public InsertItem(Object[] y) { + this.y = y; + } + + public Object[] getY() { + return y; + } + } + + public static class ResultItem { + + private Object[] y = null; + private String x = ""; + private long timeStamp = 0; + private String dateTime = ""; + + public ResultItem(String x, Object[] y, long timeStamp, String dateTime) { + this.x = x; + this.y = y; + this.timeStamp = timeStamp; + this.dateTime = dateTime; + } + + public String getX() { + return x; + } + + public Object[] getY() { + return y; + } + + public int getYDimension() { + return y.length; + } + + public String getYasString() { + String value = ""; + if (y != null && y.length > 0) { + int counter = 0; + for (Object o : y) { + if (counter > 0) { + value += "|"; + } + String writeValue = ""; + if (o instanceof Boolean) { + writeValue = ((Boolean) o == true ? "1" : "0"); + } else { + writeValue = "" + o; + } + value += writeValue; + counter++; + } + } + return value; + } + + public long getTimeStamp() { + return timeStamp; + } + + public String getDateTime() { + return dateTime; + } + } + + private class Item { + + private Object[] y = null; + private String x = ""; + private long timeStamp = 0; + private String dateTime = ""; + + public Item(Object[] y) { + this.y = y; + switch (legendType) { + case RecorderItem.LEGENDTYPE_DATE_HOUT_MINUTE: { + x = du.getHourMinuteString(); + break; + } + default: + x = du.getHourMinuteString(); + break; + } + this.timeStamp = System.currentTimeMillis(); + this.dateTime = du.getTimeStampString(); + } + + public String getX() { + return x; + } + + public Object[] getY() { + return y; + } + + public long getTimeStamp() { + return timeStamp; + } + + public String getDateTime() { + return dateTime; + } + + } +} diff --git a/src/main/java/x/DeviceUtils/TransferObject.java b/src/main/java/x/DeviceUtils/TransferObject.java new file mode 100755 index 0000000..0bd4936 --- /dev/null +++ b/src/main/java/x/DeviceUtils/TransferObject.java @@ -0,0 +1,15 @@ +package x.DeviceUtils; + +public class TransferObject { + + private boolean valueBoolean = false; + + public TransferObject(boolean valueBoolean) { + this.valueBoolean = valueBoolean; + } + + public boolean isValueBoolean() { + return valueBoolean; + } + +} diff --git a/src/main/java/x/DeviceUtils/Types.java b/src/main/java/x/DeviceUtils/Types.java new file mode 100755 index 0000000..f2ef27b --- /dev/null +++ b/src/main/java/x/DeviceUtils/Types.java @@ -0,0 +1,28 @@ +package x.DeviceUtils; + +public class Types { + + public static final int TYPE_INPUT = 1000; + public static final int TYPE_OUTPUT = 2000; + public static final int TYPE_TEMPERATURE = 3000; + public static final int TYPE_SCENE = 4000; + public static final int TYPE_TIME = 5000; + public static final int TYPE_DIMMERDEVICE = 6000; + public static final int TYPE_SENSOR = 7000; + public static final int TYPE_DHT22 = 8000; + public static final int TYPE_HEATINGPUMP = 9000; + public static final int TYPE_VENTILATION = 10000; + public static final int TYPE_SHADING = 11000; + public static final int TYPE_TOGGLE = 100; + public static final int TYPE_ON = 101; + public static final int TYPE_OFF = 102; + public static final int TYPE_BRIDGE = 103; + public static final int TYPE_DIMMER = 104; + public static final int TYPE_PROPERTIEINFO_DEFAULT = 0; + public static final int TYPE_PROPERTIEINFO_CHANGESOLLVALUE = 1; + public static final int TYPE_PROPERTIEINFO_TOGGLEACTIVATION = 2; + public static final int TYPE_PROPERTIEINFO_INFO = 3; + public static final int PROPERTYTYPE_DEFAULT = 0; + public static final int PROPERTYTYPE_TIME = 1; + +} diff --git a/src/main/java/x/Devices/BaseDevice.java b/src/main/java/x/Devices/BaseDevice.java new file mode 100644 index 0000000..56e6395 --- /dev/null +++ b/src/main/java/x/Devices/BaseDevice.java @@ -0,0 +1,141 @@ +package x.Devices; + +import akka.actor.AbstractActor; +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.Props; +import akka.actor.ReceiveTimeout; +import akka.event.Logging; +import akka.event.LoggingAdapter; +import java.time.Duration; +import java.util.Random; +import x.DeviceUtils.PropertyChangedEvent; +import x.DeviceUtils.RecorderItem; + +public class BaseDevice { + + private static final ActorSystem ACTORSYSTEM = ActorSystem.create("homeautomationsystem"); + + private ActorRef deviceActor = null; + + public static final int DEFAULTCYCLETIME = 50; + private int cycleTime = BaseDevice.DEFAULTCYCLETIME; + + public int deviceHandle = 0; + public String displayName = ""; + public String style = ""; + public String categorie = ""; + public String id = ""; + public boolean dashboard = false; + protected RecorderItem ri = null; + + public BaseDevice(int cycleTime) { + Random rN = new Random(); + this.id = System.currentTimeMillis() + "_" + System.nanoTime() + "_" + rN.nextInt(10000); + this.cycleTime = cycleTime; + } + + public BaseDevice(String id, int cycleTime) { + this.id = id; + this.cycleTime = cycleTime; + } + + public void idleDevice() { + if (deviceActor == null) { + //deviceActor = system.actorOf(Props.create(DeviceActor.class, () -> new DeviceActor(id, cycleTime)).withDispatcher("default-dispatcher"), id); + deviceActor = ACTORSYSTEM.actorOf(Props.create(DeviceActor.class, () -> new DeviceActor(id, cycleTime)), id); + afterIdle(); + } + } + + public void afterIdle() { + } + + public int getCycleTime() { + return this.cycleTime; + } + + public RecorderItem getRecorderItem() { + return ri; + } + + public void receiveMessage(Object message) { + } + + public void receiveIdle() { + } + + void propertyChangedEvent(PropertyChangedEvent event) { + try { + event.execute(this); + } catch (Exception ex) { + } + } + + public boolean sendPropertyChangedEvent(PropertyChangedEvent pcv) { + boolean value = false; + try { + if (deviceActor != null) { + deviceActor.tell(pcv, null); + value = true; + } + } catch (Exception ex) { + value = false; + } + return value; + } + + public boolean sendMessage(Object message) { + boolean value = false; + try { + if (deviceActor != null) { + deviceActor.tell(message, null); + value = true; + } + } catch (Exception ex) { + value = false; + } + return value; + } + + private class DeviceActor extends AbstractActor { + + final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this); + private int cycleTime = BaseDevice.DEFAULTCYCLETIME; + private String id = ""; + + public DeviceActor(String id, int cycleTime) { + this.id = id; + this.cycleTime = cycleTime; + getContext().setReceiveTimeout(Duration.ofMillis(cycleTime)); + } + + @Override + public void preStart() { + //log.info(id + " started with timeout " + cycleTime); + } + + @Override + public void postStop() { + //log.info(id + " stopped"); + } + + @Override + public Receive createReceive() { + return receiveBuilder() + .match(ReceiveTimeout.class, message -> { + receiveIdle(); + }) + .matchAny(message -> { + if (message instanceof PropertyChangedEvent) { + propertyChangedEvent((PropertyChangedEvent) message); + } else { + receiveMessage(message); + } + }) + .build(); + } + + } + +} diff --git a/src/main/java/x/Devices/DHT22Device.java b/src/main/java/x/Devices/DHT22Device.java new file mode 100755 index 0000000..4f5a55d --- /dev/null +++ b/src/main/java/x/Devices/DHT22Device.java @@ -0,0 +1,129 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.RecorderItem; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.ExecCommander; +import x.utils.PropertyInfo; + +public class DHT22Device extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(DHT22Device.class); + + @PropertyInfo(handle = 0, displayName = "Temp.", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 0, displayName = "Temp.") + public String temperature = ""; + + @PropertyInfo(handle = 1, displayName = "Hum.", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 1, displayName = "Hum.") + public String humanity = ""; + + @PropertyInfo(handle = 2, editable = true, displayName = "Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true) + @DashboardInfo(handle = 2, displayName = "Status", stateful = true) + public boolean activ = true; + + private String commandExec = ""; + private String commandParams = ""; + private boolean commandSetWritable = false; + private final int commandTimeout = 15000; + private final ExecCommander dht22ExecCommander = new ExecCommander(); + + public DHT22Device(int cycleTime) { + super(cycleTime); + this.ri = new RecorderItem(24, RecorderItem.LEGENDTYPE_DATE_HOUT_MINUTE, (1000 * 60 * 60)); + } + + public void addExec(String command, String params, boolean setWritable) { + this.commandExec = command; + this.commandParams = params; + this.commandSetWritable = setWritable; + } + + private void setErrorInfo() { + temperature = "-"; + humanity = "-"; + } + + @Override + public void receiveMessage(Object message) { + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + if (activ) { + if (!commandExec.equals("")) { + try { + ExecCommander.RetVal value = dht22ExecCommander.executeCommand(commandExec, commandParams, commandSetWritable, commandTimeout); + if (value.isError() || value.isKilled()) { + setErrorInfo(); + } else { + for (String s : value.getReturnvalues()) { + if (!s.equals("")) { + String[] values = s.split(";"); + if (values.length == 2) { + temperature = values[0]; + humanity = values[1]; + logger.debug("DHT22 <" + id + "> => <" + temperature + "> <" + humanity + "> => STATE <" + activ + ">"); + } else { + setErrorInfo(); + } + } else { + setErrorInfo(); + } + break; + } + } + } catch (Exception ex) { + ex.printStackTrace(); + setErrorInfo(); + } + } else { + setErrorInfo(); + } + super.receiveIdle(); + } else { + setErrorInfo(); + } + + if (ri != null) { + ri.setActive(activ); + Object data[] = {getTemperatureAsFloat(), getHumanityAsFloat()}; + ri.addItem(new RecorderItem.InsertItem(data), id); + } + } + + public float getTemperatureAsFloat() { + float result = (float) 0.0; + if (!temperature.equals("-")) { + String[] split = temperature.split(" "); + if (split.length == 2) { + try { + result = Float.parseFloat(split[0]); + } catch (Exception e) { + result = (float) 0.0; + } + } + } + return result; + } + + public float getHumanityAsFloat() { + float result = (float) 0.0; + if (!humanity.equals("-")) { + String[] split = humanity.split(" "); + if (split.length == 2) { + try { + result = Float.parseFloat(split[0]); + } catch (Exception e) { + result = (float) 0.0; + } + } + } + return result; + } + +} diff --git a/src/main/java/x/Devices/DimmerDevice.java b/src/main/java/x/Devices/DimmerDevice.java new file mode 100755 index 0000000..1ed5097 --- /dev/null +++ b/src/main/java/x/Devices/DimmerDevice.java @@ -0,0 +1,93 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.TransferObject; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; + +public class DimmerDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(DimmerDevice.class); + public static final int MAX_VALUE = 0x7FFF; + public static final int MIN_VALUE = 0; + private static final int DIRECTION_UP = 1; + private static final int DIRECTION_DOWN = 2; + + @PropertyInfo(handle = 0, displayName = "Aktiv", type = Types.TYPE_PROPERTIEINFO_INFO, stateful = true) + public boolean activ = false; + @PropertyInfo(handle = 1, displayName = "Wert (%)", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 1, displayName = "Wert (%)") + public String valuePercent; + @PropertyInfo(handle = 2, displayName = "Verzögerung (ms)", type = Types.TYPE_PROPERTIEINFO_INFO) + public int startDelay = 1250; + public int busAddress = 0; + + private long startDelayTime = 0; + private boolean value = false; + private int CURRENTDIRECTION = DimmerDevice.DIRECTION_UP; + private int value2set = 1; + private int liveValue = 0; + private boolean stateChanged = true; + + public DimmerDevice(int cycleTime) { + super(cycleTime); + } + + @Override + public void receiveMessage(Object message) { + if (message instanceof TransferObject) { + stateChanged = ((TransferObject) message).isValueBoolean(); + } else { + value = (boolean) message; + if (value) { + startDelayTime = System.currentTimeMillis(); + if (liveValue == DimmerDevice.MIN_VALUE) { + liveValue = value2set; + activ = true; + } else { + liveValue = DimmerDevice.MIN_VALUE; + activ = false; + } + stateChanged = true; + } + logger.debug("DIMMER <" + id + "> => <" + value + "><" + value2set + "><" + liveValue + "><" + CURRENTDIRECTION + ">"); + } + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + if (value && (System.currentTimeMillis() - startDelayTime) >= startDelay) { + if (value2set >= DimmerDevice.MAX_VALUE) { + CURRENTDIRECTION = DimmerDevice.DIRECTION_DOWN; + } else if (value2set <= DimmerDevice.MIN_VALUE) { + CURRENTDIRECTION = DimmerDevice.DIRECTION_UP; + } + if (CURRENTDIRECTION == DimmerDevice.DIRECTION_UP) { + value2set += 250; + } else { + value2set -= 250; + } + liveValue = value2set; + stateChanged = true; + activ = true; + } + valuePercent = ((int) ((value2set * 100.0f) / DimmerDevice.MAX_VALUE)) + ""; + super.receiveIdle(); + } + + public boolean isValue() { + return value; + } + + public int getLiveValue() { + return liveValue; + } + + public boolean isStateChanged() { + return stateChanged; + } + +} diff --git a/src/main/java/x/Devices/HeatingPumpDevice.java b/src/main/java/x/Devices/HeatingPumpDevice.java new file mode 100755 index 0000000..a15d95a --- /dev/null +++ b/src/main/java/x/Devices/HeatingPumpDevice.java @@ -0,0 +1,259 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.DateUtils; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.RecorderItem; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; + +public class HeatingPumpDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(HeatingPumpDevice.class); + private static final int WATERINDEX = 0; + private static final int PUFFERINDEX = 1; + private static final int FLOWINDEX = 2; + private static final int PUMPINDEX = 0; + private static final int FLOWWARMINDEX = 1; + private static final int FLOWCOLDINDEX = 2; + private static final int FLOWSHUTDOWNTIME = 180000; + private static final int FLOWHYSTERESE = 20; + private static final int FLOWSECURITSHUTDOWN = 490; + // 140s komplettlauf => 2% = 2,8s + private static final int FLOWTHREADWAIT = 2800; + + @PropertyInfo(handle = 0, editable = true, displayName = "Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true, visibleOnREST = true) + @DashboardInfo(handle = 0, displayName = "Status", stateful = true) + public boolean activ = false; + + @PropertyInfo(handle = 1, displayName = "Abh. Sens.Temp.", type = Types.TYPE_PROPERTIEINFO_INFO) + public String concurrentSensorTemperatureInfo = "-"; + + @PropertyInfo(handle = 2, displayName = "Abh. Outputs", type = Types.TYPE_PROPERTIEINFO_INFO) + public String outputsInfo = "-"; + + @PropertyInfo(handle = 3, editable = true, displayName = "Start-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 30, visibleOnREST = true) + public String timeStart; + + @PropertyInfo(handle = 4, editable = true, displayName = "Stop-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 30, visibleOnREST = true) + public String timeStop; + + @PropertyInfo(handle = 5, displayName = "Running", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 1, displayName = "Running", stateful = true) + public boolean runningState = false; + + @PropertyInfo(handle = 6, editable = true, displayName = "Force Activ", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true, visibleOnREST = true) + @DashboardInfo(handle = 2, displayName = "Force Activ", stateful = true) + public boolean forceActiv = false; + + String[] outputs = null; + String[] concurrentTemperatureIds = null; + private DateUtils du = null; + + public HeatingPumpDevice(int cycleTime) { + super(cycleTime); + this.du = new DateUtils(); + this.ri = new RecorderItem(24, RecorderItem.LEGENDTYPE_DATE_HOUT_MINUTE, (1000 * 60 * 60)); + } + + public void addOutputs(String outputs) { + this.outputs = outputs.split(";"); + } + + public void addConcurrentTemperatureIds(String concurrentTemperatureIds) { + this.concurrentTemperatureIds = concurrentTemperatureIds.split(";"); + } + + @Override + public void afterIdle() { + if (concurrentTemperatureIds != null) { + StringBuilder namedconcurrentSensorTemperatureIds = new StringBuilder(); + for (String tId : concurrentTemperatureIds) { + if (!tId.equals(id)) { + SensorTemperatureDevice td = DeviceListUtils.getInstance().getSensorTemperatureDeviceById(tId); + if (td != null) { + namedconcurrentSensorTemperatureIds.append(td.categorie).append(" (").append(td.displayName).append(")" + ";"); + } + } + } + concurrentSensorTemperatureInfo = namedconcurrentSensorTemperatureIds.toString(); + } + if (outputs != null) { + StringBuilder namedOutputIds = new StringBuilder(); + for (String tId : outputs) { + if (!tId.equals(id)) { + OutputDevice td = DeviceListUtils.getInstance().getOutputById(tId); + if (td != null) { + namedOutputIds.append(td.categorie).append(" (").append(td.displayName).append(")" + ";"); + } + } + } + outputsInfo = namedOutputIds.toString(); + } + } + + @Override + public void receiveMessage(Object message) { + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + if (activ && checkAnyTemperatureDevicesIsActive()) { + boolean outputState = false; + boolean checkWaterCondition = checkTemperatureCondition(HeatingPumpDevice.WATERINDEX); + boolean checkPufferCondition = checkTemperatureCondition(HeatingPumpDevice.PUFFERINDEX); + if (forceActiv) { + outputState = checkPufferCondition; + } else { + if (checkTime()) { + outputState = checkPufferCondition; + } else { + outputState = (checkWaterCondition && checkPufferCondition); + } + } + if (outputState) { + outputState = checkFlowTemperatureForPumpSecurityShutdown(); + } + setOutput(HeatingPumpDevice.PUMPINDEX, outputState); + setFlowTemperature(outputState); + runningState = outputState; + String[] dateString = du.getCompleteDateString(); + logger.debug("HEATINGPUMP <" + id + "> => (" + dateString[0] + "," + dateString[1] + ") => STATE <" + activ + "> WATERCHECK <" + checkWaterCondition + "> PUFFERCHECK <" + checkPufferCondition + "> OUTPUTSTATE <" + outputState + "> RUNNINGSTATE <" + runningState + ">" + " => FORCE <" + forceActiv + ">"); + } else { + setOutput(HeatingPumpDevice.PUMPINDEX, false); + setFlowTemperature(false); + runningState = false; + } + if (ri != null) { + ri.setActive(activ); + Object data[] = {runningState}; + ri.addItem(new RecorderItem.InsertItem(data), id); + } + super.receiveIdle(); + } + + private void setOutput(int index, boolean state) { + if (outputs != null) { + if (outputs.length >= (index + 1)) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(outputs[index]); + if (o != null) { + if (o.isValue() != state) { + o.sendMessage(state); + } + } + } + } + } + + private boolean checkTime() { + return du.checkStatus(timeStart, timeStop); + } + + private boolean checkTemperatureCondition(int index) { + boolean state = false; + if (concurrentTemperatureIds != null) { + if (concurrentTemperatureIds.length >= (index + 1)) { + SensorTemperatureDevice o = DeviceListUtils.getInstance().getSensorTemperatureDeviceById(concurrentTemperatureIds[index]); + if (o != null) { + state = ((o.getValueAsInt() - o.getSollValueAsInt()) > 0); + } + } + } + return state; + } + + private boolean checkFlowTemperatureForPumpSecurityShutdown() { + boolean state = false; + if (concurrentTemperatureIds != null) { + if (concurrentTemperatureIds.length >= (HeatingPumpDevice.FLOWINDEX + 1)) { + SensorTemperatureDevice o = DeviceListUtils.getInstance().getSensorTemperatureDeviceById(concurrentTemperatureIds[HeatingPumpDevice.FLOWINDEX]); + if (o != null) { + state = ((HeatingPumpDevice.FLOWSECURITSHUTDOWN - o.getValueAsInt()) > 0); + } + } + } + return state; + } + + private void setFlowTemperature(boolean statePump) { + boolean triggered = false; + if (statePump) { + if (concurrentTemperatureIds != null) { + if (concurrentTemperatureIds.length >= (HeatingPumpDevice.FLOWINDEX + 1)) { + SensorTemperatureDevice o = DeviceListUtils.getInstance().getSensorTemperatureDeviceById(concurrentTemperatureIds[HeatingPumpDevice.FLOWINDEX]); + if (o != null) { + resetFlowShutdown(); + triggered = true; + int valueToCompare = o.getValueAsInt() - o.getSollValueAsInt(); + if (valueToCompare > 0) { + if (valueToCompare >= HeatingPumpDevice.FLOWHYSTERESE) { + setFlowTemperatureMixer(false); + } + } else if (valueToCompare < 0) { + valueToCompare = valueToCompare * -1; + if (valueToCompare >= HeatingPumpDevice.FLOWHYSTERESE) { + setFlowTemperatureMixer(true); + } + } + } + } + } + } + if (triggered == false) { + setFlowShutdown(); + } + } + + private void setFlowTemperatureMixer(boolean state) { + if (state) { + setOutput(HeatingPumpDevice.FLOWCOLDINDEX, false); + setOutput(HeatingPumpDevice.FLOWWARMINDEX, true); + try { + Thread.sleep(HeatingPumpDevice.FLOWTHREADWAIT); + } catch (InterruptedException ex) { + } + setOutput(HeatingPumpDevice.FLOWWARMINDEX, false); + } else { + setOutput(HeatingPumpDevice.FLOWWARMINDEX, false); + setOutput(HeatingPumpDevice.FLOWCOLDINDEX, true); + try { + Thread.sleep(HeatingPumpDevice.FLOWTHREADWAIT); + } catch (InterruptedException ex) { + } + setOutput(HeatingPumpDevice.FLOWCOLDINDEX, false); + } + + } + + private long shutdownFlowTemperatureStart = 0; + + public void setFlowShutdown() { + if (shutdownFlowTemperatureStart == 0) { + setOutput(HeatingPumpDevice.FLOWWARMINDEX, false); + setOutput(HeatingPumpDevice.FLOWCOLDINDEX, true); + shutdownFlowTemperatureStart = System.currentTimeMillis(); + } + if ((shutdownFlowTemperatureStart + HeatingPumpDevice.FLOWSHUTDOWNTIME) <= System.currentTimeMillis()) { + setOutput(HeatingPumpDevice.FLOWCOLDINDEX, false); + } + } + + private void resetFlowShutdown() { + if (shutdownFlowTemperatureStart != 0) { + setOutput(HeatingPumpDevice.FLOWCOLDINDEX, false); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + } + } + shutdownFlowTemperatureStart = 0; + } + + private boolean checkAnyTemperatureDevicesIsActive() { + return DeviceListUtils.getInstance().anyTemperatureDeviceIsActive(); + } + +} diff --git a/src/main/java/x/Devices/InputDevice.java b/src/main/java/x/Devices/InputDevice.java new file mode 100755 index 0000000..ff5b49a --- /dev/null +++ b/src/main/java/x/Devices/InputDevice.java @@ -0,0 +1,68 @@ +package x.Devices; + +import java.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.InputParameter; +import x.DeviceEffects.BaseEffect; + +public class InputDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(InputDevice.class); + private boolean value = false; + private long startTime = 0; + private boolean lockedByVisu = false; + private long removeTimeLockedByVisu = 0; + public ArrayList inputParams = new ArrayList<>(); + public int busAddress = 0; + + public InputDevice(int cycleTime) { + super(cycleTime); + } + + @Override + public void receiveMessage(Object message) { + value = (boolean) message; + logger.debug("INPUT <" + id + "> => <" + value + ">"); + if (value) { + startTime = System.currentTimeMillis(); + for (InputParameter param : inputParams) { + ((BaseEffect) param.effect).resetEffect(); + ((BaseEffect) param.effect).initEffect(startTime); + ((BaseEffect) param.effect).trigger(value); + } + } + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + for (InputParameter param : inputParams) { + ((BaseEffect) param.effect).trigger(value); + } + if (isLockedByVisu()) { + if (System.currentTimeMillis() > removeTimeLockedByVisu) { + setLockedByVisu(false); + } + } + super.receiveIdle(); + } + + synchronized public boolean isLockedByVisu() { + return lockedByVisu; + } + + synchronized public void setLockedByVisu(boolean lockedByVisu) { + this.lockedByVisu = lockedByVisu; + if (lockedByVisu) { + removeTimeLockedByVisu = 90000 + System.currentTimeMillis(); + } else { + removeTimeLockedByVisu = 0; + } + } + + public boolean isValue() { + return value; + } + +} diff --git a/src/main/java/x/Devices/OutputDevice.java b/src/main/java/x/Devices/OutputDevice.java new file mode 100755 index 0000000..5d89eb6 --- /dev/null +++ b/src/main/java/x/Devices/OutputDevice.java @@ -0,0 +1,95 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.DateUtils; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.Types; +import x.MessageHandling.MessageHandler; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; +import x.websocket.model.AsyncStatusMessage; + +public class OutputDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(OutputDevice.class); + + @PropertyInfo(handle = 0, editable = true, displayName = "Start-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 60) + public String timeStart = "-"; + + @PropertyInfo(handle = 1, editable = true, displayName = "Stop-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 60) + public String timeStop = "-"; + + @PropertyInfo(handle = 2, displayName = "Status", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 0, displayName = "Status", stateful = true) + public boolean valueVisu = false; + + private DateUtils du = null; + private boolean value = false; + public int busAddress = 0; + String[] concurrentOutputs = null; + + public OutputDevice(int cycleTime) { + super(cycleTime); + this.du = new DateUtils(); + } + + public void setTimeSettings(String timeStart, String timeStop) { + this.timeStart = timeStart; + this.timeStop = timeStop; + } + + public void addConcurrentOutputs(String concurrentOutputs) { + if (!concurrentOutputs.equals("")) { + this.concurrentOutputs = concurrentOutputs.split(";"); + } + } + + @Override + public void receiveMessage(Object message) { + if (concurrentOutputs != null && concurrentOutputs.length > 0 && ((boolean) message) == true) { + boolean valid = true; + for (String output : concurrentOutputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o != null && o.isValue()) { + valid = false; + break; + } + } + value = checkTimeSettings(valid); + } else { + value = checkTimeSettings((boolean) message); + } + + valueVisu = value; + + logger.debug("OUTPUT <" + id + "> => <" + value + ">"); + + AsyncStatusMessage asm = new AsyncStatusMessage(); + asm.setId("" + deviceHandle); + asm.setValue((value == true ? "1" : "0")); + asm.setKind(AsyncStatusMessage.SET); + MessageHandler.getInstance().messageToWebSocketClients(asm); + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + super.receiveIdle(); + } + + public boolean isValue() { + return value; + } + + private boolean checkTimeSettings(boolean currentStateValue) { + boolean returnValue = currentStateValue; + if (currentStateValue == true && du != null && !timeStart.equals("-") && !timeStop.equals("-")) { + returnValue = du.checkStatus(timeStart, timeStop); + String[] dateString = du.getCompleteDateString(); + logger.debug("OUTPUT TIMECHECK <" + id + "> => <" + returnValue + "><" + dateString[0] + "><" + dateString[1] + ">"); + } + return returnValue; + } + +} diff --git a/src/main/java/x/Devices/RemoteDevice.java b/src/main/java/x/Devices/RemoteDevice.java new file mode 100644 index 0000000..bd40eb1 --- /dev/null +++ b/src/main/java/x/Devices/RemoteDevice.java @@ -0,0 +1,45 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.utils.RemoteAccessRequest; + +public class RemoteDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(RemoteDevice.class); + RemoteAccessRequest request = null; + public boolean activ = false; + private int retryCounter = 0; + + public RemoteDevice(boolean activ, String url, int cycleTime, String jwt) { + super(cycleTime); + this.activ = activ; + this.request = new RemoteAccessRequest(url, jwt); + } + + @Override + public void receiveIdle() { + if (request != null && activ == true) { + try { + boolean response = request.execute(); + if (response == false) { + retryCounter++; + if (retryCounter >= 50) { + activ = false; + logger.info("Deaktivating RemoteAccess in response!"); + } + } else { + retryCounter = 0; + logger.debug("REMOTE " + response); + } + } catch (Exception ex) { + retryCounter++; + if (retryCounter >= 50) { + activ = false; + logger.info("Deaktivating RemoteAccess in Exception!"); + logger.error(ex.getMessage()); + } + } + } + } +} diff --git a/src/main/java/x/Devices/RetainDevice.java b/src/main/java/x/Devices/RetainDevice.java new file mode 100644 index 0000000..85198ab --- /dev/null +++ b/src/main/java/x/Devices/RetainDevice.java @@ -0,0 +1,51 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.retain.RetainProcess; + +public class RetainDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(RetainDevice.class); + + public boolean activ = true; + private int retryCounter = 0; + private RetainProcess retainProcess; + private static final int CYCLETIME = (1000 * 60 * 60); + + public RetainDevice(boolean active, RetainProcess retainProcess) { + super(CYCLETIME); + this.activ = active; + this.retainProcess = retainProcess; + this.preIdle(); + } + + private void preIdle() { + if (retainProcess != null) { + try { + retainProcess.runonce(); + } catch (Exception ex) { + activ = false; + logger.error(ex.getMessage()); + } + } + } + + @Override + public void receiveIdle() { + if (retainProcess != null && activ == true) { + try { + retainProcess.execute(); + retryCounter = 0; + logger.debug("REMOTE Request ok"); + } catch (Exception ex) { + retryCounter++; + if (retryCounter >= 5) { + activ = false; + logger.error("Deaktivating RetainDevice in Exception!"); + logger.error(ex.getMessage()); + } + } + } + } +} diff --git a/src/main/java/x/Devices/SceneDevice.java b/src/main/java/x/Devices/SceneDevice.java new file mode 100755 index 0000000..f39862c --- /dev/null +++ b/src/main/java/x/Devices/SceneDevice.java @@ -0,0 +1,50 @@ +package x.Devices; + +import java.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.InputParameter; +import x.DeviceEffects.BaseEffect; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; + +public class SceneDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(SceneDevice.class); + private boolean value = false; + private long startTime = 0; + public ArrayList inputParams = new ArrayList<>(); + + @PropertyInfo(handle = 0, displayName = "Sprachkennung", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 0, displayName = "Scene", editable = true) + public String speechIdent = ""; + + public SceneDevice(int cycleTime) { + super(cycleTime); + } + + @Override + public void receiveMessage(Object message) { + value = (boolean) message; + logger.debug("SCENE <" + id + "> => <" + value + ">"); + if (value) { + startTime = System.currentTimeMillis(); + for (InputParameter param : inputParams) { + ((BaseEffect) param.effect).resetEffect(); + ((BaseEffect) param.effect).initEffect(startTime); + ((BaseEffect) param.effect).trigger(value); + } + } + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + for (InputParameter param : inputParams) { + ((BaseEffect) param.effect).trigger(value); + } + super.receiveIdle(); + } +} diff --git a/src/main/java/x/Devices/SensorTemperatureDevice.java b/src/main/java/x/Devices/SensorTemperatureDevice.java new file mode 100755 index 0000000..206a4db --- /dev/null +++ b/src/main/java/x/Devices/SensorTemperatureDevice.java @@ -0,0 +1,85 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.RecorderItem; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; + +public class SensorTemperatureDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(SensorTemperatureDevice.class); + + public int busAddress = 0; + + @PropertyInfo(handle = 0, displayName = "Temp. (°C)", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 0, displayName = "Temp. (°C)") + public int valueVisu = 1000000; + private int value = 1000000; + + @PropertyInfo(handle = 1, editable = true, displayName = "Sollwert ändern", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, visibleOnREST = true) + public int sollvalue = 0; + + @PropertyInfo(handle = 2, editable = true, displayName = "Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true) + @DashboardInfo(handle = 1, displayName = "Status", stateful = true) + public boolean activ = false; + + private String[] concurrentTemperatureIds = null; + @PropertyInfo(handle = 3, displayName = "Valid", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 2, displayName = "Valid", stateful = true) + public boolean status = true; + + public SensorTemperatureDevice(int cycleTime) { + super(cycleTime); + this.ri = new RecorderItem(24, RecorderItem.LEGENDTYPE_DATE_HOUT_MINUTE, (1000 * 60 * 60)); + } + + public void addConcurrentTemperatureIds(String concurrentTemperatureIds) { + this.concurrentTemperatureIds = concurrentTemperatureIds.split(";"); + } + + public String[] getConcurrentTemperatureIds() { + return concurrentTemperatureIds; + } + + public boolean getStatus() { + return status; + } + + @Override + public void receiveMessage(Object message) { + value = (int) ((short) ((int) message)); // Zweierkomplement => 65535 -> -1 + status = (value >= sollvalue); + logger.debug("SENSOR-TEMPERATURE <" + id + "> => <" + value + ";" + status + "> (" + sollvalue + ")> => STATE <" + activ + ">"); + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + valueVisu = value; + if (ri != null) { + ri.setActive(activ); + Object data[] = {getValueAsFloat(), getSollValueAsFloat()}; + ri.addItem(new RecorderItem.InsertItem(data), id); + } + super.receiveIdle(); + } + + public float getValueAsFloat() { + return (float) (value / 10.0); + } + + public float getSollValueAsFloat() { + return (float) (sollvalue / 10.0); + } + + public int getValueAsInt() { + return value; + } + + public int getSollValueAsInt() { + return sollvalue; + } + +} diff --git a/src/main/java/x/Devices/ShadingDevice.java b/src/main/java/x/Devices/ShadingDevice.java new file mode 100644 index 0000000..05f5adb --- /dev/null +++ b/src/main/java/x/Devices/ShadingDevice.java @@ -0,0 +1,158 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.DateUtils; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; + +public class ShadingDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(ShadingDevice.class); + + @PropertyInfo(handle = 0, editable = true, displayName = "Start-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 30) + public String timeStart; + + @PropertyInfo(handle = 1, editable = true, displayName = "Stop-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 30) + public String timeStop; + + @PropertyInfo(handle = 2, displayName = "Abh. Sens.Temp.", type = Types.TYPE_PROPERTIEINFO_INFO) + public String concurrentSensorTemperatureInfo = "-"; + + @PropertyInfo(handle = 3, displayName = "Abh. Outputs", type = Types.TYPE_PROPERTIEINFO_INFO) + public String outputsInfo = "-"; + + @PropertyInfo(handle = 4, editable = true, displayName = "Sollwert Temp. ändern", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, visibleOnREST = true) + public int sollvalueTemp = 0; + + @PropertyInfo(handle = 5, editable = true, displayName = "Dauer activ", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, visibleOnREST = true) + public int durationActive = 5; + + @PropertyInfo(handle = 6, editable = true, displayName = "Dauer Nachlauf aktiv", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, visibleOnREST = true) + public int durationPostActive = 100; + + @PropertyInfo(handle = 7, editable = true, displayName = "Dauer reactiv", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, visibleOnREST = true) + public int durationReactive = 5; + + @PropertyInfo(handle = 8, editable = true, displayName = "Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true) + @DashboardInfo(handle = 0, displayName = "Status", stateful = true) + public boolean activ = false; + + private DateUtils du = null; + private boolean started = false; + private boolean idle = true; + String[] outputs = null; + String[] concurrentTemperatureIds = null; + + public ShadingDevice(int cycleTime) { + super(cycleTime); + this.du = new DateUtils(); + } + + public void addOutputs(String outputs) { + this.outputs = outputs.split(";"); + } + + public void addConcurrentTemperatureIds(String concurrentTemperatureIds) { + this.concurrentTemperatureIds = concurrentTemperatureIds.split(";"); + } + + @Override + public void afterIdle() { + if (concurrentTemperatureIds != null) { + StringBuilder namedconcurrentSensorTemperatureIds = new StringBuilder(); + for (String tId : concurrentTemperatureIds) { + if (!tId.equals(id)) { + SensorTemperatureDevice td = DeviceListUtils.getInstance().getSensorTemperatureDeviceById(tId); + if (td != null) { + namedconcurrentSensorTemperatureIds.append(td.categorie).append(" (").append(td.displayName).append(")" + ";"); + } + } + } + concurrentSensorTemperatureInfo = namedconcurrentSensorTemperatureIds.toString(); + } + if (outputs != null) { + StringBuilder namedOutputIds = new StringBuilder(); + for (String tId : outputs) { + if (!tId.equals(id)) { + OutputDevice td = DeviceListUtils.getInstance().getOutputById(tId); + if (td != null) { + namedOutputIds.append(td.categorie).append(" (").append(td.displayName).append(")" + ";"); + } + } + } + outputsInfo = namedOutputIds.toString(); + } + } + + @Override + public void receiveMessage(Object message) { + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + if (activ) { + if (this.outputs != null && this.outputs.length == 2 && idle == true) { + boolean condition = false; + if (du.checkStatus(timeStart, timeStop) && checkTemperatureCondition()) { + condition = true; + } + if (condition && started == false) { + String[] dateString = du.getCompleteDateString(); + logger.debug("SHADING <" + id + "> => (" + dateString[0] + "," + dateString[1] + ") => STATE ON"); + setOutput(0, durationActive * 1000, durationPostActive); + started = true; + } else if (condition == false && started) { + String[] dateString = du.getCompleteDateString(); + logger.debug("SHADING <" + id + "> => (" + dateString[0] + "," + dateString[1] + ") => STATE OFF"); + setOutput(1, durationReactive * 1000, 0); + started = false; + } + } + } + super.receiveIdle(); + } + + private void setOutput(int index, int duration, int postduration) { + if (outputs != null && duration > 0) { + idle = false; + OutputDevice o = DeviceListUtils.getInstance().getOutputById(outputs[index]); + try { + if (o.isValue() != true) { + o.sendMessage(true); + } + Thread.sleep(duration); + o.sendMessage(false); + if (postduration > 0) { + Thread.sleep(3000); + if (o.isValue() != true) { + o.sendMessage(true); + } + Thread.sleep(postduration); + o.sendMessage(false); + } + } catch (Exception ex) { + if (o.isValue() == true) { + o.sendMessage(false); + } + } + idle = true; + } + } + + private boolean checkTemperatureCondition() { + boolean state = false; + if (concurrentTemperatureIds != null) { + if (concurrentTemperatureIds.length >= 1) { + SensorTemperatureDevice o = DeviceListUtils.getInstance().getSensorTemperatureDeviceById(concurrentTemperatureIds[0]); + if (o != null) { + state = ((o.getValueAsInt() - sollvalueTemp) > 0); + } + } + } + return state; + } +} diff --git a/src/main/java/x/Devices/TemperatureDevice.java b/src/main/java/x/Devices/TemperatureDevice.java new file mode 100755 index 0000000..1ed95ba --- /dev/null +++ b/src/main/java/x/Devices/TemperatureDevice.java @@ -0,0 +1,204 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.RecorderItem; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; + +public class TemperatureDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(TemperatureDevice.class); + + @PropertyInfo(handle = 0, editable = true, displayName = "Sollwert ändern", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, visibleOnREST = true) + public int sollvalue = 0; + + @PropertyInfo(handle = 1, displayName = "Aktuelle Temp. (°C)", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 0, displayName = "Temp. (°C)") + public int valueVisu = 1000000; + + @PropertyInfo(handle = 2, displayName = "Schaltschwelle unten (°C)", type = Types.TYPE_PROPERTIEINFO_INFO) + public int uHysterese = 0; + + @PropertyInfo(handle = 3, displayName = "Schaltschwelle oben (°C)", type = Types.TYPE_PROPERTIEINFO_INFO) + public int oHysterese = 0; + + @PropertyInfo(handle = 4, editable = true, displayName = "Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true, visibleOnREST = true) + @DashboardInfo(handle = 1, displayName = "Status", stateful = true) + public boolean activ = false; + + @PropertyInfo(handle = 5, displayName = "Enabled", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 2, displayName = "Enabled", stateful = true) + public boolean enabled = true; + + @PropertyInfo(handle = 6, displayName = "Abh. Temp.", type = Types.TYPE_PROPERTIEINFO_INFO) + public String concurrentTemperatureInfo = "-"; + @PropertyInfo(handle = 7, editable = true, displayName = "Abh. Temp. Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true) + public boolean concurrentTemperatureActive = true; + + public int busAddress = 0; + String[] outputs = null; + String[] concurrentTemperatureIds = null; + private int value = 1000000; + private boolean outputState = false; + + public TemperatureDevice(int cycleTime) { + super(cycleTime); + this.ri = new RecorderItem(24, RecorderItem.LEGENDTYPE_DATE_HOUT_MINUTE, (1000 * 60 * 60)); + } + + public void addOutputs(String outputs) { + this.outputs = outputs.split(";"); + } + + public void addConcurrentTemperatureIds(String concurrentTemperatureIds) { + this.concurrentTemperatureIds = concurrentTemperatureIds.split(";"); + } + + @Override + public void afterIdle() { + if (concurrentTemperatureIds != null) { + StringBuilder namedconcurrentTemperatureIds = new StringBuilder(); + for (String tId : concurrentTemperatureIds) { + if (!tId.equals(id)) { + TemperatureDevice td = DeviceListUtils.getInstance().getTemperatureDeviceById(tId); + if (td != null) { + namedconcurrentTemperatureIds.append(td.categorie).append(" (").append(td.displayName).append(")" + ";"); + } + } + } + concurrentTemperatureInfo = namedconcurrentTemperatureIds.toString(); + } + } + + @Override + public void receiveMessage(Object message) { + value = (int) ((short) ((int) message)); // Zweierkomplement => 65535 -> -1 + logger.debug("TEMPERATURE <" + id + "> => <" + value + "> (" + sollvalue + "," + oHysterese + "," + uHysterese + ") => STATE <" + activ + ">"); + if (activ) { + if (value != 1000000) { + enabled = checkConcurrentTemperatures(); + if (enabled == true) { + enabled = checkSensorDevice(); + } + if (enabled == true) { + int valueToCompare = value - sollvalue; + if (valueToCompare > 0) { + if (valueToCompare >= oHysterese) { + logger.debug("TEMPERATURE <" + id + "> => "); + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() != false) { + o.sendMessage(false); + } + } + outputState = false; + } + } else if (valueToCompare < 0) { + valueToCompare = valueToCompare * -1; + if (valueToCompare >= uHysterese) { + logger.debug("TEMPERATURE <" + id + "> => "); + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() != true) { + o.sendMessage(true); + } + } + outputState = true; + } + } + } else { + logger.debug("TEMPERATURE <" + id + "> => "); + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() != false) { + o.sendMessage(false); + } + } + outputState = false; + } + } + super.receiveMessage(message); + } + } + + @Override + public void receiveIdle() { + if (activ == false) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() != false) { + o.sendMessage(false); + } + } + outputState = false; + } + + if (ri != null) { + ri.setActive(activ); + Object data[] = {getValueAsFloat(), outputState, getSollValueAsFloat()}; + ri.addItem(new RecorderItem.InsertItem(data), id); + } + + valueVisu = value; + super.receiveIdle(); + } + + public float getValueAsFloat() { + return (float) (value / 10.0); + } + + public float getSollValueAsFloat() { + return (float) (sollvalue / 10.0); + } + + private boolean checkConcurrentTemperatures() { + boolean enableDevice = true; + if (concurrentTemperatureIds != null && concurrentTemperatureActive) { + for (String tId : concurrentTemperatureIds) { + if (!tId.equals(id)) { + TemperatureDevice td = DeviceListUtils.getInstance().getTemperatureDeviceById(tId); + if (td != null) { + int triggerTemperatureLevel = td.sollvalue - td.uHysterese; + if (td.value < triggerTemperatureLevel) { + enableDevice = false; + logger.debug("TEMPERATURE <" + id + "> disabled because TEMPERATURE <" + td.id + "> to low"); + break; + } + } + } + } + } + return enableDevice; + } + + private boolean checkSensorDevice() { + boolean enableDevice = true; + for (Object deviceHandleList : DeviceListUtils.getInstance().getDeviceList()) { + if (deviceHandleList instanceof SensorTemperatureDevice) { + if (((SensorTemperatureDevice) deviceHandleList).activ == true) { + String[] concurrentIds = ((SensorTemperatureDevice) deviceHandleList).getConcurrentTemperatureIds(); + if (concurrentIds != null) { + for (String cId : concurrentIds) { + if (cId.equals(id)) { + if (((SensorTemperatureDevice) deviceHandleList).getStatus() == false) { + enableDevice = false; + logger.debug("TEMPERATURE <" + id + "> disabled because SENSOR <" + ((SensorTemperatureDevice) deviceHandleList).id + "> status"); + break; + } + } + } + } + } + } + } + return enableDevice; + } + + public boolean isOutputState() { + return outputState; + } + +} diff --git a/src/main/java/x/Devices/TimeDevice.java b/src/main/java/x/Devices/TimeDevice.java new file mode 100755 index 0000000..4d08691 --- /dev/null +++ b/src/main/java/x/Devices/TimeDevice.java @@ -0,0 +1,71 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.DateUtils; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; + +public class TimeDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(TimeDevice.class); + + @PropertyInfo(handle = 0, editable = true, displayName = "Start-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 30) + public String timeStart; + @PropertyInfo(handle = 1, editable = true, displayName = "Stop-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 30) + public String timeStop; + @PropertyInfo(handle = 2, editable = true, displayName = "Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true) + @DashboardInfo(handle = 0, displayName = "Status", stateful = true) + public boolean activ = false; + + private DateUtils du = null; + private boolean started = false; + private boolean timevalue = false; + String[] outputs = null; + + public TimeDevice(int cycleTime) { + super(cycleTime); + this.du = new DateUtils(); + } + + public void addOutputs(String outputs) { + this.outputs = outputs.split(";"); + } + + @Override + public void receiveMessage(Object message) { + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + if (activ) { + timevalue = du.checkStatus(timeStart, timeStop); + if (timevalue && started == false) { + String[] dateString = du.getCompleteDateString(); + logger.debug("TIME <" + id + "> => (" + dateString[0] + "," + dateString[1] + ") => STATE <" + activ + ">"); + setOutputs(true); + started = true; + } else if (timevalue == false && started) { + String[] dateString = du.getCompleteDateString(); + logger.debug("TIME <" + id + "> => (" + dateString[0] + "," + dateString[1] + ") => STATE <" + activ + ">"); + setOutputs(false); + started = false; + } + } + super.receiveIdle(); + } + + private void setOutputs(boolean state) { + if (outputs != null) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() != state) { + o.sendMessage(state); + } + } + } + } +} diff --git a/src/main/java/x/Devices/VentilationDevice.java b/src/main/java/x/Devices/VentilationDevice.java new file mode 100644 index 0000000..cbd0f9e --- /dev/null +++ b/src/main/java/x/Devices/VentilationDevice.java @@ -0,0 +1,153 @@ +package x.Devices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import x.DeviceUtils.DateUtils; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.RecorderItem; +import x.DeviceUtils.Types; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; + +public class VentilationDevice extends BaseDevice { + + private final Logger logger = LoggerFactory.getLogger(VentilationDevice.class); + + @PropertyInfo(handle = 0, editable = true, displayName = "Sollwert ändern", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE) + public int sollvalue = 0; + + @PropertyInfo(handle = 1, editable = true, displayName = "Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true, visibleOnREST = true) + @DashboardInfo(handle = 0, displayName = "Status", stateful = true) + public boolean activ = false; + + @PropertyInfo(handle = 2, displayName = "Abh. Sens.Temp.", type = Types.TYPE_PROPERTIEINFO_INFO) + public String concurrentSensorTemperatureInfo = "-"; + + @PropertyInfo(handle = 3, displayName = "Abh. Outputs", type = Types.TYPE_PROPERTIEINFO_INFO) + public String outputsInfo = "-"; + + @PropertyInfo(handle = 4, displayName = "Running", type = Types.TYPE_PROPERTIEINFO_INFO) + @DashboardInfo(handle = 1, displayName = "Running", stateful = true) + public boolean runningState = false; + + @PropertyInfo(handle = 5, editable = true, displayName = "Force Activ", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true, visibleOnREST = true) + @DashboardInfo(handle = 2, displayName = "Force Activ", stateful = true) + public boolean forceActiv = false; + + @PropertyInfo(handle = 6, editable = true, displayName = "Start-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 30, visibleOnREST = true) + public String timeStart; + @PropertyInfo(handle = 7, editable = true, displayName = "Stop-Zeit", type = Types.TYPE_PROPERTIEINFO_CHANGESOLLVALUE, propertyType = Types.PROPERTYTYPE_TIME, stepValue = 30, visibleOnREST = true) + public String timeStop; + @PropertyInfo(handle = 8, editable = true, displayName = "AUS-Zeit Status", type = Types.TYPE_PROPERTIEINFO_TOGGLEACTIVATION, stateful = true, visibleOnREST = true) + public boolean offTimeActive = false; + + String[] outputs = null; + String[] concurrentTemperatureIds = null; + private DateUtils du = null; + + public VentilationDevice(int cycleTime) { + super(cycleTime); + this.du = new DateUtils(); + this.ri = new RecorderItem(24, RecorderItem.LEGENDTYPE_DATE_HOUT_MINUTE, (1000 * 60 * 60)); + } + + public void addOutputs(String outputs) { + this.outputs = outputs.split(";"); + } + + public void addConcurrentTemperatureIds(String concurrentTemperatureIds) { + this.concurrentTemperatureIds = concurrentTemperatureIds.split(";"); + } + + @Override + public void afterIdle() { + if (concurrentTemperatureIds != null) { + StringBuilder namedconcurrentSensorTemperatureIds = new StringBuilder(); + for (String tId : concurrentTemperatureIds) { + if (!tId.equals(id)) { + SensorTemperatureDevice td = DeviceListUtils.getInstance().getSensorTemperatureDeviceById(tId); + if (td != null) { + namedconcurrentSensorTemperatureIds.append(td.categorie).append(" (").append(td.displayName).append(")" + ";"); + } + } + } + concurrentSensorTemperatureInfo = namedconcurrentSensorTemperatureIds.toString(); + } + if (outputs != null) { + StringBuilder namedOutputIds = new StringBuilder(); + for (String tId : outputs) { + if (!tId.equals(id)) { + OutputDevice td = DeviceListUtils.getInstance().getOutputById(tId); + if (td != null) { + namedOutputIds.append(td.categorie).append(" (").append(td.displayName).append(")" + ";"); + } + } + } + outputsInfo = namedOutputIds.toString(); + } + } + + @Override + public void receiveMessage(Object message) { + super.receiveMessage(message); + } + + @Override + public void receiveIdle() { + if (activ) { + if (forceActiv) { + runningState = true; + } else { + if (checkTemperatureCondition()) { + runningState = !(offTimeActive && checkOffTime()); + } else { + runningState = false; + } + } + } else { + runningState = false; + } + setOutputs(runningState); + if (ri != null) { + ri.setActive(activ); + Object data[] = {runningState}; + ri.addItem(new RecorderItem.InsertItem(data), id); + } + logger.debug("VentilationDevice <" + id + "> => STATE <" + activ + "> => RUNNINGSTATE <" + runningState + ">" + " => FORCE <" + forceActiv + ">"); + super.receiveIdle(); + } + + private void setOutputs(boolean state) { + if (outputs != null) { + for (String output : outputs) { + OutputDevice o = DeviceListUtils.getInstance().getOutputById(output); + if (o.isValue() != state) { + o.sendMessage(state); + } + } + } + } + + private boolean checkTemperatureCondition() { + boolean state = false; + if (concurrentTemperatureIds != null) { + for (String temp : concurrentTemperatureIds) { + SensorTemperatureDevice o = DeviceListUtils.getInstance().getSensorTemperatureDeviceById(temp); + if (o != null) { + state = ((o.getValueAsInt() - sollvalue) >= 0); + if (state == false) { + break; + } + } else { + state = false; + break; + } + } + } + return state; + } + + private boolean checkOffTime() { + return du.checkStatus(timeStart, timeStop); + } +} diff --git a/src/main/java/x/MessageHandling/MessageHandler.java b/src/main/java/x/MessageHandling/MessageHandler.java new file mode 100755 index 0000000..3e6ef29 --- /dev/null +++ b/src/main/java/x/MessageHandling/MessageHandler.java @@ -0,0 +1,102 @@ +package x.MessageHandling; + +import java.lang.reflect.Field; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.PropertyChangedEvent; +import x.Devices.InputDevice; +import x.Devices.SceneDevice; +import x.Devices.BaseDevice; +import x.utils.PropertyInfo; +import x.websocket.model.AsyncStatusMessage; +import x.websocket.model.ConnectionManager; + +public class MessageHandler { + + private static MessageHandler singleton = null; + private ConnectionManager cm = null; + + public MessageHandler() { + } + + synchronized public static MessageHandler getInstance() { + if (singleton == null) { + singleton = new MessageHandler(); + } + return singleton; + } + + public void setConnectionManager(ConnectionManager cm) { + if (this.cm == null) { + this.cm = cm; + } + } + + synchronized public void messageToWebSocketClients(AsyncStatusMessage messageToSend) { + if (this.cm != null) { + synchronized (this.cm.getConnections()) { + this.cm.cleanConnections(); + if (this.cm.getConnections().size() > 0) { + for (ConnectionManager.Status u : this.cm.getConnections()) { + if (u.isInitDone()) { + this.cm.sendMessage(u.getUuid(), messageToSend); + } + } + } + } + } + } + + synchronized public void messageToDevice(String message) { + String[] messageIdentifier = message.split("#"); + if (messageIdentifier.length == 2) { + if (messageIdentifier[0].startsWith("p")) { + String[] handleObject = messageIdentifier[0].substring(1, messageIdentifier[0].length()).split("_"); + if (handleObject.length == 2) { + int deviceHandle = Integer.parseInt(handleObject[0]); + int propertyHandle = Integer.parseInt(handleObject[1]); + for (Object deviceHandleMap : DeviceListUtils.getInstance().getDeviceList()) { + if (((BaseDevice) deviceHandleMap).deviceHandle == deviceHandle) { + Field[] fields = ((BaseDevice) deviceHandleMap).getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + if (p.handle() == propertyHandle) { + ((BaseDevice) deviceHandleMap).sendPropertyChangedEvent(new PropertyChangedEvent(f, p, messageIdentifier[1], false)); + break; + } + } + } + break; + } + } + } + } else if (messageIdentifier[0].equals("scene")) { + SceneDevice d = null; + if (messageIdentifier[1].startsWith("-") && messageIdentifier[1].length() == 5) { + d = DeviceListUtils.getInstance().getSceneDeviceByHandle(Integer.parseInt(messageIdentifier[1])); + } else { + d = DeviceListUtils.getInstance().getSceneDeviceBySpeechIdent(messageIdentifier[1]); + } + if (d != null) { + d.sendMessage(true); + } + } else if (!messageIdentifier[0].equals("") && !messageIdentifier[0].equals("undefined")) { + int deviceHandle = Integer.parseInt(messageIdentifier[0]); + for (Object inputMap : DeviceListUtils.getInstance().getDeviceList()) { + if (inputMap instanceof InputDevice) { + if (((InputDevice) inputMap).deviceHandle == deviceHandle) { + ((InputDevice) inputMap).setLockedByVisu(messageIdentifier[1].equals("1")); + ((InputDevice) inputMap).sendMessage(messageIdentifier[1].equals("1")); + AsyncStatusMessage asm = new AsyncStatusMessage(); + asm.setKind(AsyncStatusMessage.SET); + asm.setId(messageIdentifier[0]); + asm.setValue(messageIdentifier[1]); + messageToWebSocketClients(asm); + break; + } + } + } + } + } + } +} diff --git a/src/main/java/x/main/XHomeautomationMain.java b/src/main/java/x/main/XHomeautomationMain.java new file mode 100755 index 0000000..657d47d --- /dev/null +++ b/src/main/java/x/main/XHomeautomationMain.java @@ -0,0 +1,128 @@ +package x.main; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.util.List; +import java.util.Properties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import x.DeviceUtils.DeviceListUtils; +import x.MessageHandling.MessageHandler; +import x.modbus.XModbusMaster; +import x.Devices.BaseDevice; +import x.Devices.RemoteDevice; +import x.Devices.RetainDevice; +import x.retain.RetainProcess; +import x.utils.DeviceXMLParser; + +@SpringBootApplication(scanBasePackages = {"x.rest", "x.websocket", "x.retain"}) +@EntityScan("x.retain.database") +@EnableJpaRepositories("x.retain.database") +public class XHomeautomationMain implements ApplicationRunner { + + Logger logger = LoggerFactory.getLogger(XHomeautomationMain.class); + + public static final String VERSION = "2.2.0"; + + @Autowired + RetainProcess retainProcess; + + private XModbusMaster mbs = null; + private Thread modbusThread = null; + + public void start(String modbusIP, boolean simulation, int cycleTimeMS) { + MessageHandler.getInstance(); + mbs = new XModbusMaster(modbusIP, simulation, cycleTimeMS); + modbusThread = new Thread(new Runnable() { + @Override + public void run() { + logger.info("Starting ModbusMaster..."); + mbs.start(); + } + }); + modbusThread.setPriority(Thread.MAX_PRIORITY); + modbusThread.start(); + } + + public boolean getDevices(String filename) { + boolean returnvalue = false; + try { + + DeviceXMLParser xmlParser = new DeviceXMLParser(); + List deviceList = xmlParser.initConfigFile(filename); + if (deviceList != null && deviceList.size() > 0) { + returnvalue = true; + DeviceListUtils.getInstance().setDeviceList(deviceList); + for (BaseDevice o : DeviceListUtils.getInstance().getDeviceList()) { + o.idleDevice(); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + return returnvalue; + } + + @Override + public void run(ApplicationArguments args) throws Exception { + String[] argsv = args.getSourceArgs(); + for (String s : argsv) { + logger.info("arg = " + s); + } + if (argsv.length >= 1) { + logger.info("Version: " + XHomeautomationMain.VERSION); + Properties prop = new Properties(); + BufferedInputStream stream; + try { + stream = new BufferedInputStream(new FileInputStream(argsv[0])); + prop.load(stream); + String configXML = prop.getProperty("configXml"); + String simulationString = prop.getProperty("simulation"); + boolean simulation = false; + if (simulationString.equals("true")) { + logger.info("Simulation activated"); + simulation = true; + } + String ipString = prop.getProperty("ip"); + String cycleTimeString = prop.getProperty("cycleTime"); + int cycleTimeMS = Integer.parseInt(cycleTimeString); + String remoteURLString = prop.getProperty("remoteURL"); + String remoteAccessSecondsString = prop.getProperty("remoteAccessSeconds"); + int remoteTimeAccess = Integer.parseInt(remoteAccessSecondsString) * 1000; + String remoteAccessString = prop.getProperty("remoteAccess"); + boolean remoteAccess = false; + if (remoteAccessString.equals("true")) { + logger.info("RemoteAccess activated: " + remoteURLString); + remoteAccess = true; + } + String remoteJWTString = prop.getProperty("remoteJWT"); + boolean parseDeviceList = getDevices(configXML); + if (parseDeviceList == true) { + RetainDevice rd = new RetainDevice(true, retainProcess); + rd.idleDevice(); + RemoteDevice r = new RemoteDevice(remoteAccess, remoteURLString, remoteTimeAccess, remoteJWTString); + r.idleDevice(); + start(ipString, simulation, cycleTimeMS); + } else { + logger.error("Error reading Devicelist..."); + } + } catch (Exception ex) { + logger.error("Error reading PropertiesFile"); + } + } else { + logger.error("Wrong number of Arguments: " + argsv.length); + } + } + + public static void main(String[] args) { + SpringApplicationBuilder builder = new SpringApplicationBuilder(XHomeautomationMain.class); + builder.headless(false).run(args); + } +} diff --git a/src/main/java/x/modbus/XModbusMaster.java b/src/main/java/x/modbus/XModbusMaster.java new file mode 100755 index 0000000..221407b --- /dev/null +++ b/src/main/java/x/modbus/XModbusMaster.java @@ -0,0 +1,306 @@ +package x.modbus; + +import java.lang.reflect.Field; +import java.util.Random; +import net.wimpi.modbus.Modbus; +import net.wimpi.modbus.ModbusException; +import net.wimpi.modbus.facade.ModbusTCPMaster; +import net.wimpi.modbus.procimg.InputRegister; +import net.wimpi.modbus.procimg.SimpleRegister; +import net.wimpi.modbus.util.BitVector; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.TransferObject; +import x.Devices.InputDevice; +import x.Devices.OutputDevice; +import x.Devices.TemperatureDevice; +import x.Devices.BaseDevice; +import x.Devices.DimmerDevice; +import x.Devices.SensorTemperatureDevice; +import x.MessageHandling.MessageHandler; +import x.utils.DashboardInfo; +import x.utils.PropertyInfo; +import x.utils.FieldParser; +import x.websocket.model.AsyncStatusMessage; + +public class XModbusMaster { + + private final Logger logger = LoggerFactory.getLogger(XModbusMaster.class); + + private int cycleTime = 75; + private long worstCycleTime = 75; + long startTime = 0; + long elapsedTime = 0; + int cyclicCounter = 0; + boolean modBusInit = false; + private String modbusHost = "192.168.1.1"; + private final int modbusPort = Modbus.DEFAULT_PORT; + ModbusTCPMaster modbusMaster = null; + BitVector bvOutput = null; + BitVector bvInput = null; + private boolean simulation = false; + public static final int modbusDigitalInputOffset = 0; + public static final int modbusDigitalOutputOffset = 0x200; + public static final int modbusAnalogOutputOffset = 0x200; + private static final int modbusUnitID = 1; + + public XModbusMaster(String modbusIP, boolean simulation, int cycleTimeMS) { + this.modbusHost = modbusIP; + this.simulation = simulation; + this.cycleTime = cycleTimeMS; + this.worstCycleTime = (long) cycleTimeMS; + initModbus(); + } + + private void initModbus() { + if (this.simulation == false) { + this.modbusMaster = new ModbusTCPMaster(this.modbusHost, this.modbusPort); + } else { + logger.info("ModbusTCPMaster startet in Simulation!"); + } + + } + + private void connectModbus(boolean reconnect) { + + if (reconnect && simulation == false) { + modbusMaster.disconnect(); + try { + Thread.sleep(5000); + } catch (InterruptedException ex) { + logger.error(ex.getMessage()); + } + modbusMaster = null; + this.modbusMaster = new ModbusTCPMaster(this.modbusHost, this.modbusPort); + } + + if (modbusMaster != null) { + try { + modbusMaster.connect(); + modBusInit = true; + } catch (Exception ex) { + logger.error(ex.getMessage()); + } + } else if (simulation) { + modBusInit = true; + } + } + + public void start() { + connectModbus(false); + if (modBusInit == true) { + while (true) { + startTime = System.currentTimeMillis(); + try { + readModbusDigitalInputs(); + } catch (ModbusException ex) { + logger.error(ex.getMessage()); + connectModbus(true); + } + + try { + writeModbusDigitalOutputs(); + } catch (ModbusException ex) { + logger.error(ex.getMessage()); + connectModbus(true); + } + + try { + writeModbusDimmerAnalogOutputs(); + } catch (ModbusException ex) { + logger.error(ex.getMessage()); + connectModbus(true); + } + + if (cyclicCounter % 150 == 0) { + try { + readModbusAnalogInputs(); + } catch (ModbusException ex) { + logger.error(ex.getMessage()); + connectModbus(true); + } + } + + if (cyclicCounter % 50 == 0) { + updateDeviceHandleStatusForWebSocket(); + } + + if (cyclicCounter >= 1000) { + cyclicCounter = 0; + logger.debug("INFO <" + cycleTime + "ms><" + elapsedTime + "ms><" + worstCycleTime + "ms>"); + } + elapsedTime = System.currentTimeMillis() - startTime; + if (elapsedTime < cycleTime) { + try { + Thread.sleep(cycleTime - elapsedTime); + } catch (InterruptedException ex) { + logger.error(ex.getMessage()); + } + } else { + if (elapsedTime > worstCycleTime) { + worstCycleTime = elapsedTime; + } + } + cyclicCounter++; + } + } else { + logger.error("Error init Modbus!!"); + } + } + + private void readModbusDigitalInputs() throws ModbusException { + if (simulation == false) { + int max = DeviceListUtils.getInstance().getMaxInputBusAddress(); + if (max >= 0) { + int bitCount = max + 1; + bvInput = modbusMaster.readInputDiscretes(XModbusMaster.modbusDigitalInputOffset, bitCount); + for (int address = 0; address < bitCount; address++) { + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if (deviceHandle instanceof InputDevice) { + if (((InputDevice) deviceHandle).busAddress == address) { + if (((InputDevice) deviceHandle).isLockedByVisu() == false && ((InputDevice) deviceHandle).isValue() != bvInput.getBit(address)) { + ((InputDevice) deviceHandle).sendMessage(bvInput.getBit(address)); + } + break; + } + } + } + } + } + } + } + + private void writeModbusDigitalOutputs() throws ModbusException { + if (simulation == false) { + int max = DeviceListUtils.getInstance().getMaxOutputBusAddress(); + if (max >= 0) { + int bitCount = max + 1; + bvOutput = modbusMaster.readCoils(XModbusMaster.modbusDigitalOutputOffset, bitCount); + for (int address = 0; address < bitCount; address++) { + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if (deviceHandle instanceof OutputDevice) { + if (((OutputDevice) deviceHandle).busAddress == address) { + if (bvOutput.getBit(address) != ((OutputDevice) deviceHandle).isValue()) { + modbusMaster.writeCoil(XModbusMaster.modbusUnitID, ((OutputDevice) deviceHandle).busAddress, ((OutputDevice) deviceHandle).isValue()); + } + break; + } + } + } + } + } + } + } + + private void readModbusAnalogInputs() throws ModbusException { + if (simulation == false) { + int max = DeviceListUtils.getInstance().getMaxAnalogInBusAddress(); + if (max >= 0) { + int bitCount = max + 1; + InputRegister iReg[] = modbusMaster.readInputRegisters(0, bitCount); + for (int address = 0; address < bitCount; address++) { + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if (deviceHandle instanceof TemperatureDevice) { + if (((TemperatureDevice) deviceHandle).busAddress == address) { + ((TemperatureDevice) deviceHandle).sendMessage(iReg[address].getValue()); + break; + } + } else if (deviceHandle instanceof SensorTemperatureDevice) { + if (((SensorTemperatureDevice) deviceHandle).busAddress == address) { + ((SensorTemperatureDevice) deviceHandle).sendMessage(iReg[address].getValue()); + break; + } + } + } + } + } + } else { + Random r = new Random(); + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if (deviceHandle instanceof TemperatureDevice) { + ((TemperatureDevice) deviceHandle).sendMessage(r.nextInt(300)); + } else if (deviceHandle instanceof SensorTemperatureDevice) { + ((SensorTemperatureDevice) deviceHandle).sendMessage(200 + r.nextInt(500)); + } + } + } + } + + private void writeModbusDimmerAnalogOutputs() throws ModbusException { + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if (deviceHandle instanceof DimmerDevice) { + if (((DimmerDevice) deviceHandle).isStateChanged()) { + ((DimmerDevice) deviceHandle).sendMessage(new TransferObject(false)); + int value = ((DimmerDevice) deviceHandle).getLiveValue(); + if (value > DimmerDevice.MAX_VALUE) { + value = DimmerDevice.MAX_VALUE; + } else if (value < DimmerDevice.MIN_VALUE) { + value = DimmerDevice.MIN_VALUE; + } + SimpleRegister outregister = new SimpleRegister(value); + if (simulation == false) { + modbusMaster.writeSingleRegister(((DimmerDevice) deviceHandle).busAddress, outregister); + // Read Back + //InputRegister iReg[] = modbusMaster.readInputRegisters(XModbusMaster.modbusAnalogOutputOffset, ((DimmerDevice) deviceHandle).busAddress + 1); + } + logger.debug("SPS-DIMMERVALUE <" + outregister.getValue() + ">"); + } + } + } + } + + private void updateDeviceHandleStatusForWebSocket() { + AsyncStatusMessage globalASM = new AsyncStatusMessage(); + globalASM.setKind(AsyncStatusMessage.MULTISET); + globalASM.setError(AsyncStatusMessage.NOERROR); + globalASM.getAsm().add(new AsyncStatusMessage(AsyncStatusMessage.NOERROR, AsyncStatusMessage.SET, "-10", cycleTime + "|" + elapsedTime + "|" + worstCycleTime)); + //MessageHandler.getInstance().messageToWebSocketClients(new AsyncStatusMessage(AsyncStatusMessage.NOERROR, AsyncStatusMessage.SET, "-10", cycleTime + "|" + elapsedTime + "|" + worstCycleTime)); + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if (deviceHandle instanceof OutputDevice) { + globalASM.getAsm().add(new AsyncStatusMessage(AsyncStatusMessage.NOERROR, AsyncStatusMessage.SET, "" + ((OutputDevice) deviceHandle).deviceHandle, (((OutputDevice) deviceHandle).isValue() == true ? "1" : "0"))); + //MessageHandler.getInstance().messageToWebSocketClients(new AsyncStatusMessage(AsyncStatusMessage.NOERROR, AsyncStatusMessage.SET, "" + ((OutputDevice) deviceHandle).deviceHandle, (((OutputDevice) deviceHandle).isValue() == true ? "1" : "0"))); + } + Field[] fields = deviceHandle.getClass().getFields(); + for (Field f : fields) { + + // PropertyInfos + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + try { + String value2Send = FieldParser.convertField(f, deviceHandle); + if (p.stateful()) { + if (FieldParser.convertFieldState(f, deviceHandle)) { + value2Send = value2Send + "|xactive"; + } + } + //MessageHandler.getInstance().messageToWebSocketClients(new AsyncStatusMessage(AsyncStatusMessage.NOERROR, AsyncStatusMessage.SET, "p" + ((BaseDevice) deviceHandle).deviceHandle + "_" + p.handle(), value2Send)); + globalASM.getAsm().add(new AsyncStatusMessage(AsyncStatusMessage.NOERROR, AsyncStatusMessage.SET, "p" + ((BaseDevice) deviceHandle).deviceHandle + "_" + p.handle(), value2Send)); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + // DashboardInfos + if (((BaseDevice) deviceHandle).dashboard == true) { + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + try { + String value2Send = FieldParser.convertField(f, deviceHandle); + if (d.stateful()) { + if (FieldParser.convertFieldState(f, deviceHandle)) { + value2Send = value2Send + "|xactive"; + } + } + //MessageHandler.getInstance().messageToWebSocketClients(new AsyncStatusMessage(AsyncStatusMessage.NOERROR, AsyncStatusMessage.SET, "d" + ((BaseDevice) deviceHandle).deviceHandle + "_" + d.handle(), value2Send)); + globalASM.getAsm().add(new AsyncStatusMessage(AsyncStatusMessage.NOERROR, AsyncStatusMessage.SET, "d" + ((BaseDevice) deviceHandle).deviceHandle + "_" + d.handle(), value2Send)); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + } + } + } + MessageHandler.getInstance().messageToWebSocketClients(globalASM); + } +} diff --git a/src/main/java/x/rest/RestServerMessageResource.java b/src/main/java/x/rest/RestServerMessageResource.java new file mode 100755 index 0000000..7bf0491 --- /dev/null +++ b/src/main/java/x/rest/RestServerMessageResource.java @@ -0,0 +1,184 @@ +package x.rest; + +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.PropertyChangedEvent; +import x.DeviceUtils.Types; +import x.Devices.DHT22Device; +import x.Devices.HeatingPumpDevice; +import x.Devices.OutputDevice; +import x.Devices.SensorTemperatureDevice; +import x.Devices.TemperatureDevice; +import x.Devices.VentilationDevice; +import x.rest.data.DashboardResponse; +import x.rest.data.devices.RestBaseDevice; +import x.rest.data.devices.RestParam; +import x.utils.PropertyInfo; +import x.utils.FieldParser; +import x.Devices.BaseDevice; +import x.rest.data.StatusRequest; +import x.rest.data.StatusResponse; + +@RestController +@CrossOrigin(origins = "*") +@RequestMapping(path = "/rest/data") +public class RestServerMessageResource { + + private final Logger logger = LoggerFactory.getLogger(RestServerMessageResource.class); + + private DashboardResponse dashboard() { + + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); + String tsTamp = dateFormat.format(Calendar.getInstance().getTime()); + + DashboardResponse sr = new DashboardResponse(); + sr.setError(0); + sr.setTstamp(tsTamp); + + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + BaseDevice bdo = (BaseDevice) deviceHandle; + RestBaseDevice bd = new RestBaseDevice(); + bd.setNameCategory("" + bdo.categorie); + bd.setHandleDevice("" + bdo.deviceHandle); + bd.setIdDevice(bdo.id); + bd.setDisplayNameDevice(bdo.displayName); + bd.setStyleDevice(bdo.style); + boolean valid = false; + if (deviceHandle instanceof SensorTemperatureDevice) { + bd.setTypeDevice("" + Types.TYPE_SENSOR); + bd.setSollvalue("" + ((SensorTemperatureDevice) deviceHandle).getSollValueAsFloat()); + bd.setValue("" + ((SensorTemperatureDevice) deviceHandle).getValueAsFloat()); + getPropertyInfosForDevice(deviceHandle, bd); + valid = true; + } else if (deviceHandle instanceof DHT22Device) { + bd.setTypeDevice("" + Types.TYPE_DHT22); + bd.setActiv("" + ((DHT22Device) deviceHandle).activ); + bd.setTemperature("" + ((DHT22Device) deviceHandle).getTemperatureAsFloat()); + bd.setHumanity("" + ((DHT22Device) deviceHandle).getHumanityAsFloat()); + getPropertyInfosForDevice(deviceHandle, bd); + valid = true; + } else if (deviceHandle instanceof HeatingPumpDevice) { + bd.setTypeDevice("" + Types.TYPE_HEATINGPUMP); + bd.setRunningState("" + ((HeatingPumpDevice) deviceHandle).runningState); + bd.setForceActiv("" + ((HeatingPumpDevice) deviceHandle).forceActiv); + bd.setActiv("" + ((HeatingPumpDevice) deviceHandle).activ); + getPropertyInfosForDevice(deviceHandle, bd); + valid = true; + } else if (deviceHandle instanceof VentilationDevice) { + bd.setTypeDevice("" + Types.TYPE_VENTILATION); + bd.setRunningState("" + ((VentilationDevice) deviceHandle).runningState); + bd.setForceActiv("" + ((VentilationDevice) deviceHandle).forceActiv); + bd.setActiv("" + ((VentilationDevice) deviceHandle).activ); + getPropertyInfosForDevice(deviceHandle, bd); + valid = true; + } else if (deviceHandle instanceof OutputDevice) { + bd.setTypeDevice("" + Types.TYPE_OUTPUT); + bd.setValue("" + ((OutputDevice) deviceHandle).isValue()); + getPropertyInfosForDevice(deviceHandle, bd); + valid = true; + } else if (deviceHandle instanceof TemperatureDevice) { + bd.setTypeDevice("" + Types.TYPE_TEMPERATURE); + bd.setSollvalue("" + ((TemperatureDevice) deviceHandle).getSollValueAsFloat()); + bd.setValue("" + ((TemperatureDevice) deviceHandle).getValueAsFloat()); + getPropertyInfosForDevice(deviceHandle, bd); + valid = true; + } + + if (valid) { + sr.getDevices().add(bd); + } + + } + return sr; + + } + + private void getPropertyInfosForDevice(Object deviceHandle, RestBaseDevice bd) { + Field[] fields = deviceHandle.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + try { + if (p.visibleOnREST()) { + RestParam param = new RestParam(); + param.setHandle("" + p.handle()); + param.setEditable("" + p.editable()); + param.setVisibleValue("" + p.visibleValue()); + param.setType("" + p.type()); + param.setValue(FieldParser.convertField(f, deviceHandle)); + param.setDisplayName(p.displayName()); + bd.getParams().add(param); + } + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + } + } + + private boolean messageToDevice(String message) { + boolean error = true; + String[] messageIdentifier = message.split("@"); + if (messageIdentifier.length == 2) { + if (messageIdentifier[0].startsWith("p")) { + String[] handleObject = messageIdentifier[0].substring(1, messageIdentifier[0].length()).split("_"); + if (handleObject.length == 2) { + int deviceHandle = Integer.parseInt(handleObject[0]); + int propertyHandle = Integer.parseInt(handleObject[1]); + for (Object deviceHandleMap : DeviceListUtils.getInstance().getDeviceList()) { + if (((BaseDevice) deviceHandleMap).deviceHandle == deviceHandle) { + Field[] fields = ((BaseDevice) deviceHandleMap).getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + if (p.handle() == propertyHandle) { + ((BaseDevice) deviceHandleMap).sendPropertyChangedEvent(new PropertyChangedEvent(f, p, messageIdentifier[1], false)); + error = false; + break; + } + } + } + break; + } + } + } + } + } + return error; + } + + @RequestMapping(value = "/dashboardv2", method = RequestMethod.GET) + @ResponseStatus(value = HttpStatus.OK) + @ResponseBody + public DashboardResponse dashboardv2() { + return dashboard(); + } + + //http://localhost:5005/rest/data/set/p-3005_1@1/true + @RequestMapping(value = "/setv2", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + @ResponseBody + public StatusResponse setv2(@RequestBody StatusRequest data) { + boolean error = true; + if (data != null && data.getValue().length() > 0) { + error = messageToDevice(data.getValue()); + } + StatusResponse sr = new StatusResponse(); + sr.setError("" + error); + sr.setEcho(data.getValue()); + return sr; + } + +} diff --git a/src/main/java/x/rest/data/DashboardResponse.java b/src/main/java/x/rest/data/DashboardResponse.java new file mode 100644 index 0000000..29eedf8 --- /dev/null +++ b/src/main/java/x/rest/data/DashboardResponse.java @@ -0,0 +1,38 @@ +package x.rest.data; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.util.ArrayList; +import x.rest.data.devices.RestBaseDevice; + +public class DashboardResponse { + + private int error = 0; + private String tstamp = ""; + private ArrayList devices = new ArrayList<>(); + + public int getError() { + return error; + } + + public void setError(int error) { + this.error = error; + } + + public String getTstamp() { + return tstamp; + } + + public void setTstamp(String tstamp) { + this.tstamp = tstamp; + } + + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + public ArrayList getDevices() { + return devices; + } + + public void setDevices(ArrayList devices) { + this.devices = devices; + } + +} diff --git a/src/main/java/x/rest/data/StatusRequest.java b/src/main/java/x/rest/data/StatusRequest.java new file mode 100644 index 0000000..3be79e7 --- /dev/null +++ b/src/main/java/x/rest/data/StatusRequest.java @@ -0,0 +1,15 @@ +package x.rest.data; + +public class StatusRequest { + + private String value = ""; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/x/rest/data/StatusResponse.java b/src/main/java/x/rest/data/StatusResponse.java new file mode 100644 index 0000000..fb66e46 --- /dev/null +++ b/src/main/java/x/rest/data/StatusResponse.java @@ -0,0 +1,24 @@ +package x.rest.data; + +public class StatusResponse { + + private String error = ""; + private String echo = ""; + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public String getEcho() { + return echo; + } + + public void setEcho(String echo) { + this.echo = echo; + } + +} diff --git a/src/main/java/x/rest/data/devices/Record.java b/src/main/java/x/rest/data/devices/Record.java new file mode 100644 index 0000000..7b4872e --- /dev/null +++ b/src/main/java/x/rest/data/devices/Record.java @@ -0,0 +1,51 @@ +package x.rest.data.devices; + +public class Record { + + private String id = ""; + private String dimension = ""; + private String legend = ""; + private String values = ""; + private String date = ""; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDimension() { + return dimension; + } + + public void setDimension(String dimension) { + this.dimension = dimension; + } + + public String getLegend() { + return legend; + } + + public void setLegend(String legend) { + this.legend = legend; + } + + public String getValues() { + return values; + } + + public void setValues(String values) { + this.values = values; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + +} diff --git a/src/main/java/x/rest/data/devices/RestBaseDevice.java b/src/main/java/x/rest/data/devices/RestBaseDevice.java new file mode 100644 index 0000000..ba48921 --- /dev/null +++ b/src/main/java/x/rest/data/devices/RestBaseDevice.java @@ -0,0 +1,136 @@ +package x.rest.data.devices; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.util.ArrayList; + +public class RestBaseDevice { + + private String nameCategory = ""; + private String handleDevice = ""; + private String idDevice = ""; + private String typeDevice = ""; + private String displayNameDevice = ""; + private String styleDevice = ""; + private String sollvalue = ""; + private String value = ""; + private String activ = "false"; + private String temperature = ""; + private String humanity = ""; + private String runningState = "false"; + private String forceActiv = "false"; + private ArrayList params = new ArrayList<>(); + + public String getNameCategory() { + return nameCategory; + } + + public void setNameCategory(String nameCategory) { + this.nameCategory = nameCategory; + } + + public String getHandleDevice() { + return handleDevice; + } + + public void setHandleDevice(String handleDevice) { + this.handleDevice = handleDevice; + } + + public String getIdDevice() { + return idDevice; + } + + public void setIdDevice(String idDevice) { + this.idDevice = idDevice; + } + + public String getTypeDevice() { + return typeDevice; + } + + public void setTypeDevice(String typeDevice) { + this.typeDevice = typeDevice; + } + + public String getDisplayNameDevice() { + return displayNameDevice; + } + + public void setDisplayNameDevice(String displayNameDevice) { + this.displayNameDevice = displayNameDevice; + } + + public String getStyleDevice() { + return styleDevice; + } + + public void setStyleDevice(String styleDevice) { + this.styleDevice = styleDevice; + } + + public String getSollvalue() { + return sollvalue; + } + + public void setSollvalue(String sollvalue) { + this.sollvalue = sollvalue; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getActiv() { + return activ; + } + + public void setActiv(String activ) { + this.activ = activ; + } + + public String getTemperature() { + return temperature; + } + + public void setTemperature(String temperature) { + this.temperature = temperature; + } + + public String getHumanity() { + return humanity; + } + + public void setHumanity(String humanity) { + this.humanity = humanity; + } + + public String getRunningState() { + return runningState; + } + + public void setRunningState(String runningState) { + this.runningState = runningState; + } + + public String getForceActiv() { + return forceActiv; + } + + public void setForceActiv(String forceActiv) { + this.forceActiv = forceActiv; + } + + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + public ArrayList getParams() { + return params; + } + + public void setParams(ArrayList params) { + this.params = params; + } + +} diff --git a/src/main/java/x/rest/data/devices/RestParam.java b/src/main/java/x/rest/data/devices/RestParam.java new file mode 100644 index 0000000..2190bf7 --- /dev/null +++ b/src/main/java/x/rest/data/devices/RestParam.java @@ -0,0 +1,60 @@ +package x.rest.data.devices; + +public class RestParam { + + private String handle = ""; + private String editable = "false"; + private String visibleValue = ""; + private String type = ""; + private String value = ""; + private String displayName = ""; + + public String getHandle() { + return handle; + } + + public void setHandle(String handle) { + this.handle = handle; + } + + public String getEditable() { + return editable; + } + + public void setEditable(String editable) { + this.editable = editable; + } + + public String getVisibleValue() { + return visibleValue; + } + + public void setVisibleValue(String visibleValue) { + this.visibleValue = visibleValue; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + +} diff --git a/src/main/java/x/retain/RetainProcess.java b/src/main/java/x/retain/RetainProcess.java new file mode 100644 index 0000000..e44cf67 --- /dev/null +++ b/src/main/java/x/retain/RetainProcess.java @@ -0,0 +1,106 @@ +package x.retain; + +import java.lang.reflect.Field; +import java.time.LocalDate; +import java.time.LocalTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.PropertyChangedEvent; +import x.Devices.BaseDevice; +import x.Devices.DimmerDevice; +import x.Devices.HeatingPumpDevice; +import x.Devices.SensorTemperatureDevice; +import x.Devices.TemperatureDevice; +import x.Devices.TimeDevice; +import x.Devices.OutputDevice; +import x.Devices.ShadingDevice; +import x.Devices.VentilationDevice; +import x.retain.database.Retainvariables; +import x.retain.database.RetainvariablesRepository; +import x.utils.FieldParser; +import x.utils.PropertyInfo; + +@Service +public class RetainProcess { + + private final Logger logger = LoggerFactory.getLogger(RetainProcess.class); + + @Autowired + private RetainvariablesRepository retainvariablesRepository; + + public RetainProcess() { + logger.debug("RetainProcess initialized"); + } + + public void runonce() throws Exception { + Iterable entries = retainvariablesRepository.findAll(); + for (Retainvariables rv : entries) { + for (BaseDevice deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if (deviceHandle.deviceHandle == rv.getDevicehandle()) { + updateProperty(deviceHandle, rv.getPropertyhandle(), rv.getPropertyvalue()); + break; + } + } + } + } + + private void updateProperty(BaseDevice deviceHandleObject, int properryHandle, String propertyValue) { + Field[] fields = deviceHandleObject.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + if (p.handle() == properryHandle) { + if (p.stateful() || p.editable()) { + deviceHandleObject.sendPropertyChangedEvent(new PropertyChangedEvent(f, p, propertyValue, true)); + } + break; + } + } + } + } + + private void getProperties(BaseDevice deviceHandleObject) throws Exception { + Field[] fields = deviceHandleObject.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + if (p.stateful() || p.editable()) { + int deviceHandle = deviceHandleObject.deviceHandle; + int propertyHandle = p.handle(); + String propertyValue = FieldParser.convertField(f, deviceHandleObject); + String displayName = p.displayName(); + LocalDate ld = LocalDate.now(); + Retainvariables rv = retainvariablesRepository.findByDevicehandleAndPropertyhandle(deviceHandle, propertyHandle); + if (rv == null) { + rv = new Retainvariables(); + rv.setDevicehandle(deviceHandle); + rv.setPropertyhandle(propertyHandle); + rv.setPropertyname(displayName); + } + rv.setPropertyvalue(propertyValue); + rv.setTstamp(ld.atTime(LocalTime.now())); + retainvariablesRepository.save(rv); + } + } + } + } + + public void execute() throws Exception { + for (BaseDevice deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if ((deviceHandle instanceof TemperatureDevice) + || (deviceHandle instanceof HeatingPumpDevice) + || (deviceHandle instanceof SensorTemperatureDevice) + || (deviceHandle instanceof TimeDevice) + || (deviceHandle instanceof VentilationDevice) + || (deviceHandle instanceof OutputDevice) + || (deviceHandle instanceof ShadingDevice) + || (deviceHandle instanceof DimmerDevice)) { + getProperties(deviceHandle); + } + } + } + +} diff --git a/src/main/java/x/retain/database/Retainvariables.java b/src/main/java/x/retain/database/Retainvariables.java new file mode 100644 index 0000000..50db64b --- /dev/null +++ b/src/main/java/x/retain/database/Retainvariables.java @@ -0,0 +1,70 @@ +package x.retain.database; + +import java.time.LocalDateTime; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Retainvariables { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + private int devicehandle = 0; + private int propertyhandle = 0; + private String propertyvalue = ""; + private String propertyname = ""; + private LocalDateTime tstamp; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public int getDevicehandle() { + return devicehandle; + } + + public void setDevicehandle(int devicehandle) { + this.devicehandle = devicehandle; + } + + public int getPropertyhandle() { + return propertyhandle; + } + + public void setPropertyhandle(int propertyhandle) { + this.propertyhandle = propertyhandle; + } + + public String getPropertyvalue() { + return propertyvalue; + } + + public void setPropertyvalue(String propertyvalue) { + this.propertyvalue = propertyvalue; + } + + public String getPropertyname() { + return propertyname; + } + + public void setPropertyname(String propertyname) { + this.propertyname = propertyname; + } + + public LocalDateTime getTstamp() { + return tstamp; + } + + public void setTstamp(LocalDateTime tstamp) { + this.tstamp = tstamp; + } + +} diff --git a/src/main/java/x/retain/database/RetainvariablesRepository.java b/src/main/java/x/retain/database/RetainvariablesRepository.java new file mode 100644 index 0000000..be3cff4 --- /dev/null +++ b/src/main/java/x/retain/database/RetainvariablesRepository.java @@ -0,0 +1,16 @@ +package x.retain.database; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RetainvariablesRepository extends CrudRepository { + + Retainvariables findById(int id); + + Retainvariables findByDevicehandle(int handle); + + Retainvariables findByPropertyhandle(int handle); + + Retainvariables findByDevicehandleAndPropertyhandle(int devicehandle, int propertyhandle); +} diff --git a/src/main/java/x/utils/DashboardInfo.java b/src/main/java/x/utils/DashboardInfo.java new file mode 100755 index 0000000..57adfef --- /dev/null +++ b/src/main/java/x/utils/DashboardInfo.java @@ -0,0 +1,19 @@ +package x.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface DashboardInfo { + + int handle() default 0; + + String displayName() default ""; + + boolean stateful() default false; + + boolean editable() default false; +} diff --git a/src/main/java/x/utils/DeviceXMLParser.java b/src/main/java/x/utils/DeviceXMLParser.java new file mode 100644 index 0000000..cbf3730 --- /dev/null +++ b/src/main/java/x/utils/DeviceXMLParser.java @@ -0,0 +1,685 @@ +package x.utils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import x.DeviceEffects.BaseEffect; +import x.DeviceEffects.BridgeEffect; +import x.DeviceEffects.DimmerEffect; +import x.DeviceEffects.OffEffect; +import x.DeviceEffects.OnEffect; +import x.DeviceEffects.ToggleEffect; +import x.DeviceUtils.InputParameter; +import x.Devices.OutputDevice; +import x.Devices.BaseDevice; +import x.DeviceUtils.Types; +import x.Devices.DHT22Device; +import x.Devices.DimmerDevice; +import x.Devices.HeatingPumpDevice; +import x.Devices.InputDevice; +import x.Devices.SceneDevice; +import x.Devices.SensorTemperatureDevice; +import x.Devices.ShadingDevice; +import x.Devices.TemperatureDevice; +import x.Devices.TimeDevice; +import x.Devices.VentilationDevice; + +public class DeviceXMLParser { + + private final ArrayList outputsId = new ArrayList<>(); + private final ArrayList dimmerId = new ArrayList<>(); + private final ArrayList inputId = new ArrayList<>(); + private final ArrayList temperatureId = new ArrayList<>(); + private final ArrayList temperatureSensorId = new ArrayList<>(); + private final ArrayList outputsBus = new ArrayList<>(); + private final ArrayList dimmerBus = new ArrayList<>(); + private final ArrayList inputsBus = new ArrayList<>(); + private final ArrayList temperature_sensorTemperatureBus = new ArrayList<>(); + private final ArrayList sceneId = new ArrayList<>(); + private final ArrayList sceneSpeechIdent = new ArrayList<>(); + private final ArrayList timeId = new ArrayList<>(); + private final ArrayList dht22Id = new ArrayList<>(); + private final ArrayList heatingPumpId = new ArrayList<>(); + private final ArrayList ventilationId = new ArrayList<>(); + private final ArrayList shadingId = new ArrayList<>(); + + private void checkForValidOutput(String output) throws Exception { + String[] outputs = output.split(";"); + for (String s : outputs) { + if (!outputsId.contains(s)) { + throw new Exception("Output-ID :" + s + " not valid/found"); + } + } + } + + private void checkForValidTemperatures(String temperatureIds) throws Exception { + String[] temperatureIdsList = temperatureIds.split(";"); + for (String s : temperatureIdsList) { + if (!temperatureId.contains(s)) { + throw new Exception("Temperature-ID :" + s + " not valid/found"); + } + } + } + + private void checkForValidSensorTemperatures(String temperatureIds) throws Exception { + String[] temperatureIdsList = temperatureIds.split(";"); + for (String s : temperatureIdsList) { + if (!temperatureSensorId.contains(s)) { + throw new Exception("SensorTemperature-ID :" + s + " not valid/found"); + } + } + } + + private void checkForValidDimmer(String dimmer) throws Exception { + String[] outputs = dimmer.split(";"); + for (String s : outputs) { + if (!dimmerId.contains(s)) { + throw new Exception("Dimmer-ID :" + s + " not valid/found"); + } + } + } + + private int getType(String type) throws Exception { + int returnType = 0; + if (type.equalsIgnoreCase("TOGGLE")) { + returnType = Types.TYPE_TOGGLE; + } else if (type.equalsIgnoreCase("ON")) { + returnType = Types.TYPE_ON; + } else if (type.equalsIgnoreCase("OFF")) { + returnType = Types.TYPE_OFF; + } else if (type.equalsIgnoreCase("BRIDGE")) { + returnType = Types.TYPE_BRIDGE; + } else if (type.equalsIgnoreCase("DIMMER")) { + returnType = Types.TYPE_DIMMER; + } + if (returnType == 0) { + throw new Exception("Invalid Type :" + type); + } + return returnType; + } + + private String checkAndGetAttribute(Element e, String name, boolean blankCheck) throws Exception { + String value = null; + if (e.hasAttribute(name)) { + String attr = e.getAttribute(name); + if (blankCheck) { + if (!attr.isEmpty()) { + value = attr; + } + } else { + value = attr; + } + } + if (value == null) { + throw new Exception("error in checkAttribute == null for " + e.getNodeName() + " and " + name); + } + return value; + } + + private String getCategoryName(Element e) throws Exception { + String name = null; + Element p = (Element) e.getParentNode(); + if (p.getNodeName().equals("category")) { + name = checkAndGetAttribute(p, "name", true); + } else { + throw new Exception("Wrong parent Node. Should be Category"); + } + return name; + } + + void parseOutputDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String busAddress = checkAndGetAttribute(e, "busAddress", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String concurrentOutputs = checkAndGetAttribute(e, "concurrentOutputs", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + String timeStart = checkAndGetAttribute(e, "timeStart", false); + String timeStop = checkAndGetAttribute(e, "timeStop", false); + OutputDevice d = new OutputDevice(BaseDevice.DEFAULTCYCLETIME * 20); + d.id = id; + d.busAddress = Integer.parseInt(busAddress); + if (!outputsId.contains(d.id) && !"".equals(d.id)) { + outputsId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for OutputDevice"); + } + if (!outputsBus.contains(d.busAddress)) { + outputsBus.add(d.busAddress); + } else { + throw new Exception("Double OutputBusAddress:" + d.busAddress); + } + d.deviceHandle = (d.busAddress + Types.TYPE_OUTPUT) * -1; + d.categorie = categorieName; + d.displayName = displayName; + if (!concurrentOutputs.equals("")) { + d.addConcurrentOutputs(concurrentOutputs); + } + if (dashboard.equals("true")) { + d.dashboard = true; + } + if (!timeStart.equals("") && !timeStop.equals("")) { + d.setTimeSettings(timeStart, timeStop); + } + devices.add(d); + } + + void parseDimmerDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String busAddress = checkAndGetAttribute(e, "busAddress", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + String startDelay = checkAndGetAttribute(e, "startDelay", false); + String style = checkAndGetAttribute(e, "style", false); + DimmerDevice d = new DimmerDevice(BaseDevice.DEFAULTCYCLETIME); + d.id = id; + d.busAddress = Integer.parseInt(busAddress); + if (!dimmerId.contains(d.id) && !"".equals(d.id)) { + dimmerId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for DimmerDevice"); + } + if (!dimmerBus.contains(d.busAddress)) { + dimmerBus.add(d.busAddress); + } else { + throw new Exception("Double DimmerBusAddress:" + d.busAddress); + } + d.deviceHandle = (d.busAddress + Types.TYPE_DIMMERDEVICE) * -1; + d.categorie = categorieName; + d.displayName = displayName; + if (dashboard.equals("true")) { + d.dashboard = true; + } + d.startDelay = (startDelay.equals("") ? 1250 : Integer.parseInt(startDelay)); + d.style = (style.equals("") ? "dimmer" : style); + devices.add(d); + } + + void parseInputDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String busAddress = checkAndGetAttribute(e, "busAddress", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String style = checkAndGetAttribute(e, "style", false); + InputDevice d = new InputDevice(BaseDevice.DEFAULTCYCLETIME); + d.id = id; + d.busAddress = Integer.parseInt(busAddress); + if (!inputId.contains(d.id) && !"".equals(d.id)) { + inputId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for InputDevice"); + } + if (!inputsBus.contains(d.busAddress)) { + inputsBus.add(d.busAddress); + } else { + throw new Exception("Double InputBusAddress:" + d.busAddress); + } + d.deviceHandle = (d.busAddress + Types.TYPE_INPUT) * -1; + d.categorie = categorieName; + d.displayName = displayName; + d.style = (style.equals("") ? "light" : style); + NodeList params = e.getChildNodes(); + for (int i = 0; i < params.getLength(); i++) { + if (params.item(i).getNodeType() == Node.ELEMENT_NODE) { + Element el = (Element) params.item(i); + String forwardHandle = checkAndGetAttribute(el, "forwardHandle", true); + String typeString = checkAndGetAttribute(el, "type", true); + String delay = checkAndGetAttribute(el, "delay", true); + String duration = checkAndGetAttribute(el, "duration", true); + int type = getType(typeString); + if (type == Types.TYPE_DIMMER) { + checkForValidDimmer(forwardHandle); + } else { + checkForValidOutput(forwardHandle); + } + BaseEffect effect = null; + if (type != 0) { + if (type == Types.TYPE_ON) { + effect = new OnEffect(Integer.parseInt(duration), Integer.parseInt(delay), forwardHandle); + } else if (type == Types.TYPE_OFF) { + effect = new OffEffect(Integer.parseInt(delay), forwardHandle); + } else if (type == Types.TYPE_TOGGLE) { + effect = new ToggleEffect(Integer.parseInt(delay), forwardHandle); + } else if (type == Types.TYPE_BRIDGE) { + effect = new BridgeEffect(Integer.parseInt(delay), forwardHandle); + } else if (type == Types.TYPE_DIMMER) { + effect = new DimmerEffect(Integer.parseInt(delay), forwardHandle); + } + InputParameter deviceParam = new InputParameter(type, effect); + d.inputParams.add(deviceParam); + } + } + } + devices.add(d); + } + + void parseTemperatureDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String busAddress = checkAndGetAttribute(e, "busAddress", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String sollvalue = checkAndGetAttribute(e, "sollvalue", true); + String uHysterese = checkAndGetAttribute(e, "uHysterese", true); + String oHysterese = checkAndGetAttribute(e, "oHysterese", true); + String forwardHandle = checkAndGetAttribute(e, "forwardHandle", true); + String active = checkAndGetAttribute(e, "active", false); + String style = checkAndGetAttribute(e, "style", false); + String concurrentTemperatures = checkAndGetAttribute(e, "concurrentTemperatures", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + TemperatureDevice d = new TemperatureDevice(BaseDevice.DEFAULTCYCLETIME * 50); + d.id = id; + d.busAddress = Integer.parseInt(busAddress); + if (!temperatureId.contains(d.id) && !"".equals(d.id)) { + temperatureId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for TemperatureDevice"); + } + if (!temperature_sensorTemperatureBus.contains(d.busAddress)) { + temperature_sensorTemperatureBus.add(d.busAddress); + } else { + throw new Exception("Double TemperatureBusAddress:" + d.busAddress); + } + checkForValidOutput(forwardHandle); + d.deviceHandle = (d.busAddress + Types.TYPE_TEMPERATURE) * -1; + d.categorie = categorieName; + d.displayName = displayName; + d.sollvalue = Integer.parseInt(sollvalue); + d.uHysterese = Integer.parseInt(uHysterese); + d.oHysterese = Integer.parseInt(oHysterese); + if (active.equals("true")) { + d.activ = true; + } + d.style = (style.equals("") ? "temperature" : style); + d.addOutputs(forwardHandle); + if (!concurrentTemperatures.equals("")) { + d.addConcurrentTemperatureIds(concurrentTemperatures); + } + if (dashboard.equals("true")) { + d.dashboard = true; + } + devices.add(d); + } + + void parseSensorTemperatureDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String busAddress = checkAndGetAttribute(e, "busAddress", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String sollvalue = checkAndGetAttribute(e, "sollvalue", true); + String concurrentTemperatures = checkAndGetAttribute(e, "concurrentTemperatures", false); + String active = checkAndGetAttribute(e, "active", false); + String style = checkAndGetAttribute(e, "style", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + SensorTemperatureDevice d = new SensorTemperatureDevice(BaseDevice.DEFAULTCYCLETIME * 50); + d.id = id; + d.busAddress = Integer.parseInt(busAddress); + if (!temperatureSensorId.contains(d.id) && d.id != "") { + temperatureSensorId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for SensorTemperatureDevice"); + } + if (!temperature_sensorTemperatureBus.contains(d.busAddress)) { + temperature_sensorTemperatureBus.add(d.busAddress); + } else { + throw new Exception("Double SensorTemperatureBusAddress:" + d.busAddress); + } + if (!"".equals(concurrentTemperatures)) { + checkForValidTemperatures(concurrentTemperatures); + } + d.deviceHandle = (d.busAddress + Types.TYPE_SENSOR) * -1; + d.categorie = categorieName; + d.displayName = displayName; + d.sollvalue = Integer.parseInt(sollvalue); + if (active.equals("true")) { + d.activ = true; + } + d.style = (style.equals("") ? "sensor" : style); + if (!"".equals(concurrentTemperatures)) { + d.addConcurrentTemperatureIds(concurrentTemperatures); + } + if (dashboard.equals("true")) { + d.dashboard = true; + } + devices.add(d); + } + + private int sceneCounter = 0; + + void parseSceneDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String speechIdent = checkAndGetAttribute(e, "speechIdent", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String style = checkAndGetAttribute(e, "style", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + SceneDevice d = new SceneDevice(BaseDevice.DEFAULTCYCLETIME); + d.id = id; + d.speechIdent = speechIdent; + if (!sceneId.contains(d.id) && !"".equals(d.id)) { + sceneId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for SceneDevice"); + } + if (!sceneSpeechIdent.contains(d.speechIdent)) { + sceneSpeechIdent.add(d.speechIdent); + } else { + throw new Exception("Double SceneSpeechIdent:" + d.speechIdent); + } + d.deviceHandle = (sceneCounter + Types.TYPE_SCENE) * -1; + sceneCounter++; + d.categorie = categorieName; + d.displayName = displayName; + d.style = (style.equals("") ? "scene" : style); + if (dashboard.equals("true")) { + d.dashboard = true; + } + NodeList params = e.getChildNodes(); + for (int i = 0; i < params.getLength(); i++) { + if (params.item(i).getNodeType() == Node.ELEMENT_NODE) { + Element el = (Element) params.item(i); + String forwardHandle = checkAndGetAttribute(el, "forwardHandle", true); + String typeString = checkAndGetAttribute(el, "type", true); + String delay = checkAndGetAttribute(el, "delay", true); + int type = getType(typeString); + if (type == Types.TYPE_DIMMER) { + checkForValidDimmer(forwardHandle); + } else { + checkForValidOutput(forwardHandle); + } + String duration = checkAndGetAttribute(el, "duration", true); + BaseEffect effect = null; + if (type != 0) { + if (type == Types.TYPE_ON) { + effect = new OnEffect(Integer.parseInt(duration), Integer.parseInt(delay), forwardHandle); + } else if (type == Types.TYPE_OFF) { + effect = new OffEffect(Integer.parseInt(delay), forwardHandle); + } else if (type == Types.TYPE_TOGGLE) { + effect = new ToggleEffect(Integer.parseInt(delay), forwardHandle); + } else if (type == Types.TYPE_BRIDGE) { + effect = new BridgeEffect(Integer.parseInt(delay), forwardHandle); + } else if (type == Types.TYPE_DIMMER) { + effect = new DimmerEffect(Integer.parseInt(delay), forwardHandle); + } + InputParameter deviceParam = new InputParameter(type, effect); + d.inputParams.add(deviceParam); + } + } + } + devices.add(d); + } + + private int timeCounter = 0; + + void parseTimeDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String timeStart = checkAndGetAttribute(e, "timeStart", true); + String timeStop = checkAndGetAttribute(e, "timeStop", true); + String forwardHandle = checkAndGetAttribute(e, "forwardHandle", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String style = checkAndGetAttribute(e, "style", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + TimeDevice d = new TimeDevice(BaseDevice.DEFAULTCYCLETIME * 20); + d.id = id; + if (!timeId.contains(d.id) && !"".equals(d.id)) { + timeId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for TimeDevice"); + } + d.deviceHandle = (timeCounter + Types.TYPE_TIME) * -1; + timeCounter++; + d.categorie = categorieName; + d.displayName = displayName; + // Format e.g. 23:00:00 => HH:mm:ss + d.timeStart = timeStart; + d.timeStop = timeStop; + d.style = (style.equals("") ? "time" : style); + checkForValidOutput(forwardHandle); + d.addOutputs(forwardHandle); + if (dashboard.equals("true")) { + d.dashboard = true; + } + devices.add(d); + } + + private int dht22Counter = 0; + + void parseDhtDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String command = checkAndGetAttribute(e, "command", true); + String params = checkAndGetAttribute(e, "params", true); + String writable = checkAndGetAttribute(e, "writable", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String style = checkAndGetAttribute(e, "style", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + DHT22Device d = new DHT22Device(BaseDevice.DEFAULTCYCLETIME * 6000); + d.id = id; + if (!dht22Id.contains(d.id) && !"".equals(d.id)) { + dht22Id.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for DhTDevice"); + } + d.deviceHandle = (dht22Counter + Types.TYPE_DHT22) * -1; + dht22Counter++; + d.categorie = categorieName; + d.displayName = displayName; + d.addExec(command, params, (writable.equals("true"))); + d.style = (style.equals("") ? "dht22" : style); + if (dashboard.equals("true")) { + d.dashboard = true; + } + devices.add(d); + } + + private int heatingPumpCounter = 0; + + void parseHeatingDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String timeStart = checkAndGetAttribute(e, "timeStart", true); + String timeStop = checkAndGetAttribute(e, "timeStop", true); + String forwardHandle = checkAndGetAttribute(e, "forwardHandle", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String style = checkAndGetAttribute(e, "style", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + String concurrentTemperatures = checkAndGetAttribute(e, "concurrentTemperatures", false); + String active = checkAndGetAttribute(e, "active", false); + HeatingPumpDevice d = new HeatingPumpDevice(BaseDevice.DEFAULTCYCLETIME * 900); + d.id = id; + if (!heatingPumpId.contains(d.id) && !"".equals(d.id)) { + heatingPumpId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for HeatingPumpDevice"); + } + checkForValidSensorTemperatures(concurrentTemperatures); + d.deviceHandle = (heatingPumpCounter + Types.TYPE_HEATINGPUMP) * -1; + heatingPumpCounter++; + d.categorie = categorieName; + d.displayName = displayName; + d.timeStart = timeStart; + d.timeStop = timeStop; + checkForValidOutput(forwardHandle); + d.addOutputs(forwardHandle); + if (!"".equals(concurrentTemperatures)) { + d.addConcurrentTemperatureIds(concurrentTemperatures); + } + if (active.equals("true")) { + d.activ = true; + } + d.style = (style.equals("") ? "heating" : style); + if (dashboard.equals("true")) { + d.dashboard = true; + } + devices.add(d); + } + + private int ventilationCounter = 0; + + void parseVentilationDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String forwardHandle = checkAndGetAttribute(e, "forwardHandle", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String style = checkAndGetAttribute(e, "style", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + String sollvalue = checkAndGetAttribute(e, "sollvalue", false); + String timeStart = checkAndGetAttribute(e, "timeStart", false); + String timeStop = checkAndGetAttribute(e, "timeStop", false); + String concurrentTemperatures = checkAndGetAttribute(e, "concurrentTemperatures", false); + String active = checkAndGetAttribute(e, "active", false); + VentilationDevice d = new VentilationDevice(BaseDevice.DEFAULTCYCLETIME * 1200); + d.id = id; + if (!ventilationId.contains(d.id) && !"".equals(d.id)) { + ventilationId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for VentilationDevice"); + } + d.deviceHandle = (ventilationCounter + Types.TYPE_VENTILATION) * -1; + ventilationCounter++; + d.categorie = categorieName; + d.displayName = displayName; + d.timeStart = timeStart; + d.timeStop = timeStop; + d.sollvalue = Integer.parseInt(sollvalue); + checkForValidOutput(forwardHandle); + d.addOutputs(forwardHandle); + if (!"".equals(concurrentTemperatures)) { + d.addConcurrentTemperatureIds(concurrentTemperatures); + } + if (active.equals("true")) { + d.activ = true; + } + d.style = (style.equals("") ? "dht22" : style); + if (dashboard.equals("true")) { + d.dashboard = true; + } + devices.add(d); + } + + private int shadingCounter = 0; + + void parseShadingDevice(List devices, Element e, String nodeType) throws Exception { + String categorieName = getCategoryName(e); + String id = checkAndGetAttribute(e, "id", true); + String forwardHandle = checkAndGetAttribute(e, "forwardHandle", true); + String displayName = checkAndGetAttribute(e, "displayName", true); + String style = checkAndGetAttribute(e, "style", false); + String dashboard = checkAndGetAttribute(e, "dashboard", false); + String sollvalue = checkAndGetAttribute(e, "sollvalue", false); + String timeStart = checkAndGetAttribute(e, "timeStart", false); + String timeStop = checkAndGetAttribute(e, "timeStop", false); + String durationactive = checkAndGetAttribute(e, "durationactive", false); + String durationpostActive = checkAndGetAttribute(e, "durationpostactive", false); + String durationreactive = checkAndGetAttribute(e, "durationreactive", false); + String concurrentTemperatures = checkAndGetAttribute(e, "concurrentTemperatures", false); + String active = checkAndGetAttribute(e, "active", false); + ShadingDevice d = new ShadingDevice(BaseDevice.DEFAULTCYCLETIME * 18000); + d.id = id; + if (!shadingId.contains(d.id) && !"".equals(d.id)) { + shadingId.add(d.id); + } else { + throw new Exception("Double ID/Invalid:" + d.id + " for ShadingDevice"); + } + checkForValidSensorTemperatures(concurrentTemperatures); + d.deviceHandle = (shadingCounter + Types.TYPE_SHADING) * -1; + shadingCounter++; + d.categorie = categorieName; + d.displayName = displayName; + d.timeStart = timeStart; + d.timeStop = timeStop; + d.sollvalueTemp = Integer.parseInt(sollvalue); + d.durationActive = Integer.parseInt(durationactive); + d.durationPostActive = Integer.parseInt(durationpostActive); + d.durationReactive = Integer.parseInt(durationreactive); + checkForValidOutput(forwardHandle); + d.addOutputs(forwardHandle); + if (!"".equals(concurrentTemperatures)) { + d.addConcurrentTemperatureIds(concurrentTemperatures); + } + if (active.equals("true")) { + d.activ = true; + } + d.style = (style.equals("") ? "shading" : style); + if (dashboard.equals("true")) { + d.dashboard = true; + } + devices.add(d); + } + + private void parseNode(List devices, Element e, String nodeType) throws Exception { + String nodeName = e.getNodeName(); + NodeList nl = e.getChildNodes(); + if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("output")) { + parseOutputDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("dimmer")) { + parseDimmerDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("input")) { + parseInputDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("temperature")) { + parseTemperatureDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("sensorTemperature")) { + parseSensorTemperatureDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("scene")) { + parseSceneDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("time")) { + parseTimeDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("dht22")) { + parseDhtDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("heatingpump")) { + parseHeatingDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("ventilation")) { + parseVentilationDevice(devices, e, nodeType); + } else if (nodeName.equalsIgnoreCase(nodeType) && nodeName.equalsIgnoreCase("shading")) { + parseShadingDevice(devices, e, nodeType); + } + for (int i = 0; i < nl.getLength(); i++) { + if (nl.item(i).getNodeType() == Node.ELEMENT_NODE) { + Element el = (Element) nl.item(i); + parseNode(devices, el, nodeType); + } + } + } + + public List initConfigFile(String filename) throws Exception { + + List devices = new ArrayList<>(); + File file = new File(filename); + if (file.exists()) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(new File(filename)); + + parseNode(devices, document.getDocumentElement(), "output"); + parseNode(devices, document.getDocumentElement(), "dimmer"); + parseNode(devices, document.getDocumentElement(), "input"); + parseNode(devices, document.getDocumentElement(), "temperature"); + parseNode(devices, document.getDocumentElement(), "sensorTemperature"); + parseNode(devices, document.getDocumentElement(), "scene"); + parseNode(devices, document.getDocumentElement(), "time"); + parseNode(devices, document.getDocumentElement(), "dht22"); + parseNode(devices, document.getDocumentElement(), "heatingpump"); + parseNode(devices, document.getDocumentElement(), "ventilation"); + parseNode(devices, document.getDocumentElement(), "shading"); + + } + return devices; + } + + public static void main(String[] args) { + DeviceXMLParser xmpP = new DeviceXMLParser(); + try { + List bd = xmpP.initConfigFile("/home/deployment/Documents/GitHub/XHomeautomation-Service/src/main/resources/data/homeautomation.xml"); + System.out.println("Ready"); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/main/java/x/utils/ExecCommander.java b/src/main/java/x/utils/ExecCommander.java new file mode 100755 index 0000000..ddcfc89 --- /dev/null +++ b/src/main/java/x/utils/ExecCommander.java @@ -0,0 +1,264 @@ +package x.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ExecCommander { + + private AtomicBoolean globalState = new AtomicBoolean(true); + private final RetVal globalRetVal = new RetVal(); + public static int OS_WIN = 1; + public static int OS_LINUX = 2; + public static int OS_MAC = 3; + public static int OS_SOLARIS = 4; + + public static class RetVal { + + private final Object mutex = new Object(); + private boolean ready = false; + private boolean error = false; + private boolean killed = false; + private boolean timeout = false; + private int PID = 0; + private final ArrayList returnvalues = new ArrayList<>(); + + public boolean isReady() { + return ready; + } + + public void setReady(boolean ready) { + synchronized (mutex) { + this.ready = ready; + } + } + + public boolean isError() { + return error; + } + + public void setError(boolean error) { + synchronized (mutex) { + this.error = error; + } + } + + public int getPID() { + return PID; + } + + public void setPID(int PID) { + synchronized (mutex) { + this.PID = PID; + } + } + + public boolean isKilled() { + return killed; + } + + public void setKilled(boolean killed) { + synchronized (mutex) { + this.killed = killed; + } + } + + public boolean isTimeout() { + return timeout; + } + + public void setTimeout(boolean timeout) { + synchronized (mutex) { + this.timeout = timeout; + } + } + + public void addItem(String item) { + synchronized (mutex) { + returnvalues.add(item); + } + } + + public void clearItems() { + synchronized (mutex) { + returnvalues.clear(); + } + } + + public ArrayList getReturnvalues() { + return returnvalues; + } + } + + private void initGlobalRetVal() { + globalRetVal.setReady(false); + globalRetVal.setError(true); + globalRetVal.setPID(0); + globalRetVal.setKilled(false); + globalRetVal.setTimeout(false); + globalRetVal.clearItems(); + } + + private Thread runCommand(ArrayList commands) throws Exception { + Thread processThread = new Thread(new Runnable() { + @Override + public void run() { + try { + ProcessBuilder commandProcessBuilder = new ProcessBuilder(commands); + commandProcessBuilder.redirectErrorStream(true); + Process commandProcess = commandProcessBuilder.start(); + globalRetVal.setPID(getProcessID(commandProcess)); + BufferedReader inpCommandReader = new BufferedReader(new InputStreamReader(commandProcess.getInputStream())); + while (globalState.get()) { + String ret = inpCommandReader.readLine(); + if (ret != null) { + globalRetVal.addItem(ret); + } + if (!processIsRunning(commandProcess) && ret == null) { + globalState.set(false); + } + Thread.sleep(10); + } + commandProcess.waitFor(); + commandProcess.destroy(); + globalRetVal.setReady(true); + } catch (IOException | InterruptedException ex) { + Logger.getLogger(ExecCommander.class.getName()).log(Level.SEVERE, null, ex); + } + } + }); + processThread.start(); + return processThread; + } + + public RetVal executeCommand(String command, String params, boolean setWriteable, int timeoutMS) throws Exception { + + File f = new File(command); + if (!f.exists()) { + globalRetVal.setError(true); + return globalRetVal; + } + if (setWriteable) { + f.setReadable(true, true); + f.setWritable(true, true); + f.setExecutable(true, true); + } + + globalState.set(true); + initGlobalRetVal(); + + ArrayList commands = new ArrayList<>(); + if (params.length() > 0) { + command = command + " " + params; + } + String[] commandList = command.split(" "); + for (String s : commandList) { + commands.add(s); + } + long startTime = System.currentTimeMillis(); + Thread processThread = runCommand(commands); + + boolean loop = true; + while (loop) { + if (globalRetVal.isReady() || !globalState.get()) { + loop = false; + if (!globalRetVal.isTimeout() && !globalRetVal.isKilled()) { + globalRetVal.setError(false); + } + } + if (System.currentTimeMillis() > (startTime + timeoutMS)) { + globalState.set(false); + globalRetVal.setTimeout(true); + if (!globalRetVal.isReady()) { + try { + if (getOS() == OS_LINUX && globalRetVal.getPID() != 0) { + Runtime.getRuntime().exec("kill -9 " + globalRetVal.getPID()); + globalRetVal.setKilled(true); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + globalRetVal.setError(true); + } + } + Thread.sleep(10); + } + + processThread.join(); + + return globalRetVal; + } + + private int getProcessID(Process p) { + int pid = 0; + if (getOS() == OS_LINUX) { + if (p.getClass().getName().equals("java.lang.UNIXProcess")) { + try { + Field f = p.getClass().getDeclaredField("pid"); + f.setAccessible(true); + pid = f.getInt(p); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return pid; + } + + protected boolean processIsRunning(Process process) { + try { + if (getOS() == OS_LINUX) { + process.exitValue(); + } + return false; + } catch (Exception e) { + return true; + } + } + + private int getOS() { + int retval = -1; + String OS = System.getProperty("os.name").toLowerCase(); + if (OS.indexOf("win") >= 0) { + retval = OS_WIN; + } else if (OS.indexOf("mac") >= 0) { + retval = OS_MAC; + } else if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0) { + retval = OS_LINUX; + } else if (OS.indexOf("sunos") >= 0) { + retval = OS_SOLARIS; + } + return retval; + } + + public static void main(String[] args) { + ExecCommander ap = new ExecCommander(); + System.out.println("OS:" + ap.getOS()); + try { + for (int k = 0; k < 2000; k++) { + System.out.println("NR.: " + k); + ExecCommander.RetVal value = ap.executeCommand("/home/pi/XHomeautomation/dht22", "22 4", true, 15000); + for (String s : value.getReturnvalues()) { + System.out.println(s); + } + if (value.isError() || value.isKilled()) { + System.out.println("READY:" + value.isReady()); + System.out.println("ERROR:" + value.isError()); + System.out.println("KILLED:" + value.isKilled()); + System.out.println("PID = " + value.getPID()); + //break; + } + Thread.sleep(2500); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + +} diff --git a/src/main/java/x/utils/FieldParser.java b/src/main/java/x/utils/FieldParser.java new file mode 100644 index 0000000..fe9d5e6 --- /dev/null +++ b/src/main/java/x/utils/FieldParser.java @@ -0,0 +1,63 @@ +package x.utils; + +import java.lang.reflect.Field; + +public class FieldParser { + + public static String convertField(Field field, Object device) { + String value = ""; + try { + if (field.getType().equals(Integer.TYPE)) { + value = "" + field.getInt(device); + } else if (field.getType().equals(Long.TYPE)) { + value = "" + field.getLong(device); + } else if (field.getType().equals(Boolean.TYPE)) { + value = "" + field.getBoolean(device); + if (value.equals("true")) { + value = "ON"; + } else { + value = "OFF"; + } + } else if (field.getType().equals(Float.TYPE)) { + value = "" + field.getFloat(device); + } else if (field.getType().equals(String.class)) { + value = (String) field.get(device); + } + } catch (IllegalAccessException | IllegalArgumentException ex) { + value = ""; + } + return value; + } + + public static boolean convertFieldState(Field field, Object device) { + boolean returnvalue = false; + try { + if (field.getType().equals(Integer.TYPE)) { + int value = field.getInt(device); + if (value > 0) { + returnvalue = true; + } + } else if (field.getType().equals(Long.TYPE)) { + long value = field.getLong(device); + if (value > 0) { + returnvalue = true; + } + } else if (field.getType().equals(Boolean.TYPE)) { + returnvalue = field.getBoolean(device); + } else if (field.getType().equals(Float.TYPE)) { + float value = field.getFloat(device); + if (value > 0) { + returnvalue = true; + } + } else if (field.getType().equals(String.class)) { + String value = (String) field.get(device); + if (!"0".equals(value)) { + returnvalue = true; + } + } + } catch (IllegalAccessException | IllegalArgumentException ex) { + returnvalue = false; + } + return returnvalue; + } +} diff --git a/src/main/java/x/utils/JsonBuilder.java b/src/main/java/x/utils/JsonBuilder.java new file mode 100755 index 0000000..559ca93 --- /dev/null +++ b/src/main/java/x/utils/JsonBuilder.java @@ -0,0 +1,372 @@ +package x.utils; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.RecorderItem; +import x.DeviceUtils.Types; +import x.Devices.InputDevice; +import x.Devices.OutputDevice; +import x.Devices.SceneDevice; +import x.Devices.TemperatureDevice; +import x.Devices.BaseDevice; +import x.Devices.DHT22Device; +import x.Devices.HeatingPumpDevice; +import x.Devices.DimmerDevice; +import x.Devices.SensorTemperatureDevice; +import x.Devices.ShadingDevice; +import x.Devices.TimeDevice; +import x.Devices.VentilationDevice; +import x.rest.data.devices.Record; +import x.rest.data.devices.RestBaseDevice; +import x.rest.data.devices.RestParam; +import x.websocket.model.AsyncStatusMessage; + +public class JsonBuilder { + + private final ArrayList globalResultList; + + public JsonBuilder() { + globalResultList = sortList(); + } + + public DeviceHandleList fromDeviceHandleList() { + + DeviceHandleList dhl = new DeviceHandleList(); + + for (SortableList sort : globalResultList) { + RestBaseDeviceRecorder rbd = new RestBaseDeviceRecorder(); + rbd.setNameCategory(sort.categorie); + rbd.setHandleDevice("" + sort.handleDevice); + rbd.setStyleDevice(sort.styleDevice); + rbd.setTypeDevice("" + sort.typeDevice); + rbd.setDisplayNameDevice(sort.displayName); + if (sort.params != null) { + for (PropertyInfo param : sort.params) { + RestParam rp = new RestParam(); + rp.setHandle("" + param.handle()); + rp.setEditable("" + param.editable()); + rp.setVisibleValue("" + param.visibleValue()); + rp.setType("" + param.type()); + rp.setValue(""); + rp.setDisplayName(param.displayName()); + rbd.getParams().add(rp); + } + } + if (sort.ri != null) { + ArrayList riItems = sort.ri.getAllItems(); + if (riItems.size() > 0) { + for (RecorderItem.ResultItem ri : riItems) { + Record r = new Record(); + r.setId(getUUID()); + r.setDimension("" + ri.getYDimension()); + r.setLegend(ri.getX()); + r.setValues(ri.getYasString()); + r.setDate(ri.getDateTime()); + rbd.getRecords().add(r); + } + } + } + dhl.getDevices().add(rbd); + } + return dhl; + } + + private String getUUID() { + AtomicLong counter = new AtomicLong(System.currentTimeMillis() * 1000); + return Long.toString(counter.incrementAndGet(), 36); + } + + private ArrayList sortList() { + + // Categorie + ArrayList returnList = new ArrayList<>(); + HashMap categorieList = new HashMap<>(); + for (int k = 0; k < DeviceListUtils.getInstance().getDeviceList().size(); k++) { + BaseDevice temp = (BaseDevice) DeviceListUtils.getInstance().getDeviceList().get(k); + if (!categorieList.containsKey(temp.categorie)) { + categorieList.put(temp.categorie, ""); + } + } + Iterator iterator = categorieList.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry mentry = (Map.Entry) iterator.next(); + + // Input + ArrayList in = DeviceListUtils.getInstance().getInputDevicesByCategorie(mentry.getKey().toString()); + for (InputDevice device : in) { + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_INPUT, device.displayName, null, null, device.getRecorderItem())); + } + + // Temperature + ArrayList temp = DeviceListUtils.getInstance().getTemperatureDevicesByCategorie(mentry.getKey().toString()); + for (TemperatureDevice device : temp) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_TEMPERATURE, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // Sensor + ArrayList sensor = DeviceListUtils.getInstance().getSensorTemperatureDevicesByCategorie(mentry.getKey().toString()); + for (SensorTemperatureDevice device : sensor) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_SENSOR, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // Scene + ArrayList scene = DeviceListUtils.getInstance().getSceneDevicesByCategorie(mentry.getKey().toString()); + for (SceneDevice device : scene) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_SCENE, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // Time + ArrayList time = DeviceListUtils.getInstance().getTimeDevicesByCategorie(mentry.getKey().toString()); + for (TimeDevice device : time) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_TIME, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // Shading + ArrayList shading = DeviceListUtils.getInstance().getShadingDevicesByCategorie(mentry.getKey().toString()); + for (ShadingDevice device : shading) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_SHADING, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // Output + ArrayList output = DeviceListUtils.getInstance().getOutputDevicesByCategorie(mentry.getKey().toString()); + for (OutputDevice device : output) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_OUTPUT, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // Dimmer + ArrayList dimmer = DeviceListUtils.getInstance().getDimmerDevicesByCategorie(mentry.getKey().toString()); + for (DimmerDevice device : dimmer) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_DIMMERDEVICE, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // DHT22 + ArrayList dht22 = DeviceListUtils.getInstance().getDHT22DevicesByCategorie(mentry.getKey().toString()); + for (DHT22Device device : dht22) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_DHT22, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // Heatingpump + ArrayList heatingPump = DeviceListUtils.getInstance().getHeatingPumpDevicesByCategorie(mentry.getKey().toString()); + for (HeatingPumpDevice device : heatingPump) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_HEATINGPUMP, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + // Ventilation + ArrayList ventilation = DeviceListUtils.getInstance().getVentilationDevicesByCategorie(mentry.getKey().toString()); + for (VentilationDevice device : ventilation) { + ArrayList params = new ArrayList<>(); + ArrayList dashboard = new ArrayList<>(); + Field[] fields = device.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + params.add(p); + } + DashboardInfo d = (DashboardInfo) f.getAnnotation(DashboardInfo.class); + if (d != null) { + dashboard.add(d); + } + } + returnList.add(new SortableList(device.categorie, device.deviceHandle, device.style, Types.TYPE_VENTILATION, device.displayName, params, (device.dashboard == true ? dashboard : null), device.getRecorderItem())); + } + + } + return returnList; + } + + static class SortableList { + + String categorie = ""; + int handleDevice = 0; + String styleDevice = ""; + int typeDevice = 0; + String displayName = ""; + ArrayList params = null; + ArrayList dashboard = null; + RecorderItem ri = null; + + public SortableList(String categorie, int handleDevice, String styleDevice, int typeDevice, String displayName, ArrayList params, ArrayList dashboard, RecorderItem ri) { + this.categorie = categorie; + this.handleDevice = handleDevice; + this.styleDevice = styleDevice; + this.typeDevice = typeDevice; + this.displayName = displayName; + this.params = params; + this.dashboard = dashboard; + this.ri = ri; + } + + } + + public static class DeviceHandleList { + + private int error = AsyncStatusMessage.NOERROR; + private int kind = AsyncStatusMessage.INIT; + private ArrayList devices = new ArrayList<>(); + + public int getError() { + return error; + } + + public void setError(int error) { + this.error = error; + } + + public int getKind() { + return kind; + } + + public void setKind(int kind) { + this.kind = kind; + } + + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + public ArrayList getDevices() { + return devices; + } + + public void setDevices(ArrayList devices) { + this.devices = devices; + } + + } + + public static class RestBaseDeviceRecorder extends RestBaseDevice { + + private ArrayList records = new ArrayList<>(); + + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + public ArrayList getRecords() { + return records; + } + + public void setRecords(ArrayList records) { + this.records = records; + } + + } + +} diff --git a/src/main/java/x/utils/JsonUtils.java b/src/main/java/x/utils/JsonUtils.java new file mode 100644 index 0000000..57f824f --- /dev/null +++ b/src/main/java/x/utils/JsonUtils.java @@ -0,0 +1,64 @@ +package x.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class JsonUtils { + + private final HashMap outputMap = new HashMap<>(); + + public void putObject(Object key, Object value) { + outputMap.put(key, value); + } + + public String createJsonStringFromObject() { + String json = ""; + try { + json = new ObjectMapper().writeValueAsString(outputMap); + } catch (JsonProcessingException ex) { + json = ""; + } + return json; + } + + public static String createJsonStringFromObject(Object o) { + String json = ""; + try { + json = new ObjectMapper().writeValueAsString(o); + } catch (JsonProcessingException ex) { + json = ""; + } + return json; + } + + public Map parseJson(String text) { + Map o = null; + try { + ObjectMapper tmp = new ObjectMapper(); + o = tmp.readValue(text, Map.class); + } catch (IOException ex) { + o = null; + } + return o; + } + + public static void main(String[] args) { + JsonUtils ju = new JsonUtils(); + ju.putObject("Key", "Value"); + String s = ju.createJsonStringFromObject(); + System.out.println("s = " + s); + Map resultMap = ju.parseJson("{\"person\":{\"name\":\"Guillaume\",\"age\":33,\"pets\":[\"dog\",\"cat\"]}}"); + if (resultMap != null) { + Set keySet = resultMap.keySet(); + for (String key : keySet) { + Object obj = resultMap.get(key); + System.out.println("key = " + key); + System.out.println("obj = " + obj); + } + } + } +} diff --git a/src/main/java/x/utils/PropertyInfo.java b/src/main/java/x/utils/PropertyInfo.java new file mode 100755 index 0000000..6a67761 --- /dev/null +++ b/src/main/java/x/utils/PropertyInfo.java @@ -0,0 +1,31 @@ +package x.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import x.DeviceUtils.Types; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface PropertyInfo { + + int handle() default 0; + + boolean editable() default false; + + int type() default Types.TYPE_PROPERTIEINFO_DEFAULT; + + String displayName() default ""; + + boolean stateful() default false; + + boolean visibleValue() default true; + + int propertyType() default Types.PROPERTYTYPE_DEFAULT; + + int stepValue() default 0; + + boolean visibleOnREST() default false; + +} diff --git a/src/main/java/x/utils/RemoteAccessRequest.java b/src/main/java/x/utils/RemoteAccessRequest.java new file mode 100755 index 0000000..87f2367 --- /dev/null +++ b/src/main/java/x/utils/RemoteAccessRequest.java @@ -0,0 +1,257 @@ +package x.utils; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Set; +import x.DeviceUtils.DeviceListUtils; +import x.DeviceUtils.Types; +import x.Devices.BaseDevice; +import x.Devices.OutputDevice; +import x.Devices.TemperatureDevice; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.web.client.RestTemplate; +import x.Devices.DHT22Device; +import x.Devices.HeatingPumpDevice; +import x.Devices.SensorTemperatureDevice; +import x.Devices.VentilationDevice; +import x.MessageHandling.MessageHandler; + +public class RemoteAccessRequest { + + private final String urlString; + private String jwt = ""; + + public RemoteAccessRequest(String urlString, String jwt) { + this.urlString = urlString; + this.jwt = jwt; + } + + private void getStatefulDeviceProperties(Object deviceHandleObject, int deviceHandle, HashMap tmpMap) { + ArrayList list = new ArrayList<>(); + Field[] fields = deviceHandleObject.getClass().getFields(); + for (Field f : fields) { + PropertyInfo p = (PropertyInfo) f.getAnnotation(PropertyInfo.class); + if (p != null) { + if (p.stateful() || p.editable() || p.type() == Types.TYPE_PROPERTIEINFO_INFO) { + int handle = p.handle(); + String displayName = p.displayName(); + String value = FieldParser.convertField(f, deviceHandleObject); + HashMap m = new HashMap<>(); + m.put("handle", handle); + m.put("displayName", displayName); + m.put("value", value); + list.add(handle, m); + } + } + } + if (list.size() > 0) { + tmpMap.put("properties", list); + } + } + + private AutomationRequest generateCommitMessage() { + AutomationRequest atRequest = new AutomationRequest(); + atRequest.setPlugin("automation"); + AutomationRequestParams atRequestParams = new AutomationRequestParams(); + atRequestParams.setMethod("commit"); + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + HashMap tmpMap = new HashMap<>(); + if (deviceHandle instanceof OutputDevice) { + tmpMap.put("type", Types.TYPE_OUTPUT); + } else if (deviceHandle instanceof TemperatureDevice) { + tmpMap.put("type", Types.TYPE_TEMPERATURE); + } else if (deviceHandle instanceof SensorTemperatureDevice) { + tmpMap.put("type", Types.TYPE_SENSOR); + } else if (deviceHandle instanceof DHT22Device) { + tmpMap.put("type", Types.TYPE_DHT22); + } else if (deviceHandle instanceof HeatingPumpDevice) { + tmpMap.put("type", Types.TYPE_HEATINGPUMP); + } else if (deviceHandle instanceof VentilationDevice) { + tmpMap.put("type", Types.TYPE_VENTILATION); + } + if (tmpMap.size() > 0) { + tmpMap.put("categorie", ((BaseDevice) deviceHandle).categorie); + tmpMap.put("name", ((BaseDevice) deviceHandle).displayName); + getStatefulDeviceProperties(deviceHandle, ((BaseDevice) deviceHandle).deviceHandle, tmpMap); + atRequestParams.getParams().put(((BaseDevice) deviceHandle).deviceHandle, tmpMap); + } + } + atRequest.setData(atRequestParams); + return atRequest; + } + + private void changeDeviceHandle(LinkedHashMap changes) { + try { + int devicehandle = (int) changes.get("devicehandle"); + int pHandle = (int) changes.get("propertiehandle"); + String pValue = null; + if (changes.get("value") instanceof Integer) { + pValue = changes.get("value") + ""; + } else if (changes.get("value") instanceof String) { + pValue = (String) changes.get("value"); + } + if (pValue != null) { + for (Object deviceHandle : DeviceListUtils.getInstance().getDeviceList()) { + if (((BaseDevice) deviceHandle).deviceHandle == devicehandle) { + if (deviceHandle instanceof OutputDevice) { + ((BaseDevice) deviceHandle).sendMessage((pValue.equals("1") ? true : false)); + } else { + MessageHandler.getInstance().messageToDevice("p" + devicehandle + "_" + pHandle + "#" + pValue); + } + break; + } + } + } + } catch (Exception ex) { + + } + } + + public synchronized boolean execute() throws Exception { + boolean returnResult = false; + try { + AutomationRequest committRequest = generateCommitMessage(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + this.jwt); + HttpEntity entity = new HttpEntity(committRequest, headers); + RestTemplate restTemplate = new RestTemplate(); + AutomationResponse response = restTemplate.exchange(this.urlString, HttpMethod.POST, entity, AutomationResponse.class).getBody(); + AutomationResponseData responseData = response.getData(); + if (responseData.isError() == false) { + returnResult = true; + Object changes = responseData.getChanges(); + if (changes instanceof LinkedHashMap) { + Set keys = ((LinkedHashMap) changes).keySet(); + for (String k : keys) { + Object items = (LinkedHashMap) ((LinkedHashMap) changes).get(k); + if (items instanceof LinkedHashMap) { + changeDeviceHandle((LinkedHashMap) items); + } + } + Thread.sleep(750); + returnResult = execute(); + } + } + } catch (Exception ex) { + returnResult = false; + } + return returnResult; + } + + static class AutomationRequest { + + private String plugin; + private AutomationRequestParams data; + + public String getPlugin() { + return plugin; + } + + public void setPlugin(String plugin) { + this.plugin = plugin; + } + + public AutomationRequestParams getData() { + return data; + } + + public void setData(AutomationRequestParams data) { + this.data = data; + } + + } + + static class AutomationRequestParams { + + private String method; + private HashMap params = new HashMap<>(); + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public HashMap getParams() { + return params; + } + + public void setParams(HashMap params) { + this.params = params; + } + + } + + static class AutomationResponse { + + private String username; + private String alpdesk_token; + private String plugin; + private AutomationResponseData data; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAlpdesk_token() { + return alpdesk_token; + } + + public void setAlpdesk_token(String alpdesk_token) { + this.alpdesk_token = alpdesk_token; + } + + public String getPlugin() { + return plugin; + } + + public void setPlugin(String plugin) { + this.plugin = plugin; + } + + public AutomationResponseData getData() { + return data; + } + + public void setData(AutomationResponseData data) { + this.data = data; + } + + } + + static class AutomationResponseData { + + private boolean error; + private Object changes; + + public boolean isError() { + return error; + } + + public void setError(boolean error) { + this.error = error; + } + + public Object getChanges() { + return changes; + } + + public void setChanges(Object changes) { + this.changes = changes; + } + + } + +} diff --git a/src/main/java/x/websocket/config/WebSocketConfig.java b/src/main/java/x/websocket/config/WebSocketConfig.java new file mode 100644 index 0000000..6993577 --- /dev/null +++ b/src/main/java/x/websocket/config/WebSocketConfig.java @@ -0,0 +1,23 @@ +package x.websocket.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void configureMessageBroker(MessageBrokerRegistry config) { + config.enableSimpleBroker("/reply"); + config.setApplicationDestinationPrefixes("/"); + } + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/ws").setAllowedOrigins("*"); + } +} diff --git a/src/main/java/x/websocket/controller/SocketController.java b/src/main/java/x/websocket/controller/SocketController.java new file mode 100644 index 0000000..c8e61d4 --- /dev/null +++ b/src/main/java/x/websocket/controller/SocketController.java @@ -0,0 +1,82 @@ +package x.websocket.controller; + +import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.messaging.handler.annotation.DestinationVariable; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.messaging.simp.SimpMessageHeaderAccessor; +import org.springframework.stereotype.Controller; +import x.MessageHandling.MessageHandler; +import x.utils.JsonBuilder; +import x.websocket.model.AsyncStatusMessage; +import x.websocket.model.ConnectRequest; +import x.websocket.model.ConnectResponse; +import x.websocket.model.ConnectionManager; +import x.websocket.model.RequestMessage; + +@Controller +public class SocketController { + + private final Logger logger = LoggerFactory.getLogger(SocketController.class); + + @Autowired + @Qualifier("ConnectionManager") + ConnectionManager connectionManager; + + @MessageMapping("/connect") + @SendTo("/reply/connect") + public ConnectResponse connect(SimpMessageHeaderAccessor accessor, ConnectRequest request) throws Exception { + return new ConnectResponse(connectionManager.addConnection(UUID.randomUUID())); + } + + @MessageMapping("/disconnect/{connectionId}") + public void disconnect(@DestinationVariable String connectionId) throws Exception { + connectionManager.destroyConnection(UUID.fromString(connectionId)); + } + + @MessageMapping("/watchdog/{connectionId}") + public void watchdog(@DestinationVariable String connectionId) throws Exception { + connectionManager.watchdog(UUID.fromString(connectionId)); + } + + @MessageMapping("/init/{connectionId}") + @SendTo("/reply/init/{connectionId}") + public JsonBuilder.DeviceHandleList init(@DestinationVariable String connectionId) { + logger.debug("Received Init"); + JsonBuilder.DeviceHandleList value = new JsonBuilder.DeviceHandleList(); + if (connectionManager.isConnectionValid(UUID.fromString(connectionId))) { + JsonBuilder jsonBuilder = new JsonBuilder(); + value = jsonBuilder.fromDeviceHandleList(); + connectionManager.setInitDone(UUID.fromString(connectionId)); + } + return value; + } + + @MessageMapping("/message/{connectionId}") + @SendTo("/reply/{connectionId}") + public AsyncStatusMessage message(@DestinationVariable String connectionId, RequestMessage req) { + logger.debug("Received: " + req.getMessage()); + AsyncStatusMessage reply = new AsyncStatusMessage(); + reply.setError(AsyncStatusMessage.ERROR); + if (connectionManager.isConnectionValid(UUID.fromString((connectionId)))) { + reply.setError(AsyncStatusMessage.NOERROR); + reply.setValue(req.getMessage()); + reply.setKind(AsyncStatusMessage.STATUS); + //reply.setKind(AsyncStatusMessage.INFO); + if (req.getMessage().startsWith("!")) { + String[] messageIdentifier = req.getMessage().substring(1).split("#"); + if (messageIdentifier.length == 2) { + //messageToSend = jsonBuilder.fromString(JsonBuilder.INFO, JsonBuilder.NOERROR, messageIdentifier[0], messageIdentifier[1]); + MessageHandler.getInstance().messageToDevice(req.getMessage().substring(1)); + } + } + } + + return reply; + } + +} diff --git a/src/main/java/x/websocket/model/AsyncStatusMessage.java b/src/main/java/x/websocket/model/AsyncStatusMessage.java new file mode 100644 index 0000000..b1c7d06 --- /dev/null +++ b/src/main/java/x/websocket/model/AsyncStatusMessage.java @@ -0,0 +1,71 @@ +package x.websocket.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.util.ArrayList; + +public class AsyncStatusMessage { + + private int error = 0; + private int kind = 0; + private String id = ""; + private String value = ""; + + public static int ERROR = 1; + public static int NOERROR = 0; + public static int INFO = 0; + public static int STATUS = 1; + public static int SET = 2; + public static int INIT = 3; + public static int MULTISET = 4; + + private final ArrayList asm = new ArrayList<>(); + + public AsyncStatusMessage() { + + } + + public AsyncStatusMessage(int error, int kind, String id, String value) { + this.error = error; + this.kind = kind; + this.id = id; + this.value = value; + } + + public int getError() { + return error; + } + + public void setError(int error) { + this.error = error; + } + + public int getKind() { + return kind; + } + + public void setKind(int kind) { + this.kind = kind; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + public ArrayList getAsm() { + return asm; + } + +} diff --git a/src/main/java/x/websocket/model/ConnectRequest.java b/src/main/java/x/websocket/model/ConnectRequest.java new file mode 100644 index 0000000..2cf8f39 --- /dev/null +++ b/src/main/java/x/websocket/model/ConnectRequest.java @@ -0,0 +1,15 @@ +package x.websocket.model; + +public class ConnectRequest { + + private String clientId; + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + +} diff --git a/src/main/java/x/websocket/model/ConnectResponse.java b/src/main/java/x/websocket/model/ConnectResponse.java new file mode 100644 index 0000000..5bd76af --- /dev/null +++ b/src/main/java/x/websocket/model/ConnectResponse.java @@ -0,0 +1,21 @@ +package x.websocket.model; + +import java.util.UUID; + +public class ConnectResponse { + + private String connectionId; + + public ConnectResponse(UUID uuid) { + this.connectionId = uuid.toString(); + } + + public String getConnectionId() { + return connectionId; + } + + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; + } + +} diff --git a/src/main/java/x/websocket/model/ConnectionManager.java b/src/main/java/x/websocket/model/ConnectionManager.java new file mode 100644 index 0000000..1f91947 --- /dev/null +++ b/src/main/java/x/websocket/model/ConnectionManager.java @@ -0,0 +1,162 @@ +package x.websocket.model; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.UUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Controller; +import x.MessageHandling.MessageHandler; + +@Controller +@Configuration +@EnableScheduling +@Component("ConnectionManager") +public class ConnectionManager { + + private final Logger logger = LoggerFactory.getLogger(ConnectionManager.class); + private final ArrayList connections = new ArrayList<>(); + + @Autowired + private SimpMessagingTemplate webSocket; + + public ConnectionManager() { + MessageHandler.getInstance().setConnectionManager(this); + } + + public UUID addConnection(UUID ipAddress) { + synchronized (connections) { + boolean found = false; + for (Status s : connections) { + if (s.getUuid().equals(ipAddress)) { + found = true; + break; + } + } + if (found == false) { + connections.add(new Status(ipAddress)); + logger.debug("add WS-Connection: " + ipAddress.toString()); + } else { + logger.debug("still in List WS-Connection: " + ipAddress.toString()); + } + } + + return ipAddress; + } + + public void destroyConnection(UUID uuid) { + synchronized (connections) { + for (Status s : connections) { + if (s.getUuid().equals(uuid)) { + connections.remove(s); + logger.debug("destroyed WS-Connection: " + uuid.toString()); + logger.debug("Open Connections: " + connections.size()); + break; + } + } + cleanConnections(); + } + + } + + public void watchdog(UUID uuid) { + synchronized (connections) { + for (Status s : connections) { + if (s.getUuid().equals(uuid)) { + s.triggerWatchdog(); + logger.debug("Trigger Watchdog for WS-Connection: " + uuid.toString()); + break; + } + } + cleanConnections(); + } + } + + public boolean isConnectionValid(UUID uuid) { + boolean value = false; + for (Status s : connections) { + if (s.getUuid().equals(uuid)) { + value = true; + logger.debug("valid WS-Connection: " + uuid.toString()); + break; + } + } + return value; + } + + public void setInitDone(UUID uuid) { + for (Status s : connections) { + if (s.getUuid().equals(uuid)) { + s.setInitDone(true); + logger.debug("set Init done WS-Connection: " + uuid.toString()); + break; + } + } + } + + public void cleanConnections() { + synchronized (connections) { + Iterator it = connections.iterator(); + while (it.hasNext()) { + Status s = it.next(); + if (s.isWatchdogTimeout()) { + logger.debug("destroy WS-Connection: " + s.getUuid() + " because of Watchdog"); + it.remove(); + logger.debug("Open Connections: " + connections.size()); + } + } + } + } + + public ArrayList getConnections() { + return connections; + } + + public void sendMessage(UUID u, Object message) { + webSocket.convertAndSend("/reply/" + u.toString(), message); + } + + public static class Status { + + private UUID uuid; + private boolean initDone = false; + private long watchDogTime = 0; + private long watchdogTimeout = 20000; + + public Status(UUID uuid) { + this.uuid = uuid; + this.watchDogTime = System.currentTimeMillis(); + } + + public UUID getUuid() { + return uuid; + } + + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + public boolean isInitDone() { + return initDone; + } + + public void setInitDone(boolean initDone) { + this.initDone = initDone; + } + + public void triggerWatchdog() { + this.watchDogTime = System.currentTimeMillis(); + } + + public boolean isWatchdogTimeout() { + return ((System.currentTimeMillis() - watchdogTimeout) > watchDogTime); + } + + } + +} diff --git a/src/main/java/x/websocket/model/RequestMessage.java b/src/main/java/x/websocket/model/RequestMessage.java new file mode 100644 index 0000000..94953bf --- /dev/null +++ b/src/main/java/x/websocket/model/RequestMessage.java @@ -0,0 +1,15 @@ +package x.websocket.model; + +public class RequestMessage { + + private String message; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/src/main/java/x/websocket/model/ResponseMessage.java b/src/main/java/x/websocket/model/ResponseMessage.java new file mode 100644 index 0000000..bd215b0 --- /dev/null +++ b/src/main/java/x/websocket/model/ResponseMessage.java @@ -0,0 +1,24 @@ +package x.websocket.model; + +public class ResponseMessage { + + private boolean error = true; + private String mesg; + + public boolean getError() { + return error; + } + + public void setError(boolean error) { + this.error = error; + } + + public String getMesg() { + return mesg; + } + + public void setMesg(String mesg) { + this.mesg = mesg; + } + +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..c9563ff --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,27 @@ +#server +server.port=5005 + +#logging +logging.level.org.springframework=INFO +logging.level.x=DEBUG +#output to a temp_folder/file +#logging.file=${java.io.tmpdir}/application.log +# Logging pattern for the console +#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} - %msg%n +# Logging pattern for file +#logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg% + +#database +spring.datasource.url=jdbc:h2:file:/tmp/xhomeautomationdb_dev +spring.datasource.initialize=false +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=xhomeautomation +spring.datasource.password=xhomeautomation +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=update +#enable sql-Console after starting application http://localhost:8080/h2-console or http://localhost:5005/h2-console +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console +spring.h2.console.settings.trace=false +spring.h2.console.settings.web-allow-others=true + diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties new file mode 100644 index 0000000..2f5a66d --- /dev/null +++ b/src/main/resources/application-prod.properties @@ -0,0 +1,27 @@ +#server +server.port=5005 + +#logging +logging.level.org.springframework=ERROR +logging.level.x=INFO +#output to a temp_folder/file +#logging.file=${java.io.tmpdir}/application.log +# Logging pattern for the console +#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} - %msg%n +# Logging pattern for file +#logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg% + +#database +spring.datasource.url=jdbc:h2:file:/tmp/xhomeautomationdb_prod +spring.datasource.initialize=false +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=xhomeautomation +spring.datasource.password=xhomeautomation +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=update +#enable sql-Console after starting application http://localhost:8080/h2-console or http://localhost:5005/h2-console +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console +spring.h2.console.settings.trace=false +spring.h2.console.settings.web-allow-others=true + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..139b0b4 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,27 @@ +#server +server.port=5005 + +#logging +logging.level.org.springframework=INFO +logging.level.x=INFO +#output to a temp_folder/file +#logging.file=${java.io.tmpdir}/application.log +# Logging pattern for the console +#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} - %msg%n +# Logging pattern for file +#logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg% + +#database +spring.datasource.url=jdbc:h2:file:/tmp/xhomeautomationdb_prod +spring.datasource.initialize=false +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=xhomeautomation +spring.datasource.password=xhomeautomation +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=update +#enable sql-Console after starting application http://localhost:8080/h2-console or http://localhost:5005/h2-console +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console +spring.h2.console.settings.trace=false +spring.h2.console.settings.web-allow-others=true + diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..025d138 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,11 @@ + +-- DROP TABLE IF EXISTS retainvariables; + +CREATE TABLE IF NOT EXISTS retainvariables ( + id INT AUTO_INCREMENT PRIMARY KEY, + devicehandle INT DEFAULT 0, + propertyhandle INT DEFAULT 0, + propertyvalue VARCHAR(255) NOT NULL, + propertyname VARCHAR(255) NOT NULL, + tstamp TIMESTAMP NULL +); \ No newline at end of file diff --git a/src/main/resources/data/homeautomation.properties b/src/main/resources/data/homeautomation.properties new file mode 100755 index 0000000..5f35989 --- /dev/null +++ b/src/main/resources/data/homeautomation.properties @@ -0,0 +1,21 @@ + +#Homeautomation Properties +configXml=homeautomation.xml +debug=false +simulation=false +cycleTime=75 + +#IP-ModbusCoupler +ip=192.168.1.96 + +#RemoteSettings +remoteURL=http://xdebian/plugin +remoteAccessSeconds=600 +remoteAccess=true +remoteJWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IllXeHdaR1Z6YTE5MFpYTjBiV0Z1WkdGdWRBPT0ifQ.eyJpc3MiOiJBbHBkZXNrIiwiYXVkIjoiaHR0cHM6XC9cL2FscGRlc2suZGUiLCJqdGkiOiJZV3h3WkdWemExOTBaWE4wYldGdVpHRnVkQT09IiwiaWF0IjoxNTkyMjE1OTE0LCJuYmYiOjE1OTIyMTU5MTQsInVzZXJuYW1lIjoidGVzdG1hbmRhbnQifQ.WfFT6X6e1RCb40MRE9WGicT4zeOSQDHocAe7ybwTMBk + +#RestSettings +restURL=http://localhost:5005/rest +restAccess=true + + diff --git a/src/main/resources/data/homeautomation.xml b/src/main/resources/data/homeautomation.xml new file mode 100644 index 0000000..c79a69b --- /dev/null +++ b/src/main/resources/data/homeautomation.xml @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/data/homeautomationtest.properties b/src/main/resources/data/homeautomationtest.properties new file mode 100755 index 0000000..341d8ff --- /dev/null +++ b/src/main/resources/data/homeautomationtest.properties @@ -0,0 +1,15 @@ + +#Homeautomation Properties +configXml=homeautomation.xml +debug=true +simulation=true +cycleTime=75 + +#IP-ModbusCoupler +ip=192.168.1.96 + +#RemoteSettings +remoteURL=http://xdebian/plugin +remoteAccessSeconds=60 +remoteAccess=true +remoteJWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IllXeHdaR1Z6YTE5MFpYTjBiV0Z1WkdGdWRBPT0ifQ.eyJpc3MiOiJBbHBkZXNrIiwiYXVkIjoiaHR0cHM6XC9cL2FscGRlc2suZGUiLCJqdGkiOiJZV3h3WkdWemExOTBaWE4wYldGdVpHRnVkQT09IiwiaWF0IjoxNTkyMjE1OTE0LCJuYmYiOjE1OTIyMTU5MTQsInVzZXJuYW1lIjoidGVzdG1hbmRhbnQifQ.WfFT6X6e1RCb40MRE9WGicT4zeOSQDHocAe7ybwTMBk \ No newline at end of file diff --git a/src/main/resources/data/startXHomeautomation.sh b/src/main/resources/data/startXHomeautomation.sh new file mode 100755 index 0000000..fe464f2 --- /dev/null +++ b/src/main/resources/data/startXHomeautomation.sh @@ -0,0 +1,5 @@ +#!/bin/sh +cd /home/pi/xhomeautomation +mount /dev/sda1 /media/xhomeautomationusb/ +sudo mount | grep sda1 +java -Xdebug -Xrunjdwp:transport=dt_socket,address=5001,server=y,suspend=n -jar XHomeautomation-Service-Deploy.jar /home/pi/xhomeautomation/homeautomation.properties --spring.profiles.active=prod --spring.datasource.url=jdbc:h2:file:/media/xhomeautomationusb/xhomeautomationdb_prod