diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/DeviceStateRepository.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/DeviceStateRepository.kt index a63776d..51442cc 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/DeviceStateRepository.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/DeviceStateRepository.kt @@ -43,4 +43,6 @@ interface DeviceStateRepository { fun updateTargetTemperature(deviceId: DeviceId, temperature: Double) + fun updateLockObject(deviceId: DeviceId, locked: Boolean) + } diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/FileDeviceRepository.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/FileDeviceRepository.kt index a8d9412..1fe33a6 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/FileDeviceRepository.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/FileDeviceRepository.kt @@ -28,7 +28,6 @@ import de.stefan_oltmann.smarthome.server.model.GroupAddressType import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.File -import java.lang.Exception const val DEVICES_FILE_NAME = "devices.json" @@ -96,6 +95,7 @@ class FileDeviceRepository : DeviceRepository { device.gaPercentageStatus -> return device to GroupAddressType.PERCENTAGE_STATUS device.gaCurrentTemperature -> return device to GroupAddressType.CURRENT_TEMPERATURE device.gaTargetTemperature -> return device to GroupAddressType.TARGET_TEMPERATURE + device.gaLockObject -> return device to GroupAddressType.LOCK_OBJECT } } diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/FileDeviceStateRepository.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/FileDeviceStateRepository.kt index 8db659d..b845d64 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/FileDeviceStateRepository.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/FileDeviceStateRepository.kt @@ -139,4 +139,21 @@ class FileDeviceStateRepository : DeviceStateRepository { append = true ) } + + override fun updateLockObject(deviceId: DeviceId, locked: Boolean) { + + getOrCreateDeviceStatus(deviceId).locked = locked + + val millis = System.currentTimeMillis() + + /* Write local object */ + _history.add(DeviceStateHistoryEntry(deviceId, millis, locked = locked)) + + /* Persist */ + csvWriter.writeAll( + rows = listOf(listOf(deviceId.value, millis, "locked", if (locked) 1 else 0)), + targetFileName = "$DATA_DIR_NAME/$HISTORY_CSV", + append = true + ) + } } diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/InfluxDbDeviceStateRepository.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/InfluxDbDeviceStateRepository.kt index ace0f8f..1ae7b22 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/InfluxDbDeviceStateRepository.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/data/impl/InfluxDbDeviceStateRepository.kt @@ -162,6 +162,24 @@ class InfluxDbDeviceStateRepository( runBlocking { client.getWriteKotlinApi().writePoint(point) } } + override fun updateLockObject(deviceId: DeviceId, locked: Boolean) { + + getOrCreateDeviceStatus(deviceId).locked = locked + + val millis = System.currentTimeMillis() + + /* Write local object */ + _history.add(DeviceStateHistoryEntry(deviceId, millis, locked = locked)) + + /* Persist */ + + val point = Point.measurement(deviceId.value) + .addField("locked", locked) + .time(Instant.now().toEpochMilli(), WritePrecision.MS) + + runBlocking { client.getWriteKotlinApi().writePoint(point) } + } + data class InfluxDbSettings(val url: String, val token: String) companion object { diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/knx/impl/KnxServiceImpl.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/knx/impl/KnxServiceImpl.kt index 85a0013..36ee5b3 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/knx/impl/KnxServiceImpl.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/knx/impl/KnxServiceImpl.kt @@ -44,7 +44,6 @@ import li.pitschmann.knx.core.plugin.audit.FileAuditPlugin import li.pitschmann.knx.core.utils.Sleeper import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.net.http.WebSocketHandshakeException import java.nio.file.Paths import kotlin.concurrent.thread @@ -141,6 +140,10 @@ class KnxServiceImpl( device.gaTargetTemperature?.let { groupAddress -> knxClient.readRequest(GroupAddress.of(groupAddress), READ_TIMEOUT_MS) } + + device.gaLockObject?.let { groupAddress -> + knxClient.readRequest(GroupAddress.of(groupAddress), READ_TIMEOUT_MS) + } } } @@ -148,7 +151,7 @@ class KnxServiceImpl( knxClient.writeRequest( GroupAddress.of(device.gaPowerStateWrite), - powerStateDpt.of(powerState == DevicePowerState.ON) + booleanDpt.of(powerState == DevicePowerState.ON) ) } @@ -236,6 +239,11 @@ class KnxServiceImpl( device.id, groupAddress ) + GroupAddressType.LOCK_OBJECT -> handleLockObjectItem( + item, + device.id, + groupAddress + ) else -> return } } @@ -248,7 +256,7 @@ class KnxServiceImpl( try { - val value = powerStateDpt.of(item.cemi.data).value + val value = booleanDpt.of(item.cemi.data).value val powerState = if (value) DevicePowerState.ON else DevicePowerState.OFF @@ -316,6 +324,23 @@ class KnxServiceImpl( } } + private fun handleLockObjectItem( + item: TunnelingRequestBody, + deviceId: DeviceId, + groupAddress: GroupAddress + ) { + + try { + + val locked = booleanDpt.of(item.cemi.data).value + + deviceStateRepository.updateLockObject(deviceId, locked) + + } catch (ex: DataPointTypeIncompatibleBytesException) { + logger.error(createWrongTypeMessage(groupAddress, item, "DPT1"), ex) + } + } + override fun onError(throwable: Throwable) { logger.error("KNX client error.", throwable) } @@ -337,7 +362,7 @@ class KnxServiceImpl( const val KNX_CONTROL_CHANNEL_PORT = 50011 const val KNX_DATA_CHANNEL_PORT = 50012 - val powerStateDpt: DPT1 = DPT1.BOOL + val booleanDpt: DPT1 = DPT1.BOOL val percentageDpt: DPT5 = DPT5.SCALING val temperatureDpt: DPT9 = DPT9.TEMPERATURE diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/Device.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/Device.kt index a00d798..fc079d1 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/Device.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/Device.kt @@ -32,5 +32,6 @@ data class Device( @JsonInclude(JsonInclude.Include.NON_NULL) val gaPercentageWrite: String? = null, @JsonInclude(JsonInclude.Include.NON_NULL) val gaPercentageStatus: String? = null, @JsonInclude(JsonInclude.Include.NON_NULL) val gaCurrentTemperature: String? = null, - @JsonInclude(JsonInclude.Include.NON_NULL) val gaTargetTemperature: String? = null + @JsonInclude(JsonInclude.Include.NON_NULL) val gaTargetTemperature: String? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) val gaLockObject: String? = null ) diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/DeviceState.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/DeviceState.kt index 9be184d..90a612a 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/DeviceState.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/DeviceState.kt @@ -37,4 +37,7 @@ class DeviceState(val deviceId: DeviceId) { @JsonInclude(JsonInclude.Include.NON_NULL) var targetTemperature: Double? = null + @JsonInclude(JsonInclude.Include.NON_NULL) + var locked: Boolean? = null + } diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/DeviceStateHistoryEntry.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/DeviceStateHistoryEntry.kt index 29c55e5..b68e254 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/DeviceStateHistoryEntry.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/DeviceStateHistoryEntry.kt @@ -26,5 +26,6 @@ data class DeviceStateHistoryEntry( @JsonInclude(JsonInclude.Include.NON_NULL) val powerState: DevicePowerState? = null, @JsonInclude(JsonInclude.Include.NON_NULL) val percentage: Int? = null, @JsonInclude(JsonInclude.Include.NON_NULL) val currentTemperature: Double? = null, - @JsonInclude(JsonInclude.Include.NON_NULL) val targetTemperature: Double? = null + @JsonInclude(JsonInclude.Include.NON_NULL) val targetTemperature: Double? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) val locked: Boolean? = null ) diff --git a/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/GroupAddressType.kt b/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/GroupAddressType.kt index 422a136..f3686da 100644 --- a/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/GroupAddressType.kt +++ b/src/main/kotlin/de/stefan_oltmann/smarthome/server/model/GroupAddressType.kt @@ -26,5 +26,6 @@ enum class GroupAddressType { PERCENTAGE_STATUS, CURRENT_TEMPERATURE, TARGET_TEMPERATURE, + LOCK_OBJECT }