Compare commits
No commits in common. "c7f0d4e8b380eb0997eff60fbedd3fc60d63ba22" and "c96a00333b019293f185574a26d18c5d9b330fd6" have entirely different histories.
c7f0d4e8b3
...
c96a00333b
25 changed files with 67 additions and 258 deletions
31
build.gradle
31
build.gradle
|
|
@ -3,30 +3,6 @@ plugins {
|
||||||
id 'application'
|
id 'application'
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Ziver Koc
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
// Hal common configuration
|
// Hal common configuration
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
|
|
@ -86,7 +62,7 @@ distributions {
|
||||||
from 'hal.conf.example'
|
from 'hal.conf.example'
|
||||||
from 'logging.properties'
|
from 'logging.properties'
|
||||||
|
|
||||||
from sourceSets.main.output.resourcesDir
|
from "${buildDir}/resources"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,11 +76,10 @@ task copyRecources(type: Copy) {
|
||||||
from "${subProject.projectDir}/resources"
|
from "${subProject.projectDir}/resources"
|
||||||
}
|
}
|
||||||
|
|
||||||
into(sourceSets.main.output.resourcesDir)
|
into("${buildDir}/resources")
|
||||||
}
|
}
|
||||||
|
|
||||||
jar.dependsOn(copyRecources)
|
processResources.finalizedBy(copyRecources)
|
||||||
copyRecources.mustRunAfter(processResources)
|
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass = 'se.hal.HalServer'
|
mainClass = 'se.hal.HalServer'
|
||||||
|
|
|
||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,3 @@
|
||||||
<!--
|
|
||||||
~ The MIT License (MIT)
|
|
||||||
~
|
|
||||||
~ Copyright (c) 2025 Ziver Koc
|
|
||||||
~
|
|
||||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
~ of this software and associated documentation files (the "Software"), to deal
|
|
||||||
~ in the Software without restriction, including without limitation the rights
|
|
||||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
~ copies of the Software, and to permit persons to whom the Software is
|
|
||||||
~ furnished to do so, subject to the following conditions:
|
|
||||||
~
|
|
||||||
~ The above copyright notice and this permission notice shall be included in
|
|
||||||
~ all copies or substantial portions of the Software.
|
|
||||||
~
|
|
||||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
~ THE SOFTWARE.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<h1 class="page-header">Event Configuration</h1>
|
<h1 class="page-header">Event Configuration</h1>
|
||||||
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
|
@ -47,7 +23,7 @@
|
||||||
{{#localEvents}}
|
{{#localEvents}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{.getId()}}</td>
|
<td>{{.getId()}}</td>
|
||||||
<td><a href="event/{{.getId()}}">{{.getName()}}</a></td>
|
<td>{{.getName()}}</td>
|
||||||
<td>{{.getType()}}</td>
|
<td>{{.getType()}}</td>
|
||||||
<td>{{.getDeviceConfig()}}</td>
|
<td>{{.getDeviceConfig()}}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export default {
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<tr :data-device-id="event.id">
|
<tr :data-device-id="event.id">
|
||||||
<td><a :href="'event/' + event.id">{{ event.name }}</a></td>
|
<td><a :href="'?id=' + event.id">{{ event.name }}</a></td>
|
||||||
<td>{{ event.configType }}</td>
|
<td>{{ event.configType }}</td>
|
||||||
<td>{{ event.data?.valueStr }}</td>
|
<td>{{ event.data?.valueStr }}</td>
|
||||||
<td class="timestamp">{{ timestamp }}</td>
|
<td class="timestamp">{{ timestamp }}</td>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,3 @@
|
||||||
<!--
|
|
||||||
~ The MIT License (MIT)
|
|
||||||
~
|
|
||||||
~ Copyright (c) 2025 Ziver Koc
|
|
||||||
~
|
|
||||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
~ of this software and associated documentation files (the "Software"), to deal
|
|
||||||
~ in the Software without restriction, including without limitation the rights
|
|
||||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
~ copies of the Software, and to permit persons to whom the Software is
|
|
||||||
~ furnished to do so, subject to the following conditions:
|
|
||||||
~
|
|
||||||
~ The above copyright notice and this permission notice shall be included in
|
|
||||||
~ all copies or substantial portions of the Software.
|
|
||||||
~
|
|
||||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
~ THE SOFTWARE.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|
@ -77,14 +53,14 @@
|
||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
"vue": "./js/vue/lib/vue.esm-browser.js",
|
"vue": "./js/vue/vue.esm-browser.js",
|
||||||
"vue-router": "./js/vue/lib/vue-router.esm-browser.js",
|
"vue-router": "./js/vue/vue-router.esm-browser.js",
|
||||||
"@vue/devtools-api": "https://unpkg.com/@vue/devtools-api@6.4.5/lib/esm/index.js",
|
"@vue/devtools-api": "https://unpkg.com/@vue/devtools-api@6.4.5/lib/esm/index.js",
|
||||||
|
|
||||||
{{#javascriptModules}}"{{.getModuleName()}}": "{{.getScriptPath()}}",
|
{{#javascriptModules}}"{{.getModuleName()}}": "{{.getScriptPath()}}",
|
||||||
{{/javascriptModules}}
|
{{/javascriptModules}}
|
||||||
|
|
||||||
"App": "./js/vue/App.js"
|
"App": "./js/vue/components/App.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,3 @@
|
||||||
<!--
|
|
||||||
~ The MIT License (MIT)
|
|
||||||
~
|
|
||||||
~ Copyright (c) 2025 Ziver Koc
|
|
||||||
~
|
|
||||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
~ of this software and associated documentation files (the "Software"), to deal
|
|
||||||
~ in the Software without restriction, including without limitation the rights
|
|
||||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
~ copies of the Software, and to permit persons to whom the Software is
|
|
||||||
~ furnished to do so, subject to the following conditions:
|
|
||||||
~
|
|
||||||
~ The above copyright notice and this permission notice shall be included in
|
|
||||||
~ all copies or substantial portions of the Software.
|
|
||||||
~
|
|
||||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
~ THE SOFTWARE.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<h1 class="page-header">Sensor Configuration</h1>
|
<h1 class="page-header">Sensor Configuration</h1>
|
||||||
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
|
@ -47,7 +23,7 @@
|
||||||
{{#localSensors}}
|
{{#localSensors}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{.getId()}}</td>
|
<td>{{.getId()}}</td>
|
||||||
<td><a href="sensor_overview?id={{.getId()}}">{{.getName()}}</a></td>
|
<td>{{.getName()}}</td>
|
||||||
<td>{{.getType()}}</td>
|
<td>{{.getType()}}</td>
|
||||||
<td>{{.isSynced()}}</td>
|
<td>{{.isSynced()}}</td>
|
||||||
<td>{{.getDeviceConfig()}}</td>
|
<td>{{.getDeviceConfig()}}</td>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,3 @@
|
||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Ziver Koc
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package se.hal.intf;
|
package se.hal.intf;
|
||||||
|
|
||||||
import zutil.ClassUtil;
|
import zutil.ClassUtil;
|
||||||
|
|
@ -67,7 +43,7 @@ public abstract class HalAbstractControllerManager<T extends HalAbstractControll
|
||||||
|
|
||||||
// Instantiate autostart controllers, but only the first time
|
// Instantiate autostart controllers, but only the first time
|
||||||
|
|
||||||
synchronized (this) {
|
synchronized (autostartProcessed) {
|
||||||
if (!autostartProcessed) {
|
if (!autostartProcessed) {
|
||||||
for (Iterator<Class<? extends HalAutostartController>> it = pluginManager.getClassIterator(HalAutostartController.class); it.hasNext(); ) {
|
for (Iterator<Class<? extends HalAutostartController>> it = pluginManager.getClassIterator(HalAutostartController.class); it.hasNext(); ) {
|
||||||
Class controller = it.next();
|
Class controller = it.next();
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,3 @@
|
||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Ziver Koc
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package se.hal.page;
|
package se.hal.page;
|
||||||
|
|
||||||
import se.hal.intf.HalJavascriptModule;
|
import se.hal.intf.HalJavascriptModule;
|
||||||
|
|
@ -31,7 +7,7 @@ public class JavascriptModules implements HalJavascriptModule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HalJsModule[] getJavascriptModules() {
|
public HalJsModule[] getJavascriptModules() {
|
||||||
HalWebPage.getRootNav().createSubNav("Events").createSubNav("events", "Overview");
|
HalWebPage.getRootNav().createSubNav("Events").createSubNav("event_overview", "Overview");
|
||||||
|
|
||||||
return new HalJsModule[] {
|
return new HalJsModule[] {
|
||||||
new HalJsModule("AlertStore", "./js/vue/stores/AlertStore.js"),
|
new HalJsModule("AlertStore", "./js/vue/stores/AlertStore.js"),
|
||||||
|
|
@ -43,7 +19,7 @@ public class JavascriptModules implements HalJavascriptModule {
|
||||||
|
|
||||||
new HalJsModule("EventActionComponent", "./js/vue/components/EventActionComponent.js"),
|
new HalJsModule("EventActionComponent", "./js/vue/components/EventActionComponent.js"),
|
||||||
new HalJsModulePage("EventDetailPageComponent", "./js/vue/components/EventDetailPageComponent.js", "/event/:id"),
|
new HalJsModulePage("EventDetailPageComponent", "./js/vue/components/EventDetailPageComponent.js", "/event/:id"),
|
||||||
new HalJsModulePage("EventOverviewPageComponent", "./js/vue/components/EventOverviewPageComponent.js", "/events"),
|
new HalJsModulePage("EventOverviewPageComponent", "./js/vue/components/EventOverviewPageComponent.js", "/event_overview"),
|
||||||
new HalJsModule("EventTableComponent", "./js/vue/components/EventTableComponent.js"),
|
new HalJsModule("EventTableComponent", "./js/vue/components/EventTableComponent.js"),
|
||||||
new HalJsModule("EventTableRowComponent", "./js/vue/components/EventTableRowComponent.js"),
|
new HalJsModule("EventTableRowComponent", "./js/vue/components/EventTableRowComponent.js"),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,6 @@ hal_core.http_port=8080
|
||||||
## Tibber Plugin
|
## Tibber Plugin
|
||||||
#hal_tibber.token=
|
#hal_tibber.token=
|
||||||
|
|
||||||
## EcoSense plugin
|
|
||||||
#hal_vendor_ecosense.username=
|
|
||||||
#hal_vendor_ecosense.password=
|
|
||||||
|
|
||||||
## Zigbee plugin
|
## Zigbee plugin
|
||||||
#hal_zigbee.com_port=COM4
|
#hal_zigbee.com_port=COM4
|
||||||
#hal_zigbee.dongle=CC2531|CONBEE|XBEE
|
#hal_zigbee.dongle=CC2531|CONBEE|XBEE
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,8 @@ import zutil.parser.json.JSONWriter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.time.LocalDateTime;
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
@ -62,6 +59,8 @@ public class EcoSenseCloudAPIClient {
|
||||||
private static final String USER_POOL_REGION = "us-west-2";
|
private static final String USER_POOL_REGION = "us-west-2";
|
||||||
private static final String API_URL = "https://api.cloud.ecosense.io/api";
|
private static final String API_URL = "https://api.cloud.ecosense.io/api";
|
||||||
|
|
||||||
|
private static SimpleDateFormat DATE_PARSER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); //2025-10-22T14:09:29.691533
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
/** The authenticated Amazon Cognito ID token. */
|
/** The authenticated Amazon Cognito ID token. */
|
||||||
|
|
@ -93,9 +92,7 @@ public class EcoSenseCloudAPIClient {
|
||||||
cognitoClient.close();
|
cognitoClient.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EcoSenseDevice> getDevices() {
|
public EcoSenseDevice getDevices() {
|
||||||
List<EcoSenseDevice> devices = new ArrayList<>();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (ObjectUtil.isEmpty(idToken)) {
|
if (ObjectUtil.isEmpty(idToken)) {
|
||||||
authenticate();
|
authenticate();
|
||||||
|
|
@ -150,13 +147,13 @@ public class EcoSenseCloudAPIClient {
|
||||||
ecoSenseDevice.wifiName = deviceNode.getString("wifi_name");
|
ecoSenseDevice.wifiName = deviceNode.getString("wifi_name");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ecoSenseDevice.lastRadonUpdateTime = LocalDateTime.parse(deviceNode.getString("last_radon_update_time")).toInstant(ZoneOffset.UTC).toEpochMilli();
|
ecoSenseDevice.lastRadonUpdateTime = DATE_PARSER.parse(deviceNode.getString("last_radon_update_time")).getTime();
|
||||||
ecoSenseDevice.lastUpdateTime = LocalDateTime.parse(deviceNode.getString("last_update_time")).toInstant(ZoneOffset.UTC).toEpochMilli();
|
ecoSenseDevice.lastUpdateTime = DATE_PARSER.parse(deviceNode.getString("last_update_time")).getTime();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.WARNING, "Was unable to parse timestamp from EcoSense API.", e);
|
logger.log(Level.WARNING, "Was unable to parse timestamp from EcoSense API.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
devices.add(ecoSenseDevice);
|
return ecoSenseDevice;
|
||||||
}
|
}
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|
@ -165,7 +162,7 @@ public class EcoSenseCloudAPIClient {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return devices;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EcoSenseDevice {
|
public class EcoSenseDevice {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ package se.hal.plugin.vendor.ecosense;
|
||||||
import se.hal.HalContext;
|
import se.hal.HalContext;
|
||||||
import se.hal.HalServer;
|
import se.hal.HalServer;
|
||||||
import se.hal.intf.*;
|
import se.hal.intf.*;
|
||||||
import se.hal.plugin.vendor.ecosense.device.EccoCubeRadonSensorConfig;
|
import se.hal.plugin.vendor.ecosense.device.EccoCubeRadonSensor;
|
||||||
import se.hal.struct.devicedata.RadonSensorData;
|
import se.hal.struct.devicedata.RadonSensorData;
|
||||||
import zutil.ObjectUtil;
|
import zutil.ObjectUtil;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
|
|
@ -77,23 +77,21 @@ public class EcoSenseController implements HalSensorController, Runnable, HalDae
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
if (!ObjectUtil.isEmpty(ecoSenseClient)) {
|
if (!ObjectUtil.isEmpty(ecoSenseClient)) {
|
||||||
List<EcoSenseCloudAPIClient.EcoSenseDevice> apiResponse = ecoSenseClient.getDevices();
|
EcoSenseCloudAPIClient.EcoSenseDevice apiResponse = ecoSenseClient.getDevices();
|
||||||
if (apiResponse.isEmpty()) {
|
if (apiResponse == null) {
|
||||||
logger.info("Received no devices from EcoSense API.");
|
logger.warning("Received empty response from EcoSense API.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (EcoSenseCloudAPIClient.EcoSenseDevice device : apiResponse) {
|
EccoCubeRadonSensor radonSensor = new EccoCubeRadonSensor();
|
||||||
EccoCubeRadonSensorConfig radonSensor = new EccoCubeRadonSensorConfig();
|
radonSensor.setSerialNumber(apiResponse.serialNumber);
|
||||||
radonSensor.setSerialNumber(device.serialNumber);
|
|
||||||
|
|
||||||
RadonSensorData radonSensorData = new RadonSensorData(device.radonLevel, device.lastRadonUpdateTime);
|
RadonSensorData radonSensorData = new RadonSensorData(apiResponse.radonLevel, apiResponse.lastRadonUpdateTime);
|
||||||
|
|
||||||
if (deviceReportListener != null) {
|
if (deviceReportListener != null) {
|
||||||
deviceReportListener.reportReceived(radonSensor, radonSensorData);
|
deviceReportListener.reportReceived(radonSensor, radonSensorData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, "Failed to poll EcoSense API.", e);
|
logger.log(Level.SEVERE, "Failed to poll EcoSense API.", e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,12 @@ import se.hal.intf.HalSensorController;
|
||||||
import se.hal.intf.HalSensorData;
|
import se.hal.intf.HalSensorData;
|
||||||
import se.hal.plugin.vendor.ecosense.EcoSenseController;
|
import se.hal.plugin.vendor.ecosense.EcoSenseController;
|
||||||
import se.hal.struct.devicedata.RadonSensorData;
|
import se.hal.struct.devicedata.RadonSensorData;
|
||||||
import zutil.ui.conf.Configurator;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A sensor that calculate current radon level
|
* A sensor that calculate current radon level
|
||||||
*/
|
*/
|
||||||
public class EccoCubeRadonSensorConfig implements HalSensorConfig {
|
public class EccoCubeRadonSensor implements HalSensorConfig {
|
||||||
|
|
||||||
@Configurator.Configurable(value = "Device serial number")
|
|
||||||
private String serialNumber;
|
private String serialNumber;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -67,20 +63,7 @@ public class EccoCubeRadonSensorConfig implements HalSensorConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean equals(Object o) {
|
public boolean equals(Object obj) {
|
||||||
if (!(o instanceof EccoCubeRadonSensorConfig)) return false;
|
return false;
|
||||||
|
|
||||||
EccoCubeRadonSensorConfig that = (EccoCubeRadonSensorConfig) o;
|
|
||||||
return Objects.equals(serialNumber, that.serialNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(serialNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Serial number: " + serialNumber;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,6 @@
|
||||||
"interfaces": [
|
"interfaces": [
|
||||||
{"se.hal.intf.HalAutostartController": "se.hal.plugin.vendor.ecosense.EcoSenseController"},
|
{"se.hal.intf.HalAutostartController": "se.hal.plugin.vendor.ecosense.EcoSenseController"},
|
||||||
|
|
||||||
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.vendor.ecosense.device.EccoCubeRadonSensorConfig"}
|
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.vendor.ecosense.device.EccoCubeRadonSensor"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,27 +1,3 @@
|
||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Ziver Koc
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':hal-core')
|
implementation project(':hal-core')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,6 @@ import zutil.parser.json.JSONParser;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,6 +44,7 @@ public class TibberAPIClient {
|
||||||
private static final Logger logger = LogUtil.getLogger();
|
private static final Logger logger = LogUtil.getLogger();
|
||||||
|
|
||||||
private static final String TIBBER_API_ENDPOINT = "https://api.tibber.com/v1-beta/gql";
|
private static final String TIBBER_API_ENDPOINT = "https://api.tibber.com/v1-beta/gql";
|
||||||
|
private static SimpleDateFormat DATE_PARSER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); // 2024-12-28T23:00:00.000+01:00
|
||||||
|
|
||||||
private String tibberToken;
|
private String tibberToken;
|
||||||
|
|
||||||
|
|
@ -81,16 +80,16 @@ public class TibberAPIClient {
|
||||||
"consumptionUnit " + // Should be Kwh
|
"consumptionUnit " + // Should be Kwh
|
||||||
"}" +
|
"}" +
|
||||||
"}" +
|
"}" +
|
||||||
// "currentSubscription {" +
|
"currentSubscription {" +
|
||||||
// "priceInfo {" +
|
"priceInfo {" +
|
||||||
// "current {" +
|
"current {" +
|
||||||
// "total " +
|
"total " +
|
||||||
// "energy " +
|
"energy " +
|
||||||
// "tax " +
|
"tax " +
|
||||||
// "startsAt " +
|
"startsAt " +
|
||||||
// "}" +
|
"}" +
|
||||||
// "}" +
|
"}" +
|
||||||
// "}" +
|
"}" +
|
||||||
"}" +
|
"}" +
|
||||||
"}" +
|
"}" +
|
||||||
"}\"" +
|
"}\"" +
|
||||||
|
|
@ -101,7 +100,12 @@ public class TibberAPIClient {
|
||||||
// unitPrice=0.1507875, cost=0.3131856375, from=2024-12-28T21:00:00.000+01:00, consumption=2.077, to=2024-12-28T22:00:00.000+01:00, unitPriceVAT=0.0301575, consumptionUnit=kWh
|
// unitPrice=0.1507875, cost=0.3131856375, from=2024-12-28T21:00:00.000+01:00, consumption=2.077, to=2024-12-28T22:00:00.000+01:00, unitPriceVAT=0.0301575, consumptionUnit=kWh
|
||||||
|
|
||||||
TibberConsumption consumption = new TibberConsumption();
|
TibberConsumption consumption = new TibberConsumption();
|
||||||
consumption.timestamp = LocalDateTime.parse(data.getString("from")).toInstant(ZoneOffset.UTC).toEpochMilli();
|
|
||||||
|
try {
|
||||||
|
consumption.timestamp = DATE_PARSER.parse(data.getString("from")).getTime();
|
||||||
|
} catch (ParseException e) {
|
||||||
|
logger.warning("Was unable to parse timestamp from Tibber API.");
|
||||||
|
}
|
||||||
|
|
||||||
if (data.get("cost") != null) {
|
if (data.get("cost") != null) {
|
||||||
consumption.cost = data.getDouble("cost");
|
consumption.cost = data.getDouble("cost");
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ package se.hal.plugin.vendor.tibber;
|
||||||
import se.hal.HalContext;
|
import se.hal.HalContext;
|
||||||
import se.hal.HalServer;
|
import se.hal.HalServer;
|
||||||
import se.hal.intf.*;
|
import se.hal.intf.*;
|
||||||
import se.hal.plugin.vendor.tibber.device.TibberElectricityCostSensorConfig;
|
import se.hal.plugin.vendor.tibber.device.TibberElectricityCostSensor;
|
||||||
import se.hal.plugin.vendor.tibber.device.TibberElectricityPriceSensorConfig;
|
import se.hal.plugin.vendor.tibber.device.TibberElectricityPriceSensor;
|
||||||
import se.hal.plugin.vendor.tibber.device.TibberPowerConsumptionSensorConfig;
|
import se.hal.plugin.vendor.tibber.device.TibberPowerConsumptionSensor;
|
||||||
import se.hal.struct.devicedata.PriceSensorData;
|
import se.hal.struct.devicedata.PriceSensorData;
|
||||||
import se.hal.util.ListenerUtil;
|
import se.hal.util.ListenerUtil;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
|
|
@ -92,13 +92,13 @@ public class TibberController implements HalSensorController, Runnable, HalDaemo
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consumption.cost > 0) {
|
if (consumption.cost > 0) {
|
||||||
ListenerUtil.callReportReceived(deviceListeners, new TibberElectricityCostSensorConfig(), new PriceSensorData(consumption.cost, consumption.timestamp));
|
ListenerUtil.callReportReceived(deviceListeners, new TibberElectricityCostSensor(), new PriceSensorData(consumption.cost, consumption.timestamp));
|
||||||
}
|
}
|
||||||
if (consumption.unitPriceVAT > 0) {
|
if (consumption.unitPriceVAT > 0) {
|
||||||
ListenerUtil.callReportReceived(deviceListeners, new TibberElectricityPriceSensorConfig(), new PriceSensorData(consumption.unitPriceVAT, consumption.timestamp));
|
ListenerUtil.callReportReceived(deviceListeners, new TibberElectricityPriceSensor(), new PriceSensorData(consumption.unitPriceVAT, consumption.timestamp));
|
||||||
}
|
}
|
||||||
if (consumption.consumption > 0) {
|
if (consumption.consumption > 0) {
|
||||||
ListenerUtil.callReportReceived(deviceListeners, new TibberPowerConsumptionSensorConfig(), new PriceSensorData(consumption.consumption, consumption.timestamp));
|
ListenerUtil.callReportReceived(deviceListeners, new TibberPowerConsumptionSensor(), new PriceSensorData(consumption.consumption, consumption.timestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import se.hal.struct.devicedata.CostSensorData;
|
||||||
/**
|
/**
|
||||||
* A sensor that calculate current electricity bil
|
* A sensor that calculate current electricity bil
|
||||||
*/
|
*/
|
||||||
public class TibberElectricityCostSensorConfig implements HalSensorConfig {
|
public class TibberElectricityCostSensor implements HalSensorConfig {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -59,6 +59,6 @@ public class TibberElectricityCostSensorConfig implements HalSensorConfig {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return obj instanceof TibberElectricityCostSensorConfig;
|
return obj instanceof TibberElectricityCostSensor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ import se.hal.struct.devicedata.PriceSensorData;
|
||||||
/**
|
/**
|
||||||
* A sensor that shows the price of electricity at a specific time
|
* A sensor that shows the price of electricity at a specific time
|
||||||
*/
|
*/
|
||||||
public class TibberElectricityPriceSensorConfig implements HalSensorConfig {
|
public class TibberElectricityPriceSensor implements HalSensorConfig {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -59,6 +59,6 @@ public class TibberElectricityPriceSensorConfig implements HalSensorConfig {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return obj instanceof TibberElectricityPriceSensorConfig;
|
return obj instanceof TibberElectricityPriceSensor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -31,7 +31,7 @@ import se.hal.struct.devicedata.PowerConsumptionSensorData;
|
||||||
import se.hal.plugin.vendor.tibber.TibberController;
|
import se.hal.plugin.vendor.tibber.TibberController;
|
||||||
|
|
||||||
|
|
||||||
public class TibberPowerConsumptionSensorConfig implements HalSensorConfig {
|
public class TibberPowerConsumptionSensor implements HalSensorConfig {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -56,6 +56,6 @@ public class TibberPowerConsumptionSensorConfig implements HalSensorConfig {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return obj instanceof TibberPowerConsumptionSensorConfig;
|
return obj instanceof TibberPowerConsumptionSensor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
"interfaces": [
|
"interfaces": [
|
||||||
{"se.hal.intf.HalAutostartController": "se.hal.plugin.vendor.tibber.TibberController"},
|
{"se.hal.intf.HalAutostartController": "se.hal.plugin.vendor.tibber.TibberController"},
|
||||||
|
|
||||||
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.vendor.tibber.device.TibberElectricityCostSensorConfig"},
|
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.vendor.tibber.device.TibberElectricityCostSensor"},
|
||||||
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.vendor.tibber.device.TibberElectricityPriceSensorConfig"},
|
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.vendor.tibber.device.TibberElectricityPriceSensor"},
|
||||||
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.vendor.tibber.device.TibberPowerConsumptionSensorConfig"}
|
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.vendor.tibber.device.TibberPowerConsumptionSensor"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue