diff --git a/README.md b/README.md
index f2cd324..0cfe941 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,32 @@
-# home-pump
+# Home Pump Monitor
+
+A Java application that reads data from a heat pump via Modbus TCP and sends metrics to InfluxDB.
+
+## Features
+
+- Reads Modbus holding registers from a heat pump
+- Caches readings for 60 seconds to reduce load
+- Sends temperature and pump status data to InfluxDB every minute
+- Configurable via JSON file
+
+## Configuration
+
+Edit `src/main/resources/registryMapping.json` to configure:
+
+- Modbus TCP connection (address, port)
+- InfluxDB connection (address, username, password)
+- List of registers to read with descriptions
+
+## Build and Run
+
+```bash
+mvn clean package
+java -jar target/home-pump-1.0.0.jar
+```
+
+## Requirements
+
+- Java 21
+- Modbus TCP accessible heat pump
+- InfluxDB instance
diff --git a/pom.xml b/pom.xml
index cdf7e9a..ad2a634 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,9 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- org.example
- test.com
- 1.0-SNAPSHOT
+ com.home
+ home-pump
+ 1.0.0
com.fasterxml.jackson.core
@@ -15,12 +15,12 @@
org.mockito
mockito-junit-jupiter
- 4.2.0
+ 5.8.0
test
org.slf4j
- slf4j-api
+ slf4j-simple
1.7.32
@@ -41,13 +41,13 @@
org.junit.jupiter
junit-jupiter
- 5.8.2
+ 5.10.0
test
org.projectlombok
lombok
- 1.18.20
+ 1.18.30
provided
@@ -116,27 +116,15 @@
maven-compiler-plugin
3.8.1
- 11
- 11
+ 21
+ 21
UTF-8
-
- -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
- -J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED
-
true
org.projectlombok
lombok
- 1.18.20
+ 1.18.30
diff --git a/src/main/java/home/automation/Main.java b/src/main/java/home/automation/Main.java
index 62959dd..138495e 100644
--- a/src/main/java/home/automation/Main.java
+++ b/src/main/java/home/automation/Main.java
@@ -1,15 +1,14 @@
package home.automation;
import home.automation.configuration.JsonFileBasedConfiguration;
+import home.automation.configuration.RegistryConfiguration;
import home.automation.configuration.RegistryMapping;
import home.automation.influxdb.InfluxConnection;
import home.automation.influxdb.InfluxMetricModbusSender;
-import home.automation.modbus.ModbusCachedWrapper;
import home.automation.modbus.ModbusConnection;
import home.automation.modbus.ModbusRtuTcpRepository;
import home.automation.modbus.ModbusService;
-import java.time.Duration;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -21,22 +20,17 @@ public class Main {
final JsonFileBasedConfiguration jsonFileBasedConfiguration = new JsonFileBasedConfiguration();
- final List registryMappings = jsonFileBasedConfiguration
+ final RegistryConfiguration config = jsonFileBasedConfiguration
.getConfig()
- .orElseThrow(() -> new Exception("Missing configuration file"))
- .getRegistry_mapping();
+ .orElseThrow(() -> new Exception("Missing configuration file"));
- final InfluxConnection connection = new InfluxConnection(
- jsonFileBasedConfiguration.getConfig()
- .get()
- .getInflux_db()
- );
+ final List registryMappings = config.getRegistry_mapping();
+
+ final InfluxConnection connection = new InfluxConnection(config.getInflux_db());
ModbusService service = new ModbusService(
- new ModbusCachedWrapper(new ModbusRtuTcpRepository(
- new ModbusConnection(
- new JsonFileBasedConfiguration()
- ), registryMappings), Duration.ofSeconds(60))
+ new ModbusRtuTcpRepository(
+ new ModbusConnection(jsonFileBasedConfiguration), registryMappings)
);
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
diff --git a/src/main/java/home/automation/influxdb/InfluxMetricModbusSender.java b/src/main/java/home/automation/influxdb/InfluxMetricModbusSender.java
index f73cbd2..578b3cd 100644
--- a/src/main/java/home/automation/influxdb/InfluxMetricModbusSender.java
+++ b/src/main/java/home/automation/influxdb/InfluxMetricModbusSender.java
@@ -2,6 +2,7 @@ package home.automation.influxdb;
import home.automation.modbus.ModbusService;
import home.automation.modbus.Registry;
+import lombok.extern.slf4j.Slf4j;
import org.influxdb.InfluxDB;
import org.influxdb.dto.Point;
import org.influxdb.dto.Query;
@@ -9,6 +10,7 @@ import org.influxdb.dto.Query;
import java.util.List;
import java.util.concurrent.TimeUnit;
+@Slf4j
public class InfluxMetricModbusSender implements MetricSender, Runnable {
private final ModbusService modbusService;
private final InfluxDB influxConnection;
@@ -26,7 +28,7 @@ public class InfluxMetricModbusSender implements MetricSender, Runnabl
@Override
public boolean send(List metricsToSend) {
- modbusService.readAll().forEach(registry ->
+ metricsToSend.forEach(registry ->
influxConnection.write(
Point.measurement("POMPA")
.addField(registry.getDesc(), Double.valueOf(registry.getValue()))
@@ -40,7 +42,15 @@ public class InfluxMetricModbusSender implements MetricSender, Runnabl
@Override
public void run() {
- final List registries = modbusService.readAll();
- send(registries);
+ try {
+ final List registries = modbusService.readAll();
+ if (!registries.isEmpty()) {
+ send(registries);
+ } else {
+ log.warn("No registries read from Modbus");
+ }
+ } catch (Exception e) {
+ log.error("Error in scheduled Modbus reading", e);
+ }
}
}
diff --git a/src/main/java/home/automation/modbus/ModbusRtuTcpRepository.java b/src/main/java/home/automation/modbus/ModbusRtuTcpRepository.java
index 5c6df86..c0a0120 100644
--- a/src/main/java/home/automation/modbus/ModbusRtuTcpRepository.java
+++ b/src/main/java/home/automation/modbus/ModbusRtuTcpRepository.java
@@ -6,6 +6,7 @@ import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import home.automation.configuration.RegistryMapping;
+import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
@@ -14,10 +15,9 @@ import java.util.stream.Collectors;
import static com.serotonin.modbus4j.code.DataType.TWO_BYTE_INT_SIGNED;
+@Slf4j
public class ModbusRtuTcpRepository implements Repository {
- private static final int ZERO = 0;
- private static final int HALF_SHORT_INT = 32768;
private final Connection connection;
private final List registryMappings;
@@ -27,11 +27,7 @@ public class ModbusRtuTcpRepository implements Repository {
}
private String normalizeRegistryValue(Short value) {
- if (ZERO > value) {
- return String.valueOf(-(value + HALF_SHORT_INT) / 10.0);
- } else {
- return String.valueOf((value / 10.0));
- }
+ return String.valueOf(value / 10.0);
}
@Override
@@ -39,14 +35,14 @@ public class ModbusRtuTcpRepository implements Repository {
BatchRead requestBatchRead = new BatchRead<>();
registryMappings.forEach(elem -> requestBatchRead.addLocator(
elem.getRegistry(),
- BaseLocator.holdingRegister(1, elem.getRegistry(), TWO_BYTE_INT_SIGNED)
+ BaseLocator.holdingRegister(1, elem.getRegistry() - 1, TWO_BYTE_INT_SIGNED)
));
try {
final BatchResults send = connection.getConnection().send(requestBatchRead);
return registryMappings.stream()
.map(mapping -> {
- final Double doubleValue = send.getDoubleValue(mapping.getRegistry());
+ final Double doubleValue = send.getDoubleValue(mapping.getRegistry()) / 10.0;
return Registry.builder()
.registry(mapping.getRegistry())
.value(doubleValue.toString())
@@ -55,7 +51,7 @@ public class ModbusRtuTcpRepository implements Repository {
}
).collect(Collectors.toList());
} catch (Exception e) {
- e.printStackTrace();
+ log.error("Cannot get all registries due to: ", e);
return Collections.emptyList();
}
}
@@ -72,13 +68,14 @@ public class ModbusRtuTcpRepository implements Repository {
.desc(mapping.getDesc())
.build();
} catch (Exception e) {
+ log.error("Cannot get registry {} due to: ", register, e);
throw new RuntimeException(e.getMessage());
}
}
@NotNull
private ReadHoldingRegistersResponse getRegistry(int register) throws Exception {
- ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(1, register, 1);
+ ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(1, register - 1, 1);
ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) connection.getConnection().send(request);
if (response.isException()) {
diff --git a/src/main/resources/registryMapping.json b/src/main/resources/registryMapping.json
index 5fc24ea..a5ef8ef 100644
--- a/src/main/resources/registryMapping.json
+++ b/src/main/resources/registryMapping.json
@@ -79,10 +79,6 @@
"registry": 4119,
"desc": "WyCHL"
},
- {
- "registry": 4116,
- "desc": "Pompa CCW"
- },
{
"registry": 4116,
"desc": "Pompa CCW"
diff --git a/src/test/java/home/automation/modbus/ModbusCachedWrapperTests.java b/src/test/java/home/automation/modbus/ModbusCachedWrapperTests.java
deleted file mode 100644
index dde43d0..0000000
--- a/src/test/java/home/automation/modbus/ModbusCachedWrapperTests.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package home.automation.modbus;
-
-import org.junit.jupiter.api.Test;
-
-import java.time.Duration;
-import java.util.List;
-
-import static java.lang.Thread.sleep;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-
-class ModbusCachedWrapperTests {
-
- public static final int MAX_TESTS = 10;
- private final RepositoryInMemory repository = new RepositoryInMemory();
- private final ModbusCachedWrapper cachedRepository = new ModbusCachedWrapper(repository, Duration.ofSeconds(1));
-
- @Test
- public void givenRepositoryInMemoryAndCacheDurationOf1Seconds_WhenGetRegistryFromCachedRepository2times_ThenValuesAreTheSame() {
- final Registry firstTry = cachedRepository.get(4065);
- final Registry secondTry = cachedRepository.get(4065);
- assert (firstTry.getValue().equals(secondTry.getValue()));
- }
-
- @Test
- public void givenRepositoryInMemoryAndCacheDurationOf1Seconds_WhenGetRegistryFromCachedRepository2timesWithDelayMoreThan1Sec_ThenValuesAreTheDifferent() throws InterruptedException {
- final Registry firstTry = cachedRepository.get(4065);
- final long currentTimeMillis = System.currentTimeMillis();
- final long desireTimeoutInMs = 1000;
- while (desireTimeoutInMs > System.currentTimeMillis() - currentTimeMillis) {
- sleep(510);
- }
- final Registry secondTry = cachedRepository.get(4065);
- assert (!firstTry.getValue().equals(secondTry.getValue()));
- }
-
- @Test
- public void givenRepositoryInMemoryAndCacheDurationOf3Seconds_WhenGetRegistryFromCachedRepositoryMultipleTimesWithIn3SecondDelay_ThenAllValuesAreTheSame() throws InterruptedException {
- final Registry firstTry = cachedRepository.get(4065);
- final long currentTimeMillis = System.currentTimeMillis();
- final long desireTimeoutInMs = 500;
- int count = 0;
- while (desireTimeoutInMs > System.currentTimeMillis() - currentTimeMillis || count == MAX_TESTS) {
- count++;
- final Registry secondTry = cachedRepository.get(4065);
- assert (firstTry.getValue().equals(secondTry.getValue()));
- }
- }
-
- @Test
- public void givenRepositoryInMemoryAndCacheDurationOf3Seconds_WhenGetAllFromCachedRepository2times_ThenValuesAreTheSame() {
- final List firstTry = cachedRepository.getAll();
- final List secondTry = cachedRepository.getAll();
- assertEquals(firstTry, secondTry);
- }
-
- @Test
- public void givenRepositoryInMemoryAndCacheDurationOf3Seconds_WhenGetAllFromCachedRepository2timesWith4SecondDelay_ThenValuesAreTheDifferent() throws InterruptedException {
- final List firstTry = cachedRepository.getAll();
- final long currentTimeMillis = System.currentTimeMillis();
- final long desireTimeoutInMs = 1000;
- while (desireTimeoutInMs > System.currentTimeMillis() - currentTimeMillis) {
- sleep(510);
- }
- final List secondTry = cachedRepository.getAll();
- assertNotEquals(firstTry, secondTry);
- }
-
- @Test
- public void givenRepositoryInMemoryAndCacheDurationOf3Seconds_WhenGetAllFromCachedRepositoryMultipleTimesWithIn3SecondDelay_ThenAllValuesAreTheSame() throws InterruptedException {
- final List firstTry = cachedRepository.getAll();
- final long currentTimeMillis = System.currentTimeMillis();
- final long desireTimeoutInMs = 500;
- int count = 0;
- while (desireTimeoutInMs > System.currentTimeMillis() - currentTimeMillis || count == MAX_TESTS) {
- count++;
- final List secondTry = cachedRepository.getAll();
- assertEquals(firstTry, secondTry);
- }
- }
-}
\ No newline at end of file
diff --git a/src/test/java/home/automation/modbus/ModbusRtuTcpRepositoryTests.java b/src/test/java/home/automation/modbus/ModbusRtuTcpRepositoryTests.java
index 2639a7e..0bbe335 100644
--- a/src/test/java/home/automation/modbus/ModbusRtuTcpRepositoryTests.java
+++ b/src/test/java/home/automation/modbus/ModbusRtuTcpRepositoryTests.java
@@ -49,7 +49,7 @@ class ModbusRtuTcpRepositoryTests {
@Test
public void givenModbusRepositoryWhenGetRegistryIsNegativeValueIsNormalizedAndReturned() throws Exception {
when(response.isException()).thenReturn(false);
- short[] returnValue = {parseShort("-32668")};
+ short[] returnValue = {parseShort("-100")};
when(response.getShortData()).thenReturn(returnValue);
when(modbusMaster.send(any(ModbusRequest.class))).thenReturn(response);
assert ("-10.0".equals(repository.get(4201).getValue()));