From 833135f3a508938b336bdf7f146abc1e9cef5da2 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sun, 8 Oct 2023 09:25:00 +0200 Subject: [PATCH 01/42] First iteration --- .code/docker-compose/docker-compose.yml | 17 + DEV NOTES.md | 18 + bridge/pom.xml | 6 + .../cookiecode/rika2mqtt/bridge/Bridge.java | 30 +- .../rika2mqtt/bridge/BridgeTest.java | 8 + plugins-api/README.md | 22 + plugins-api/pom.xml | 29 + .../plugins/api/Rika2MqttPlugin.java | 5 + .../plugins/api/StoveStatusExtension.java | 15 + .../rika2mqtt/plugins/api/model/Auth.java | 41 ++ .../rika2mqtt/plugins/api/model/Controls.java | 76 +++ .../plugins/api/model/ParameterDebug.java | 12 + .../api/model/ParameterErrorCount.java | 12 + .../rika2mqtt/plugins/api/model/Sensors.java | 166 +++++ .../rika2mqtt/plugins/api/model/StoveId.java | 33 + .../plugins/api/model/StoveStatus.java | 46 ++ .../plugins/api/model/UpdatableControls.java | 74 ++ plugins-internal/pom.xml | 94 +++ .../internal/Rika2MqttPluginManager.java | 54 ++ .../event/PolledStoveStatusEvent.java | 13 + .../internal/event/Rika2MqttPluginEvent.java | 3 + .../internal/mapper/ControlsMapper.java | 12 + .../internal/mapper/SensorsMapper.java | 78 +++ .../internal/mapper/StoveStatusMapper.java | 13 + .../internal/mapper/SensorsMapperTest.java | 640 ++++++++++++++++++ .../mapper/StoveStatusMapperTest.java | 48 ++ plugins/disabled.txt | 1 + pom.xml | 6 + rika-firenet/pom.xml | 1 - .../rika2mqtt/rika/firenet/model/Sensors.java | 2 + .../rika/firenet/model/StoveStatus.java | 2 + .../firenet/model/StoveStatusSerdeTest.java | 3 +- rika2mqtt-example-plugin/pom.xml | 96 +++ .../plugins/example/ExampleHook.java | 14 + .../plugins/example/ExamplePlugin.java | 19 + rika2mqtt-flux-metrics-plugin/pom.xml | 163 +++++ .../metrics/Rika2MqttInfluxMetricsPlugin.java | 53 ++ .../influxdb/metrics/StoveStatusHook.java | 218 ++++++ .../src/main/resources/application.conf | 69 ++ 39 files changed, 2208 insertions(+), 4 deletions(-) create mode 100644 DEV NOTES.md create mode 100644 plugins-api/README.md create mode 100644 plugins-api/pom.xml create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Rika2MqttPlugin.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/StoveStatusExtension.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Auth.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Controls.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterDebug.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterErrorCount.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Sensors.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveId.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveStatus.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/UpdatableControls.java create mode 100644 plugins-internal/pom.xml create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/Rika2MqttPluginManager.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/PolledStoveStatusEvent.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/Rika2MqttPluginEvent.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/ControlsMapper.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapper.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapper.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapperTest.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapperTest.java create mode 100644 plugins/disabled.txt create mode 100644 rika2mqtt-example-plugin/pom.xml create mode 100644 rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java create mode 100644 rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java create mode 100644 rika2mqtt-flux-metrics-plugin/pom.xml create mode 100644 rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java create mode 100644 rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java create mode 100644 rika2mqtt-flux-metrics-plugin/src/main/resources/application.conf diff --git a/.code/docker-compose/docker-compose.yml b/.code/docker-compose/docker-compose.yml index 2d65b821..d549dbaa 100644 --- a/.code/docker-compose/docker-compose.yml +++ b/.code/docker-compose/docker-compose.yml @@ -30,6 +30,23 @@ services: # - MQTT_TELEMETRY_REPORT_TOPIC_NAME=tele/rika2mqtt + influxdb: + container_name: influxdb + image: influxdb:2.7 + ports: + - 8086:8086 +# volumes: +# - $PWD/data:/var/lib/influxdb2 +# - $PWD/config:/etc/influxdb2 + environment: + - DOCKER_INFLUXDB_INIT_MODE=setup + - DOCKER_INFLUXDB_INIT_USERNAME=admin + - DOCKER_INFLUXDB_INIT_PASSWORD=adminadmin + - DOCKER_INFLUXDB_INIT_ORG=my-org + - DOCKER_INFLUXDB_INIT_RETENTION=1w + - DOCKER_INFLUXDB_INIT_BUCKET=rika2mqtt + - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=admin-token + volumes: data: name: "mqtt-broker-data" diff --git a/DEV NOTES.md b/DEV NOTES.md new file mode 100644 index 00000000..032e6a32 --- /dev/null +++ b/DEV NOTES.md @@ -0,0 +1,18 @@ +TODO: +* open a PR to PF4J there is an issue: + + + dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.Rika2MqttInfluxMetricsPlugin + influx-metrics-plugin + 0.0.1 + 2.0.0 + Export RIKA stoves values to InfluxDB. + Sebastien Vermeille + MIT + # Why am I forced to define this... nothing should mean = empty by default it suppose it is x,y,z open a PR to fix it + + +If I do not declare any plugin dependencies entry, when the plugin is loaded it fails saying there is not plugin id. + +Declaring it empty might be a wish but then the error is wrong +IMHO should not be forced to declare dependencies if there are none. (See with authors) diff --git a/bridge/pom.xml b/bridge/pom.xml index 002a3593..fefc8d66 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -123,6 +123,12 @@ compile 1.1.0 + + dev.cookiecode + plugins-internal + 1.1.0 + compile + org.glassfish.expressly diff --git a/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java b/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java index b02da1e6..69477d5b 100644 --- a/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java +++ b/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java @@ -25,6 +25,9 @@ import com.google.common.annotations.VisibleForTesting; import com.google.gson.Gson; import dev.cookiecode.rika2mqtt.bridge.misc.EmailObfuscator; +import dev.cookiecode.rika2mqtt.plugins.internal.Rika2MqttPluginManager; +import dev.cookiecode.rika2mqtt.plugins.internal.event.PolledStoveStatusEvent; +import dev.cookiecode.rika2mqtt.plugins.internal.mapper.StoveStatusMapper; import dev.cookiecode.rika2mqtt.rika.firenet.RikaFirenetService; import dev.cookiecode.rika2mqtt.rika.firenet.exception.CouldNotAuthenticateToRikaFirenetException; import dev.cookiecode.rika2mqtt.rika.firenet.exception.InvalidStoveIdException; @@ -43,6 +46,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.flogger.Flogger; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -76,6 +80,11 @@ public class Bridge { private final EmailObfuscator emailObfuscator; private final Gson gson; + private final StoveStatusMapper stoveStatusMapper; + + private final Rika2MqttPluginManager pluginManager; + + private final ApplicationEventPublisher applicationEventPublisher; private final List stoveIds = new ArrayList<>(); @@ -85,6 +94,7 @@ void init() { initStoves(rikaFirenetService.getStoves()); printStartupMessages(); + pluginManager.start(); publishToMqtt(); } @@ -123,7 +133,25 @@ void publishToMqtt() { try { status = rikaFirenetService.getStatus(stoveId); - mqttService.publish(gson.toJson(status)); + final var jsonStatus = gson.toJson(status); + mqttService.publish(jsonStatus); + + applicationEventPublisher.publishEvent( + PolledStoveStatusEvent.builder() + .stoveStatus( + stoveStatusMapper.toApiStoveStatus(status) + // RIKA API data structure has some drawback, the mapper address + // it by adding some convenience for plugin developers. (i.e: + // error0 error1 -> errors[]) + // gson.fromJson( + // jsonStatus, + // + // dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus + // .class) // TODO: a bit a hack mapstruct + // would help having something + // // nicer I believe + ) + .build()); } catch (InvalidStoveIdException e) { // TODO: could occurs if a stove is added later (after deployment of this rika2mqtt // instance, might be valuable to perform a reload of stoves "periodically") -> should diff --git a/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java b/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java index ae764a26..9c66a358 100644 --- a/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java +++ b/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java @@ -36,6 +36,8 @@ import com.google.gson.Gson; import dev.cookiecode.rika2mqtt.bridge.misc.EmailObfuscator; +import dev.cookiecode.rika2mqtt.plugins.internal.Rika2MqttPluginManager; +import dev.cookiecode.rika2mqtt.plugins.internal.mapper.StoveStatusMapper; import dev.cookiecode.rika2mqtt.rika.firenet.RikaFirenetService; import dev.cookiecode.rika2mqtt.rika.firenet.model.StoveId; import dev.cookiecode.rika2mqtt.rika.mqtt.MqttService; @@ -52,6 +54,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.context.ApplicationEventPublisher; /** * Test class @@ -66,6 +69,11 @@ class BridgeTest { @Mock MqttService mqttService; @Mock EmailObfuscator emailObfuscator; @Mock Gson gson; + @Mock StoveStatusMapper stoveStatusMapper; + + @Mock Rika2MqttPluginManager pluginManager; + + @Mock ApplicationEventPublisher applicationEventPublisher; @InjectMocks @Spy Bridge bridge; @BeforeEach diff --git a/plugins-api/README.md b/plugins-api/README.md new file mode 100644 index 00000000..27ec20c0 --- /dev/null +++ b/plugins-api/README.md @@ -0,0 +1,22 @@ +# Module plugins-api + +Expose API that can be extended by 3rd parties plugins to let developers connect and bring extra behaviours to rika2mqtt. + +## Architecture of this module + +### Responsibilities + +Loads external plugins at startup +Ensure provides some entry points extendable by plugins. + +### Opinionated choices + +**Question:** Why PF4J ? + +**Answer:** I did some projects with it and it is actuvely maintained. I really like PF4J is very easy to use, +no XML configuration and let the job done! + +### How does it work ? + +https://pf4j.org/ + diff --git a/plugins-api/pom.xml b/plugins-api/pom.xml new file mode 100644 index 00000000..09377660 --- /dev/null +++ b/plugins-api/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + dev.cookiecode + rika2mqtt-parent + 1.1.0 + + + plugins-api + + + ${basedir}/.. + ${java.sdk.version} + ${java.sdk.version} + ${source.encoding} + + + + + + org.pf4j + pf4j + ${pf4j.version} + + + diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Rika2MqttPlugin.java new file mode 100644 index 00000000..2647b288 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Rika2MqttPlugin.java @@ -0,0 +1,5 @@ +package dev.cookiecode.rika2mqtt.plugins.api; + +import org.pf4j.Plugin; + +public class Rika2MqttPlugin extends Plugin {} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/StoveStatusExtension.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/StoveStatusExtension.java new file mode 100644 index 00000000..c443c6e3 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/StoveStatusExtension.java @@ -0,0 +1,15 @@ +package dev.cookiecode.rika2mqtt.plugins.api; + +import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import org.pf4j.ExtensionPoint; + +public interface StoveStatusExtension extends ExtensionPoint { + + /** + * RIKA stove status is regularly polled by Rika2Mqtt. Each time a scheduled poll succeed this + * hook will be invoked and forwarded to plugins. + * + * @param stoveStatus the status retrieved from rika-firenet + */ + void onPollStoveStatusSucceed(StoveStatus stoveStatus); +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Auth.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Auth.java new file mode 100644 index 00000000..cb3db67a --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Auth.java @@ -0,0 +1,41 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.model; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * @author Sebastien Vermeille + */ +@Builder +@Getter +@ToString +@EqualsAndHashCode +public class Auth { + + private final String email; + private final String password; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Controls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Controls.java new file mode 100644 index 00000000..56c4a571 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Controls.java @@ -0,0 +1,76 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.model; + +import lombok.Builder; +import lombok.Data; + +/** + * @author Sebastien Vermeille + */ +@Data +@Builder +public class Controls { + + private Long revision; + private Boolean onOff; + private Integer operatingMode; + private Integer heatingPower; + private Integer targetTemperature; + private Integer bakeTemperature; + private Boolean ecoMode; + private String heatingTimeMon1; + private String heatingTimeMon2; + private String heatingTimeTue1; + private String heatingTimeTue2; + private String heatingTimeWed1; + private String heatingTimeWed2; + private String heatingTimeThu1; + private String heatingTimeThu2; + private String heatingTimeFri1; + private String heatingTimeFri2; + private String heatingTimeSat1; + private String heatingTimeSat2; + private String heatingTimeSun1; + private String heatingTimeSun2; + private Boolean heatingTimesActiveForComfort; + private Integer setBackTemperature; + private Boolean convectionFan1Active; + private Integer convectionFan1Level; + private Integer convectionFan1Area; + private Boolean convectionFan2Active; + private Integer convectionFan2Level; + private Integer convectionFan2Area; + private Boolean frostProtectionActive; + private Integer frostProtectionTemperature; + private Integer temperatureOffset; + + // @SerializedName("RoomPowerRequest") // for coherence (the rest of the api is using camelCase) + private Integer roomPowerRequest; + + private Integer debug0; + private Integer debug1; + private Integer debug2; + private Integer debug3; + private Integer debug4; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterDebug.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterDebug.java new file mode 100644 index 00000000..924e1ca7 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterDebug.java @@ -0,0 +1,12 @@ +package dev.cookiecode.rika2mqtt.plugins.api.model; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ParameterDebug { + + private int number; + private int value; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterErrorCount.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterErrorCount.java new file mode 100644 index 00000000..da64ec50 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterErrorCount.java @@ -0,0 +1,12 @@ +package dev.cookiecode.rika2mqtt.plugins.api.model; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ParameterErrorCount { + + private int number; + private int value; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Sensors.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Sensors.java new file mode 100644 index 00000000..4d5f9029 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Sensors.java @@ -0,0 +1,166 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.model; + +import java.util.List; +import lombok.Builder; +import lombok.Data; + +/** + * @author Sebastien Vermeille + */ +@Data +@Builder +public class Sensors { + + private Double inputRoomTemperature; + private Integer inputFlameTemperature; + private Integer inputBakeTemperature; + private Integer statusError; + private Integer statusSubError; + private Integer statusWarning; + private Integer statusService; + private Integer outputDischargeMotor; + private Integer outputDischargeCurrent; + + // @SerializedName("outputIDFan") // for coherence (the rest of the api is using camelCase) + private Integer outputIdFan; + + // @SerializedName("outputIDFanTarget") // for coherence (the rest of the api is using camelCase) + private Integer outputIdFanTarget; + + private Integer outputInsertionMotor; + private Integer outputInsertionCurrent; + private Integer outputAirFlaps; + private Integer outputAirFlapsTargetPosition; + private Boolean outputBurnBackFlapMagnet; + private Boolean outputGridMotor; + private Boolean outputIgnition; + private Boolean inputUpperTemperatureLimiter; + private Boolean inputPressureSwitch; + private Integer inputPressureSensor; + private Boolean inputGridContact; + private Boolean inputDoor; + private Boolean inputCover; + private Boolean inputExternalRequest; + private Boolean inputBurnBackFlapSwitch; + private Boolean inputFlueGasFlapSwitch; + private Double inputBoardTemperature; + private Integer inputCurrentStage; + + // @SerializedName("inputTargetStagePID") // for coherence (the rest of the api is using + // camelCase) + private Integer inputTargetStagePid; + + // @SerializedName("inputCurrentStagePID") // for coherence (the rest of the api is using + // camelCase) + private Integer inputCurrentStagePid; + + private Integer statusMainState; + private Integer statusSubState; + private Integer statusWifiStrength; + private Boolean parameterEcoModePossible; + private Integer parameterFabricationNumber; + private Integer parameterStoveTypeNumber; + private Integer parameterLanguageNumber; + private Integer parameterVersionMainBoard; + + // @SerializedName("parameterVersionTFT") // for coherence (the rest of the api is using + // camelCase) + private Integer parameterVersionTft; + + // @SerializedName("parameterVersionWiFi") // for coherence (the rest of the api use Wifi not + // WiFi) + private Integer parameterVersionWifi; + + private Integer parameterVersionMainBoardBootLoader; + + // @SerializedName("parameterVersionTFTBootLoader") + // for coherence (the rest of the api is using camelCase) + private Integer parameterVersionTftBootLoader; + + // @SerializedName("parameterVersionWiFiBootLoader") + // for coherence (the rest of the api is using camelCase) + private Integer parameterVersionWifiBootLoader; + + private Integer parameterVersionMainBoardSub; + + // @SerializedName("parameterVersionTFTSub") + // for coherence (the rest of the api is using camelCase) + private Integer parameterVersionTftSub; + + // @SerializedName("parameterVersionWiFiSub") + // for coherence (the rest of the api use Wifi not WiFi) + private Integer parameterVersionWifiSub; + + private Integer parameterRuntimePellets; + private Integer parameterRuntimeLogs; + private Integer parameterFeedRateTotal; + private Integer parameterFeedRateService; + private Integer parameterServiceCountdownKg; + private Integer parameterServiceCountdownTime; + private Integer parameterIgnitionCount; + private Integer parameterOnOffCycleCount; + private Integer parameterFlameSensorOffset; + private Integer parameterPressureSensorOffset; + private Integer parameterErrorCount0; + private Integer parameterErrorCount1; + private Integer parameterErrorCount2; + private Integer parameterErrorCount3; + private Integer parameterErrorCount4; + private Integer parameterErrorCount5; + private Integer parameterErrorCount6; + private Integer parameterErrorCount7; + private Integer parameterErrorCount8; + private Integer parameterErrorCount9; + private Integer parameterErrorCount10; + private Integer parameterErrorCount11; + private Integer parameterErrorCount12; + private Integer parameterErrorCount13; + private Integer parameterErrorCount14; + private Integer parameterErrorCount15; + private Integer parameterErrorCount16; + private Integer parameterErrorCount17; + private Integer parameterErrorCount18; + private Integer parameterErrorCount19; + + private List parametersErrorCount; + + private Boolean statusHeatingTimesNotProgrammed; + private Boolean statusFrostStarted; + private Integer parameterSpiralMotorsTuning; + + // @SerializedName("parameterIDFanTuning") // for coherence (the rest of the api is using + // camelCase) + private Integer parameterIdFanTuning; + + private Integer parameterCleanIntervalBig; + private Integer parameterKgTillCleaning; + private Integer parameterDebug0; + private Integer parameterDebug1; + private Integer parameterDebug2; + private Integer parameterDebug3; + private Integer parameterDebug4; + + private List parametersDebug; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveId.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveId.java new file mode 100644 index 00000000..7a4c2b8d --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveId.java @@ -0,0 +1,33 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.model; + +/** + * @author Sebastien Vermeille + */ +public record StoveId(Long id) { + + public static StoveId of(Long id) { + return new StoveId(id); + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveStatus.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveStatus.java new file mode 100644 index 00000000..309b2b43 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveStatus.java @@ -0,0 +1,46 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.model; + +import lombok.Data; + +/** + * @author Sebastien Vermeille + */ +@Data +public class StoveStatus { + + private String name; + + // @SerializedName( + // value = "stoveId", + // alternate = {"stoveID"}) // for coherence (the rest of the api is using camelCase) + private Long stoveId; + + private Long lastSeenMinutes; + private Long lastConfirmedRevision; + private String oem; + private String stoveType; + private Sensors sensors; + private Controls controls; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/UpdatableControls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/UpdatableControls.java new file mode 100644 index 00000000..813fd88e --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/UpdatableControls.java @@ -0,0 +1,74 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.model; + +import static lombok.AccessLevel.NONE; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +public class UpdatableControls { + + private Long revision; + /** Fields below (some, or all) can be sent using MQTT command to pilot RIKA. */ + private Integer operatingMode; + + private Integer heatingPower; + private Integer targetTemperature; + private Integer bakeTemperature; + private Boolean heatingTimesActiveForComfort; + private Integer setBackTemperature; + private Boolean convectionFan1Active; + private Integer convectionFan1Level; + private Integer convectionFan1Area; + private Boolean convectionFan2Active; + private Integer convectionFan2Level; + private Integer convectionFan2Area; + private Boolean frostProtectionActive; + private Integer frostProtectionTemperature; + private Boolean onOff; + + // @FieldNameConstants lombok annotation would generate this. Unfortunately at the moment it + // generates issues to generate + // javadoc: error: cannot find symbol + @NoArgsConstructor(access = NONE) + public static final class Fields { + public static final String REVISION = "revision"; + public static final String OPERATING_MODE = "operatingMode"; + public static final String HEATING_POWER = "heatingPower"; + public static final String TARGET_TEMPERATURE = "targetTemperature"; + public static final String BAKE_TEMPERATURE = "bakeTemperature"; + public static final String HEATING_TIMES_ACTIVE_FOR_COMFORT = "heatingTimesActiveForComfort"; + public static final String SET_BACK_TEMPERATURE = "setBackTemperature"; + public static final String CONVECTION_FAN1_ACTIVE = "convectionFan1Active"; + public static final String CONVECTION_FAN1_LEVEL = "convectionFan1Level"; + public static final String CONVECTION_FAN1_AREA = "convectionFan1Area"; + public static final String CONVECTION_FAN2_ACTIVE = "convectionFan2Active"; + public static final String CONVECTION_FAN2_LEVEL = "convectionFan2Level"; + public static final String CONVECTION_FAN2_AREA = "convectionFan2Area"; + public static final String FROST_PROTECTION_ACTIVE = "frostProtectionActive"; + public static final String FROST_PROTECTION_TEMPERATURE = "frostProtectionTemperature"; + public static final String ON_OFF = "onOff"; + } +} diff --git a/plugins-internal/pom.xml b/plugins-internal/pom.xml new file mode 100644 index 00000000..fe0a105f --- /dev/null +++ b/plugins-internal/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + dev.cookiecode + rika2mqtt-parent + 1.1.0 + + + plugins-internal + + + ${basedir}/.. + ${java.sdk.version} + ${java.sdk.version} + ${source.encoding} + + + + + org.pf4j + pf4j + ${pf4j.version} + + + dev.cookiecode + plugins-api + 1.1.0 + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + org.assertj + assertj-core + ${assertj.version} + test + + + dev.cookiecode + rika2mqtt-rika-firenet + 1.1.0 + compile + + + + + + + spring-boot-maven-plugin + + true + + org.springframework.boot + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.sdk.version} + ${java.sdk.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + true + + + -Amapstruct.defaultComponentModel=spring + + + + + + + diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/Rika2MqttPluginManager.java new file mode 100644 index 00000000..1f644bdf --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/Rika2MqttPluginManager.java @@ -0,0 +1,54 @@ +package dev.cookiecode.rika2mqtt.plugins.internal; + +import dev.cookiecode.rika2mqtt.plugins.api.StoveStatusExtension; +import dev.cookiecode.rika2mqtt.plugins.internal.event.PolledStoveStatusEvent; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.flogger.Flogger; +import org.pf4j.DefaultPluginManager; +import org.pf4j.PluginManager; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +// TODO: add javadoc +@Service +@RequiredArgsConstructor +@Flogger +public class Rika2MqttPluginManager { + + private PluginManager pluginManager; + + @PostConstruct + void init() { + pluginManager = new DefaultPluginManager(); + } + + public void start() { + log.atInfo().log("Plugin manager starting ..."); + pluginManager.loadPlugins(); + pluginManager.startPlugins(); + } + + @EventListener + public void handlePolledStoveStatusEvent(PolledStoveStatusEvent event) { + System.out.println("HOOK FOR PLUGINS received a polled status event!"); + var extensions = pluginManager.getExtensions(StoveStatusExtension.class); + + if (extensions.isEmpty()) { + + log.atSevere().log(); + log.atSevere().log(); + log.atSevere().log(); + log.atSevere().log("NO EXTENSION POINT FOUND for StoveStatusExtension!"); + log.atSevere().log(); + log.atSevere().log(); + log.atSevere().log(); + } + + extensions.forEach( + stoveStatusExtension -> { + log.atInfo().log("INVOKE EXTENSION POINT"); + stoveStatusExtension.onPollStoveStatusSucceed(event.getStoveStatus()); + }); + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/PolledStoveStatusEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/PolledStoveStatusEvent.java new file mode 100644 index 00000000..41f86ede --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/PolledStoveStatusEvent.java @@ -0,0 +1,13 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.event; + +import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import lombok.*; + +@RequiredArgsConstructor +@Getter +@ToString +@EqualsAndHashCode +@Builder +public class PolledStoveStatusEvent implements Rika2MqttPluginEvent { + private final StoveStatus stoveStatus; +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/Rika2MqttPluginEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/Rika2MqttPluginEvent.java new file mode 100644 index 00000000..ab0a1b87 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/Rika2MqttPluginEvent.java @@ -0,0 +1,3 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.event; + +public interface Rika2MqttPluginEvent {} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/ControlsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/ControlsMapper.java new file mode 100644 index 00000000..65ceefb5 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/ControlsMapper.java @@ -0,0 +1,12 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.mapper; + +import static org.mapstruct.ReportingPolicy.IGNORE; + +import dev.cookiecode.rika2mqtt.plugins.api.model.Controls; +import lombok.NonNull; +import org.mapstruct.Mapper; + +@Mapper(unmappedTargetPolicy = IGNORE) // ignore as we are using a map +public interface ControlsMapper { + Controls toApiControls(@NonNull dev.cookiecode.rika2mqtt.rika.firenet.model.Controls controls); +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapper.java new file mode 100644 index 00000000..7e7e4710 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapper.java @@ -0,0 +1,78 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.mapper; + +import static org.mapstruct.ReportingPolicy.IGNORE; + +import dev.cookiecode.rika2mqtt.plugins.api.model.ParameterDebug; +import dev.cookiecode.rika2mqtt.plugins.api.model.ParameterErrorCount; +import dev.cookiecode.rika2mqtt.plugins.api.model.Sensors; +import java.util.ArrayList; +import java.util.List; +import lombok.NonNull; +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; + +@Mapper(unmappedTargetPolicy = IGNORE) // ignore as we are using a map +public interface SensorsMapper { + + Sensors toApiSensors(@NonNull dev.cookiecode.rika2mqtt.rika.firenet.model.Sensors sensors); + + @AfterMapping + default void afterMapping( + dev.cookiecode.rika2mqtt.rika.firenet.model.Sensors source, + @MappingTarget Sensors.SensorsBuilder target) { + + // wrap debug properties as a collection + List debugList = new ArrayList<>(); + debugList.add(ParameterDebug.builder().number(0).value(source.getParameterDebug0()).build()); + debugList.add(ParameterDebug.builder().number(1).value(source.getParameterDebug1()).build()); + debugList.add(ParameterDebug.builder().number(2).value(source.getParameterDebug2()).build()); + debugList.add(ParameterDebug.builder().number(3).value(source.getParameterDebug3()).build()); + debugList.add(ParameterDebug.builder().number(4).value(source.getParameterDebug4()).build()); + target.parametersDebug(debugList); + + // wrap error count properties as a collection + List errorCountList = new ArrayList<>(); + errorCountList.add( + ParameterErrorCount.builder().number(0).value(source.getParameterErrorCount0()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(1).value(source.getParameterErrorCount1()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(2).value(source.getParameterErrorCount2()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(3).value(source.getParameterErrorCount3()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(4).value(source.getParameterErrorCount4()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(5).value(source.getParameterErrorCount5()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(6).value(source.getParameterErrorCount6()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(7).value(source.getParameterErrorCount7()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(8).value(source.getParameterErrorCount8()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(9).value(source.getParameterErrorCount9()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(10).value(source.getParameterErrorCount10()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(11).value(source.getParameterErrorCount11()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(12).value(source.getParameterErrorCount12()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(13).value(source.getParameterErrorCount13()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(14).value(source.getParameterErrorCount14()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(15).value(source.getParameterErrorCount15()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(16).value(source.getParameterErrorCount16()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(17).value(source.getParameterErrorCount17()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(18).value(source.getParameterErrorCount18()).build()); + errorCountList.add( + ParameterErrorCount.builder().number(19).value(source.getParameterErrorCount19()).build()); + target.parametersErrorCount(errorCountList); + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapper.java new file mode 100644 index 00000000..21b8bfb7 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapper.java @@ -0,0 +1,13 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.mapper; + +import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import lombok.NonNull; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) // ignore as we are using a map +public interface StoveStatusMapper { + + StoveStatus toApiStoveStatus( + @NonNull dev.cookiecode.rika2mqtt.rika.firenet.model.StoveStatus stoveStatus); +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapperTest.java new file mode 100644 index 00000000..a692bdf1 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapperTest.java @@ -0,0 +1,640 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.mapper; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +import dev.cookiecode.rika2mqtt.rika.firenet.model.Sensors; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = {SensorsMapperImpl.class}) +class SensorsMapperTest { + + @Autowired private SensorsMapper mapper; + + @Test + void toApiSensorsShouldFillAllSensorsPropertiesGivenACompleteRikaFirenetSensorInstance() { + + // GIVEN + Integer inputFlameTemperature = 200; + Integer inputBakeTemperature = 300; + Integer statusError = 1; + Integer statusSubError = 2; + Integer statusWarning = 3; + Integer statusService = 4; + Integer outputDischargeMotor = 5; + Integer outputDischargeCurrent = 6; + Integer outputIdFan = 7; + Integer outputIdFanTarget = 8; + Integer outputInsertionMotor = 9; + Integer outputInsertionCurrent = 10; + Integer outputAirFlaps = 11; + Integer outputAirFlapsTargetPosition = 12; + Boolean outputBurnBackFlapMagnet = true; + Boolean outputGridMotor = false; + Boolean outputIgnition = true; + Boolean inputUpperTemperatureLimiter = true; + Boolean inputPressureSwitch = true; + Integer inputPressureSensor = 13; + Boolean inputGridContact = true; + Boolean inputDoor = true; + Boolean inputCover = true; + Boolean inputExternalRequest = true; + Boolean inputBurnBackFlapSwitch = true; + Integer parameterDebug0 = 44; + Integer parameterDebug1 = 45; + Integer parameterDebug2 = 46; + Integer parameterDebug3 = 47; + Integer parameterDebug4 = 48; + Integer parameterErrorCount0 = 30; + Integer parameterErrorCount1 = 31; + Integer parameterErrorCount2 = 32; + Integer parameterErrorCount3 = 33; + Integer parameterErrorCount4 = 34; + Integer parameterErrorCount5 = 35; + Integer parameterErrorCount6 = 36; + Integer parameterErrorCount7 = 37; + Integer parameterErrorCount8 = 38; + Integer parameterErrorCount9 = 39; + Integer parameterErrorCount10 = 40; + Integer parameterErrorCount11 = 41; + Integer parameterErrorCount12 = 42; + Integer parameterErrorCount13 = 43; + Integer parameterErrorCount14 = 44; + Integer parameterErrorCount15 = 45; + Integer parameterErrorCount16 = 46; + Integer parameterErrorCount17 = 47; + Integer parameterErrorCount18 = 48; + Integer parameterErrorCount19 = 49; + Boolean inputFlueGasFlapSwitch = true; + Double inputBoardTemperature = 20.20; + Integer inputCurrentStage = 12; + Integer statusMainState = 42; + Integer statusSubState = 43; + Integer statusWifiStrength = 100; + Boolean parameterEcoModePossible = true; + Integer parameterFabricationNumber = 12323; + Integer parameterStoveTypeNumber = 46; + Integer parameterLanguageNumber = 2; + Integer parameterVersionMainBoard = 5; + Integer parameterVersionTft = 6; + Integer parameterVersionWifi = 7; + Integer parameterVersionMainBoardBootLoader = 8; + Integer parameterVersionTftBootLoader = 9; + Integer parameterVersionWifiBootLoader = 10; + Integer parameterVersionMainBoardSub = 11; + Integer inputTargetStagePid = 12; + Integer inputCurrentStagePid = 13; + Integer parameterVersionTftSub = 14; + Integer parameterVersionWifiSub = 15; + Integer parameterRuntimePellets = 16; + Integer parameterRuntimeLogs = 17; + Integer parameterFeedRateTotal = 18; + Integer parameterFeedRateService = 19; + Integer parameterServiceCountdownKg = 20; + Integer parameterServiceCountdownTime = 21; + Integer parameterIgnitionCount = 22; + Integer parameterOnOffCycleCount = 23; + Integer parameterFlameSensorOffset = 24; + Integer parameterPressureSensorOffset = 25; + Integer parameterKgTillCleaning = 26; + Integer parameterCleanIntervalBig = 27; + Integer parameterIdFanTuning = 28; + Integer parameterSpiralMotorsTuning = 29; + Boolean statusFrostStarted = true; + Boolean statusHeatingTimesNotProgrammed = true; + Double inputRoomTemperature = 22.4; + Sensors rikaFirenetSensors = + Sensors.builder() + .inputRoomTemperature(inputRoomTemperature) + .inputFlameTemperature(inputFlameTemperature) + .inputBakeTemperature(inputBakeTemperature) + .statusError(statusError) + .statusSubError(statusSubError) + .statusWarning(statusWarning) + .statusService(statusService) + .outputDischargeMotor(outputDischargeMotor) + .outputDischargeCurrent(outputDischargeCurrent) + .outputIdFan(outputIdFan) + .outputIdFanTarget(outputIdFanTarget) + .outputInsertionMotor(outputInsertionMotor) + .outputInsertionCurrent(outputInsertionCurrent) + .outputAirFlaps(outputAirFlaps) + .outputAirFlapsTargetPosition(outputAirFlapsTargetPosition) + .outputBurnBackFlapMagnet(outputBurnBackFlapMagnet) + .outputGridMotor(outputGridMotor) + .outputIgnition(outputIgnition) + .inputUpperTemperatureLimiter(inputUpperTemperatureLimiter) + .inputPressureSwitch(inputPressureSwitch) + .inputPressureSensor(inputPressureSensor) + .inputGridContact(inputGridContact) + .inputDoor(inputDoor) + .inputCover(inputCover) + .inputExternalRequest(inputExternalRequest) + .inputBurnBackFlapSwitch(inputBurnBackFlapSwitch) + .inputFlueGasFlapSwitch(inputFlueGasFlapSwitch) + .inputBoardTemperature(inputBoardTemperature) + .inputCurrentStage(inputCurrentStage) + .statusMainState(statusMainState) + .statusSubState(statusSubState) + .statusWifiStrength(statusWifiStrength) + .parameterEcoModePossible(parameterEcoModePossible) + .parameterFabricationNumber(parameterFabricationNumber) + .parameterStoveTypeNumber(parameterStoveTypeNumber) + .parameterLanguageNumber(parameterLanguageNumber) + .parameterVersionMainBoard(parameterVersionMainBoard) + .parameterVersionTft(parameterVersionTft) + .parameterVersionWifi(parameterVersionWifi) + .parameterVersionMainBoardBootLoader(parameterVersionMainBoardBootLoader) + .parameterVersionTftBootLoader(parameterVersionTftBootLoader) + .parameterVersionWifiBootLoader(parameterVersionWifiBootLoader) + .parameterVersionMainBoardSub(parameterVersionMainBoardSub) + .inputTargetStagePid(inputTargetStagePid) + .inputCurrentStagePid(inputCurrentStagePid) + .parameterVersionTftSub(parameterVersionTftSub) + .parameterVersionWifiSub(parameterVersionWifiSub) + .parameterRuntimePellets(parameterRuntimePellets) + .parameterRuntimeLogs(parameterRuntimeLogs) + .parameterFeedRateTotal(parameterFeedRateTotal) + .parameterFeedRateService(parameterFeedRateService) + .parameterServiceCountdownKg(parameterServiceCountdownKg) + .parameterServiceCountdownTime(parameterServiceCountdownTime) + .parameterIgnitionCount(parameterIgnitionCount) + .parameterOnOffCycleCount(parameterOnOffCycleCount) + .parameterFlameSensorOffset(parameterFlameSensorOffset) + .parameterPressureSensorOffset(parameterPressureSensorOffset) + .parameterKgTillCleaning(parameterKgTillCleaning) + .parameterCleanIntervalBig(parameterCleanIntervalBig) + .parameterIdFanTuning(parameterIdFanTuning) + .parameterSpiralMotorsTuning(parameterSpiralMotorsTuning) + .statusFrostStarted(statusFrostStarted) + .statusHeatingTimesNotProgrammed(statusHeatingTimesNotProgrammed) + .parameterErrorCount0(parameterErrorCount0) + .parameterErrorCount1(parameterErrorCount1) + .parameterErrorCount2(parameterErrorCount2) + .parameterErrorCount3(parameterErrorCount3) + .parameterErrorCount4(parameterErrorCount4) + .parameterErrorCount5(parameterErrorCount5) + .parameterErrorCount6(parameterErrorCount6) + .parameterErrorCount7(parameterErrorCount7) + .parameterErrorCount8(parameterErrorCount8) + .parameterErrorCount9(parameterErrorCount9) + .parameterErrorCount10(parameterErrorCount10) + .parameterErrorCount11(parameterErrorCount11) + .parameterErrorCount12(parameterErrorCount12) + .parameterErrorCount13(parameterErrorCount13) + .parameterErrorCount14(parameterErrorCount14) + .parameterErrorCount15(parameterErrorCount15) + .parameterErrorCount16(parameterErrorCount16) + .parameterErrorCount17(parameterErrorCount17) + .parameterErrorCount18(parameterErrorCount18) + .parameterErrorCount19(parameterErrorCount19) + .parameterDebug0(parameterDebug0) + .parameterDebug1(parameterDebug1) + .parameterDebug2(parameterDebug2) + .parameterDebug3(parameterDebug3) + .parameterDebug4(parameterDebug4) + .build(); + + // WHEN + var apiSensors = mapper.toApiSensors(rikaFirenetSensors); + + // THEN + assertThat(apiSensors).hasNoNullFieldsOrProperties(); + } + + @Test + void toApiSensorsShouldFillParametersDebugCollectionExtraFieldProperly() { + + // GIVEN + Integer inputFlameTemperature = 200; + Integer inputBakeTemperature = 300; + Integer statusError = 1; + Integer statusSubError = 2; + Integer statusWarning = 3; + Integer statusService = 4; + Integer outputDischargeMotor = 5; + Integer outputDischargeCurrent = 6; + Integer outputIdFan = 7; + Integer outputIdFanTarget = 8; + Integer outputInsertionMotor = 9; + Integer outputInsertionCurrent = 10; + Integer outputAirFlaps = 11; + Integer outputAirFlapsTargetPosition = 12; + Boolean outputBurnBackFlapMagnet = true; + Boolean outputGridMotor = false; + Boolean outputIgnition = true; + Boolean inputUpperTemperatureLimiter = true; + Boolean inputPressureSwitch = true; + Integer inputPressureSensor = 13; + Boolean inputGridContact = true; + Boolean inputDoor = true; + Boolean inputCover = true; + Boolean inputExternalRequest = true; + Boolean inputBurnBackFlapSwitch = true; + Integer parameterDebug0 = 44; + Integer parameterDebug1 = 45; + Integer parameterDebug2 = 46; + Integer parameterDebug3 = 47; + Integer parameterDebug4 = 48; + Integer parameterErrorCount0 = 30; + Integer parameterErrorCount1 = 31; + Integer parameterErrorCount2 = 32; + Integer parameterErrorCount3 = 33; + Integer parameterErrorCount4 = 34; + Integer parameterErrorCount5 = 35; + Integer parameterErrorCount6 = 36; + Integer parameterErrorCount7 = 37; + Integer parameterErrorCount8 = 38; + Integer parameterErrorCount9 = 39; + Integer parameterErrorCount10 = 40; + Integer parameterErrorCount11 = 41; + Integer parameterErrorCount12 = 42; + Integer parameterErrorCount13 = 43; + Integer parameterErrorCount14 = 44; + Integer parameterErrorCount15 = 45; + Integer parameterErrorCount16 = 46; + Integer parameterErrorCount17 = 47; + Integer parameterErrorCount18 = 48; + Integer parameterErrorCount19 = 49; + Boolean inputFlueGasFlapSwitch = true; + Double inputBoardTemperature = 20.20; + Integer inputCurrentStage = 12; + Integer statusMainState = 42; + Integer statusSubState = 43; + Integer statusWifiStrength = 100; + Boolean parameterEcoModePossible = true; + Integer parameterFabricationNumber = 12323; + Integer parameterStoveTypeNumber = 46; + Integer parameterLanguageNumber = 2; + Integer parameterVersionMainBoard = 5; + Integer parameterVersionTft = 6; + Integer parameterVersionWifi = 7; + Integer parameterVersionMainBoardBootLoader = 8; + Integer parameterVersionTftBootLoader = 9; + Integer parameterVersionWifiBootLoader = 10; + Integer parameterVersionMainBoardSub = 11; + Integer inputTargetStagePid = 12; + Integer inputCurrentStagePid = 13; + Integer parameterVersionTftSub = 14; + Integer parameterVersionWifiSub = 15; + Integer parameterRuntimePellets = 16; + Integer parameterRuntimeLogs = 17; + Integer parameterFeedRateTotal = 18; + Integer parameterFeedRateService = 19; + Integer parameterServiceCountdownKg = 20; + Integer parameterServiceCountdownTime = 21; + Integer parameterIgnitionCount = 22; + Integer parameterOnOffCycleCount = 23; + Integer parameterFlameSensorOffset = 24; + Integer parameterPressureSensorOffset = 25; + Integer parameterKgTillCleaning = 26; + Integer parameterCleanIntervalBig = 27; + Integer parameterIdFanTuning = 28; + Integer parameterSpiralMotorsTuning = 29; + Boolean statusFrostStarted = true; + Boolean statusHeatingTimesNotProgrammed = true; + Double inputRoomTemperature = 22.4; + Sensors rikaFirenetSensors = + Sensors.builder() + .inputRoomTemperature(inputRoomTemperature) + .inputFlameTemperature(inputFlameTemperature) + .inputBakeTemperature(inputBakeTemperature) + .statusError(statusError) + .statusSubError(statusSubError) + .statusWarning(statusWarning) + .statusService(statusService) + .outputDischargeMotor(outputDischargeMotor) + .outputDischargeCurrent(outputDischargeCurrent) + .outputIdFan(outputIdFan) + .outputIdFanTarget(outputIdFanTarget) + .outputInsertionMotor(outputInsertionMotor) + .outputInsertionCurrent(outputInsertionCurrent) + .outputAirFlaps(outputAirFlaps) + .outputAirFlapsTargetPosition(outputAirFlapsTargetPosition) + .outputBurnBackFlapMagnet(outputBurnBackFlapMagnet) + .outputGridMotor(outputGridMotor) + .outputIgnition(outputIgnition) + .inputUpperTemperatureLimiter(inputUpperTemperatureLimiter) + .inputPressureSwitch(inputPressureSwitch) + .inputPressureSensor(inputPressureSensor) + .inputGridContact(inputGridContact) + .inputDoor(inputDoor) + .inputCover(inputCover) + .inputExternalRequest(inputExternalRequest) + .inputBurnBackFlapSwitch(inputBurnBackFlapSwitch) + .inputFlueGasFlapSwitch(inputFlueGasFlapSwitch) + .inputBoardTemperature(inputBoardTemperature) + .inputCurrentStage(inputCurrentStage) + .statusMainState(statusMainState) + .statusSubState(statusSubState) + .statusWifiStrength(statusWifiStrength) + .parameterEcoModePossible(parameterEcoModePossible) + .parameterFabricationNumber(parameterFabricationNumber) + .parameterStoveTypeNumber(parameterStoveTypeNumber) + .parameterLanguageNumber(parameterLanguageNumber) + .parameterVersionMainBoard(parameterVersionMainBoard) + .parameterVersionTft(parameterVersionTft) + .parameterVersionWifi(parameterVersionWifi) + .parameterVersionMainBoardBootLoader(parameterVersionMainBoardBootLoader) + .parameterVersionTftBootLoader(parameterVersionTftBootLoader) + .parameterVersionWifiBootLoader(parameterVersionWifiBootLoader) + .parameterVersionMainBoardSub(parameterVersionMainBoardSub) + .inputTargetStagePid(inputTargetStagePid) + .inputCurrentStagePid(inputCurrentStagePid) + .parameterVersionTftSub(parameterVersionTftSub) + .parameterVersionWifiSub(parameterVersionWifiSub) + .parameterRuntimePellets(parameterRuntimePellets) + .parameterRuntimeLogs(parameterRuntimeLogs) + .parameterFeedRateTotal(parameterFeedRateTotal) + .parameterFeedRateService(parameterFeedRateService) + .parameterServiceCountdownKg(parameterServiceCountdownKg) + .parameterServiceCountdownTime(parameterServiceCountdownTime) + .parameterIgnitionCount(parameterIgnitionCount) + .parameterOnOffCycleCount(parameterOnOffCycleCount) + .parameterFlameSensorOffset(parameterFlameSensorOffset) + .parameterPressureSensorOffset(parameterPressureSensorOffset) + .parameterKgTillCleaning(parameterKgTillCleaning) + .parameterCleanIntervalBig(parameterCleanIntervalBig) + .parameterIdFanTuning(parameterIdFanTuning) + .parameterSpiralMotorsTuning(parameterSpiralMotorsTuning) + .statusFrostStarted(statusFrostStarted) + .statusHeatingTimesNotProgrammed(statusHeatingTimesNotProgrammed) + .parameterErrorCount0(parameterErrorCount0) + .parameterErrorCount1(parameterErrorCount1) + .parameterErrorCount2(parameterErrorCount2) + .parameterErrorCount3(parameterErrorCount3) + .parameterErrorCount4(parameterErrorCount4) + .parameterErrorCount5(parameterErrorCount5) + .parameterErrorCount6(parameterErrorCount6) + .parameterErrorCount7(parameterErrorCount7) + .parameterErrorCount8(parameterErrorCount8) + .parameterErrorCount9(parameterErrorCount9) + .parameterErrorCount10(parameterErrorCount10) + .parameterErrorCount11(parameterErrorCount11) + .parameterErrorCount12(parameterErrorCount12) + .parameterErrorCount13(parameterErrorCount13) + .parameterErrorCount14(parameterErrorCount14) + .parameterErrorCount15(parameterErrorCount15) + .parameterErrorCount16(parameterErrorCount16) + .parameterErrorCount17(parameterErrorCount17) + .parameterErrorCount18(parameterErrorCount18) + .parameterErrorCount19(parameterErrorCount19) + .parameterDebug0(parameterDebug0) + .parameterDebug1(parameterDebug1) + .parameterDebug2(parameterDebug2) + .parameterDebug3(parameterDebug3) + .parameterDebug4(parameterDebug4) + .build(); + + // WHEN + var apiSensors = mapper.toApiSensors(rikaFirenetSensors); + + // THEN + assertThat(apiSensors.getParametersDebug()).isNotEmpty(); + assertThat(apiSensors.getParametersDebug()).hasSize(5); + assertThat(apiSensors.getParametersDebug().get(0).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterDebug0()); + assertThat(apiSensors.getParametersDebug().get(1).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterDebug1()); + assertThat(apiSensors.getParametersDebug().get(2).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterDebug2()); + assertThat(apiSensors.getParametersDebug().get(3).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterDebug3()); + assertThat(apiSensors.getParametersDebug().get(4).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterDebug4()); + } + + @Test + void toApiSensorsShouldFillParametersErrorCountCollectionExtraFieldProperly() { + + // GIVEN + Integer inputFlameTemperature = 200; + Integer inputBakeTemperature = 300; + Integer statusError = 1; + Integer statusSubError = 2; + Integer statusWarning = 3; + Integer statusService = 4; + Integer outputDischargeMotor = 5; + Integer outputDischargeCurrent = 6; + Integer outputIdFan = 7; + Integer outputIdFanTarget = 8; + Integer outputInsertionMotor = 9; + Integer outputInsertionCurrent = 10; + Integer outputAirFlaps = 11; + Integer outputAirFlapsTargetPosition = 12; + Boolean outputBurnBackFlapMagnet = true; + Boolean outputGridMotor = false; + Boolean outputIgnition = true; + Boolean inputUpperTemperatureLimiter = true; + Boolean inputPressureSwitch = true; + Integer inputPressureSensor = 13; + Boolean inputGridContact = true; + Boolean inputDoor = true; + Boolean inputCover = true; + Boolean inputExternalRequest = true; + Boolean inputBurnBackFlapSwitch = true; + Integer parameterDebug0 = 44; + Integer parameterDebug1 = 45; + Integer parameterDebug2 = 46; + Integer parameterDebug3 = 47; + Integer parameterDebug4 = 48; + Integer parameterErrorCount0 = 30; + Integer parameterErrorCount1 = 31; + Integer parameterErrorCount2 = 32; + Integer parameterErrorCount3 = 33; + Integer parameterErrorCount4 = 34; + Integer parameterErrorCount5 = 35; + Integer parameterErrorCount6 = 36; + Integer parameterErrorCount7 = 37; + Integer parameterErrorCount8 = 38; + Integer parameterErrorCount9 = 39; + Integer parameterErrorCount10 = 40; + Integer parameterErrorCount11 = 41; + Integer parameterErrorCount12 = 42; + Integer parameterErrorCount13 = 43; + Integer parameterErrorCount14 = 44; + Integer parameterErrorCount15 = 45; + Integer parameterErrorCount16 = 46; + Integer parameterErrorCount17 = 47; + Integer parameterErrorCount18 = 48; + Integer parameterErrorCount19 = 49; + Boolean inputFlueGasFlapSwitch = true; + Double inputBoardTemperature = 20.20; + Integer inputCurrentStage = 12; + Integer statusMainState = 42; + Integer statusSubState = 43; + Integer statusWifiStrength = 100; + Boolean parameterEcoModePossible = true; + Integer parameterFabricationNumber = 12323; + Integer parameterStoveTypeNumber = 46; + Integer parameterLanguageNumber = 2; + Integer parameterVersionMainBoard = 5; + Integer parameterVersionTft = 6; + Integer parameterVersionWifi = 7; + Integer parameterVersionMainBoardBootLoader = 8; + Integer parameterVersionTftBootLoader = 9; + Integer parameterVersionWifiBootLoader = 10; + Integer parameterVersionMainBoardSub = 11; + Integer inputTargetStagePid = 12; + Integer inputCurrentStagePid = 13; + Integer parameterVersionTftSub = 14; + Integer parameterVersionWifiSub = 15; + Integer parameterRuntimePellets = 16; + Integer parameterRuntimeLogs = 17; + Integer parameterFeedRateTotal = 18; + Integer parameterFeedRateService = 19; + Integer parameterServiceCountdownKg = 20; + Integer parameterServiceCountdownTime = 21; + Integer parameterIgnitionCount = 22; + Integer parameterOnOffCycleCount = 23; + Integer parameterFlameSensorOffset = 24; + Integer parameterPressureSensorOffset = 25; + Integer parameterKgTillCleaning = 26; + Integer parameterCleanIntervalBig = 27; + Integer parameterIdFanTuning = 28; + Integer parameterSpiralMotorsTuning = 29; + Boolean statusFrostStarted = true; + Boolean statusHeatingTimesNotProgrammed = true; + Double inputRoomTemperature = 22.4; + Sensors rikaFirenetSensors = + Sensors.builder() + .inputRoomTemperature(inputRoomTemperature) + .inputFlameTemperature(inputFlameTemperature) + .inputBakeTemperature(inputBakeTemperature) + .statusError(statusError) + .statusSubError(statusSubError) + .statusWarning(statusWarning) + .statusService(statusService) + .outputDischargeMotor(outputDischargeMotor) + .outputDischargeCurrent(outputDischargeCurrent) + .outputIdFan(outputIdFan) + .outputIdFanTarget(outputIdFanTarget) + .outputInsertionMotor(outputInsertionMotor) + .outputInsertionCurrent(outputInsertionCurrent) + .outputAirFlaps(outputAirFlaps) + .outputAirFlapsTargetPosition(outputAirFlapsTargetPosition) + .outputBurnBackFlapMagnet(outputBurnBackFlapMagnet) + .outputGridMotor(outputGridMotor) + .outputIgnition(outputIgnition) + .inputUpperTemperatureLimiter(inputUpperTemperatureLimiter) + .inputPressureSwitch(inputPressureSwitch) + .inputPressureSensor(inputPressureSensor) + .inputGridContact(inputGridContact) + .inputDoor(inputDoor) + .inputCover(inputCover) + .inputExternalRequest(inputExternalRequest) + .inputBurnBackFlapSwitch(inputBurnBackFlapSwitch) + .inputFlueGasFlapSwitch(inputFlueGasFlapSwitch) + .inputBoardTemperature(inputBoardTemperature) + .inputCurrentStage(inputCurrentStage) + .statusMainState(statusMainState) + .statusSubState(statusSubState) + .statusWifiStrength(statusWifiStrength) + .parameterEcoModePossible(parameterEcoModePossible) + .parameterFabricationNumber(parameterFabricationNumber) + .parameterStoveTypeNumber(parameterStoveTypeNumber) + .parameterLanguageNumber(parameterLanguageNumber) + .parameterVersionMainBoard(parameterVersionMainBoard) + .parameterVersionTft(parameterVersionTft) + .parameterVersionWifi(parameterVersionWifi) + .parameterVersionMainBoardBootLoader(parameterVersionMainBoardBootLoader) + .parameterVersionTftBootLoader(parameterVersionTftBootLoader) + .parameterVersionWifiBootLoader(parameterVersionWifiBootLoader) + .parameterVersionMainBoardSub(parameterVersionMainBoardSub) + .inputTargetStagePid(inputTargetStagePid) + .inputCurrentStagePid(inputCurrentStagePid) + .parameterVersionTftSub(parameterVersionTftSub) + .parameterVersionWifiSub(parameterVersionWifiSub) + .parameterRuntimePellets(parameterRuntimePellets) + .parameterRuntimeLogs(parameterRuntimeLogs) + .parameterFeedRateTotal(parameterFeedRateTotal) + .parameterFeedRateService(parameterFeedRateService) + .parameterServiceCountdownKg(parameterServiceCountdownKg) + .parameterServiceCountdownTime(parameterServiceCountdownTime) + .parameterIgnitionCount(parameterIgnitionCount) + .parameterOnOffCycleCount(parameterOnOffCycleCount) + .parameterFlameSensorOffset(parameterFlameSensorOffset) + .parameterPressureSensorOffset(parameterPressureSensorOffset) + .parameterKgTillCleaning(parameterKgTillCleaning) + .parameterCleanIntervalBig(parameterCleanIntervalBig) + .parameterIdFanTuning(parameterIdFanTuning) + .parameterSpiralMotorsTuning(parameterSpiralMotorsTuning) + .statusFrostStarted(statusFrostStarted) + .statusHeatingTimesNotProgrammed(statusHeatingTimesNotProgrammed) + .parameterErrorCount0(parameterErrorCount0) + .parameterErrorCount1(parameterErrorCount1) + .parameterErrorCount2(parameterErrorCount2) + .parameterErrorCount3(parameterErrorCount3) + .parameterErrorCount4(parameterErrorCount4) + .parameterErrorCount5(parameterErrorCount5) + .parameterErrorCount6(parameterErrorCount6) + .parameterErrorCount7(parameterErrorCount7) + .parameterErrorCount8(parameterErrorCount8) + .parameterErrorCount9(parameterErrorCount9) + .parameterErrorCount10(parameterErrorCount10) + .parameterErrorCount11(parameterErrorCount11) + .parameterErrorCount12(parameterErrorCount12) + .parameterErrorCount13(parameterErrorCount13) + .parameterErrorCount14(parameterErrorCount14) + .parameterErrorCount15(parameterErrorCount15) + .parameterErrorCount16(parameterErrorCount16) + .parameterErrorCount17(parameterErrorCount17) + .parameterErrorCount18(parameterErrorCount18) + .parameterErrorCount19(parameterErrorCount19) + .parameterDebug0(parameterDebug0) + .parameterDebug1(parameterDebug1) + .parameterDebug2(parameterDebug2) + .parameterDebug3(parameterDebug3) + .parameterDebug4(parameterDebug4) + .build(); + + // WHEN + var apiSensors = mapper.toApiSensors(rikaFirenetSensors); + + // THEN + assertThat(apiSensors.getParametersErrorCount()).isNotEmpty(); + assertThat(apiSensors.getParametersErrorCount()).hasSize(20); + assertThat(apiSensors.getParametersErrorCount().get(0).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount0()); + assertThat(apiSensors.getParametersErrorCount().get(1).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount1()); + assertThat(apiSensors.getParametersErrorCount().get(2).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount2()); + assertThat(apiSensors.getParametersErrorCount().get(3).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount3()); + assertThat(apiSensors.getParametersErrorCount().get(4).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount4()); + assertThat(apiSensors.getParametersErrorCount().get(5).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount5()); + assertThat(apiSensors.getParametersErrorCount().get(6).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount6()); + assertThat(apiSensors.getParametersErrorCount().get(7).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount7()); + assertThat(apiSensors.getParametersErrorCount().get(8).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount8()); + assertThat(apiSensors.getParametersErrorCount().get(9).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount9()); + assertThat(apiSensors.getParametersErrorCount().get(10).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount10()); + assertThat(apiSensors.getParametersErrorCount().get(11).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount11()); + assertThat(apiSensors.getParametersErrorCount().get(12).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount12()); + assertThat(apiSensors.getParametersErrorCount().get(13).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount13()); + assertThat(apiSensors.getParametersErrorCount().get(14).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount14()); + assertThat(apiSensors.getParametersErrorCount().get(15).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount15()); + assertThat(apiSensors.getParametersErrorCount().get(16).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount16()); + assertThat(apiSensors.getParametersErrorCount().get(17).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount17()); + assertThat(apiSensors.getParametersErrorCount().get(18).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount18()); + assertThat(apiSensors.getParametersErrorCount().get(19).getValue()) + .isEqualTo(rikaFirenetSensors.getParameterErrorCount19()); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapperTest.java new file mode 100644 index 00000000..437cfea6 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapperTest.java @@ -0,0 +1,48 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import dev.cookiecode.rika2mqtt.rika.firenet.model.Controls; +import dev.cookiecode.rika2mqtt.rika.firenet.model.Sensors; +import dev.cookiecode.rika2mqtt.rika.firenet.model.StoveStatus; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** Test class */ +@SpringBootTest(classes = {StoveStatusMapperImpl.class}) +class StoveStatusMapperTest { + + @Autowired private StoveStatusMapper mapper; + + @Test + void toApiStoveStatusShouldFillAllStoveProperties() { + + // GIVEN + String name = ""; + Long stoveId = 12L; + Long lastConfirmedRevision = 12321312L; + String oem = ""; + Long lastSeenMinutes = 0L; + String stoveType = ""; + Sensors sensors = Sensors.builder().build(); + Controls controls = Controls.builder().build(); + StoveStatus rikaFirenetStatus = + StoveStatus.builder() + .name(name) + .stoveId(stoveId) + .lastConfirmedRevision(lastConfirmedRevision) + .oem(oem) + .lastSeenMinutes(lastSeenMinutes) + .stoveType(stoveType) + .sensors(sensors) + .controls(controls) + .build(); + + // WHEN + final var apiStatus = mapper.toApiStoveStatus(rikaFirenetStatus); + + // THEN + assertThat(apiStatus).hasNoNullFieldsOrProperties(); + } +} diff --git a/plugins/disabled.txt b/plugins/disabled.txt new file mode 100644 index 00000000..14e3b6d9 --- /dev/null +++ b/plugins/disabled.txt @@ -0,0 +1 @@ +rika2mqtt-flux-metrics-plugin-1.1.0.jar diff --git a/pom.xml b/pom.xml index 432006d0..87bc1bd0 100644 --- a/pom.xml +++ b/pom.xml @@ -54,8 +54,10 @@ ${basedir} 1.18.30 + 1.5.5.Final 0.8 8.0.1.Final + 3.10.0 1.19.1 @@ -88,6 +90,10 @@ rika-firenet bridge mqtt + plugins-api + rika2mqtt-flux-metrics-plugin + plugins-internal + rika2mqtt-example-plugin diff --git a/rika-firenet/pom.xml b/rika-firenet/pom.xml index 6be0f05a..c6761318 100644 --- a/rika-firenet/pom.xml +++ b/rika-firenet/pom.xml @@ -45,7 +45,6 @@ ${source.encoding} - 1.5.5.Final 2.9.0 4.12.0 5.2.1 diff --git a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/Sensors.java b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/Sensors.java index b18a5cc6..f2490d51 100644 --- a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/Sensors.java +++ b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/Sensors.java @@ -23,12 +23,14 @@ package dev.cookiecode.rika2mqtt.rika.firenet.model; import com.google.gson.annotations.SerializedName; +import lombok.Builder; import lombok.Data; /** * @author Sebastien Vermeille */ @Data +@Builder public class Sensors { private Double inputRoomTemperature; diff --git a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/StoveStatus.java b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/StoveStatus.java index ec1b39e7..6bb8bd09 100644 --- a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/StoveStatus.java +++ b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/StoveStatus.java @@ -23,12 +23,14 @@ package dev.cookiecode.rika2mqtt.rika.firenet.model; import com.google.gson.annotations.SerializedName; +import lombok.Builder; import lombok.Data; /** * @author Sebastien Vermeille */ @Data +@Builder public class StoveStatus { private String name; diff --git a/rika-firenet/src/test/java/dev/cookiecode/rika2mqtt/rika/firenet/model/StoveStatusSerdeTest.java b/rika-firenet/src/test/java/dev/cookiecode/rika2mqtt/rika/firenet/model/StoveStatusSerdeTest.java index 7af757a5..81bf5fb9 100644 --- a/rika-firenet/src/test/java/dev/cookiecode/rika2mqtt/rika/firenet/model/StoveStatusSerdeTest.java +++ b/rika-firenet/src/test/java/dev/cookiecode/rika2mqtt/rika/firenet/model/StoveStatusSerdeTest.java @@ -47,8 +47,7 @@ class StoveStatusSerdeTest { @Test void serializationOfStoveStatusToJsonShouldNotPropagateUppercaseId() { // GIVEN - var status = new StoveStatus(); - status.setStoveId(12L); + var status = StoveStatus.builder().stoveId(12L).build(); // WHEN var jsonResult = gson.toJson(status); diff --git a/rika2mqtt-example-plugin/pom.xml b/rika2mqtt-example-plugin/pom.xml new file mode 100644 index 00000000..3b4e0810 --- /dev/null +++ b/rika2mqtt-example-plugin/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + dev.cookiecode + rika2mqtt-parent + 1.1.0 + + + rika2mqtt-example-plugin + + + ${basedir}/.. + ${java.sdk.version} + ${java.sdk.version} + ${source.encoding} + + + + + dev.cookiecode + plugins-api + 1.1.0 + provided + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.6.0 + + + jar-with-dependencies + + ${project.artifactId}-${project.version} + false + false + + + true + true + + + dev.cookiecode.rika2mqtt.plugins.example.ExamplePlugin + example-plugin + 0.0.1 + 2.0.0 + Log each time an extension point is invoked. + Sebastien Vermeille + MIT + + + + + + + + make-assembly + package + + single + + + + + + + + maven-antrun-plugin + 3.1.0 + + + + copy-to-lib + + run + + package + + + + + + + + + + + diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java new file mode 100644 index 00000000..4ca02f21 --- /dev/null +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java @@ -0,0 +1,14 @@ +package dev.cookiecode.rika2mqtt.plugins.example; + +import dev.cookiecode.rika2mqtt.plugins.api.StoveStatusExtension; +import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import org.pf4j.Extension; + +@Extension +public class ExampleHook implements StoveStatusExtension { + + @Override + public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { + System.out.println("EXAMPLE PLUGIN >> onPollStoveStatusSucceed invoked"); + } +} diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java new file mode 100644 index 00000000..246a79b5 --- /dev/null +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java @@ -0,0 +1,19 @@ +package dev.cookiecode.rika2mqtt.plugins.example; + +import dev.cookiecode.rika2mqtt.plugins.api.Rika2MqttPlugin; + +public class ExamplePlugin extends Rika2MqttPlugin { + + ExampleHook hook; + + @Override + public void start() { + System.out.println("EXAMPLE PLUGIN >> STARTED"); + hook = new ExampleHook(); + } + + @Override + public void stop() { + System.out.println("EXAMPLE PLUGIN >> STOPPED"); + } +} diff --git a/rika2mqtt-flux-metrics-plugin/pom.xml b/rika2mqtt-flux-metrics-plugin/pom.xml new file mode 100644 index 00000000..93d12aa9 --- /dev/null +++ b/rika2mqtt-flux-metrics-plugin/pom.xml @@ -0,0 +1,163 @@ + + + 4.0.0 + + dev.cookiecode + rika2mqtt-parent + 1.1.0 + + + rika2mqtt-flux-metrics-plugin + + + ${basedir}/.. + ${java.sdk.version} + ${java.sdk.version} + ${source.encoding} + 2.5.9 + + + + dev.cookiecode + plugins-api + 1.1.0 + provided + + + + + + + + + io.kamon + kamon-bundle_2.13 + ${kamon.version} + compile + + + io.kamon + kamon-influxdb_2.13 + ${kamon.version} + compile + + + + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.0 + + + shade-jar-with-dependencies + package + + shade + + + + + + dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.Rika2MqttInfluxMetricsPlugin + ${project.artifactId} + ${project.version} + ${project.version} + Export RIKA stoves values to InfluxDB. + Sebastien Vermeille + MIT + + + + + reference.conf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + maven-antrun-plugin + 3.1.0 + + + + copy-to-lib + + run + + package + + + + + + + + + + + diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java new file mode 100644 index 00000000..7ab50cca --- /dev/null +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java @@ -0,0 +1,53 @@ +package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; + +import com.typesafe.config.ConfigFactory; +import dev.cookiecode.rika2mqtt.plugins.api.Rika2MqttPlugin; +import java.util.HashMap; +import kamon.Kamon; + +/** A plugin to export rika2mqtt metrics to InfluxDB */ +public class Rika2MqttInfluxMetricsPlugin extends Rika2MqttPlugin { + + @Override + public void start() { + // This method is called by the application when the plugin is started. + System.out.println("Started plugin influxmetrics !"); + // Config config = ConfigFactory.load("application.conf"); + + // var mergedConfig = config.withFallback(ConfigFactory.defaultReference()); + + Kamon.init(); + + var defaultConfig = Kamon.config(); + + // var codeConfig = ConfigFactory.parseString("kamon.influxdb.port = 8086"); + // var codeConfig2 = ConfigFactory.parseString("kamon.influxdb.hostname = 0.0.0.0"); + // var codeConfig3 = ConfigFactory.parseString("kamon.influxdb.database = mydb"); + + var props = new HashMap(); + props.put("kamon.influxdb.port", 8086); + props.put("kamon.influxdb.hostname", "0.0.0.0"); + props.put("kamon.influxdb.database", "rika2mqtt"); + props.put("kamon.influxdb.protocol", "http"); + props.put("kamon.influxdb.authentication.token", "admin-token"); + + var codeConfig = ConfigFactory.parseMap(props); + var newConfig = codeConfig.withFallback(defaultConfig); + Kamon.reconfigure(newConfig); + Kamon.loadModules(); + // Kamon.initWithoutAttaching(ConfigFactory.defaultReference()); + // + // InfluxDBReporter influxDBReporter = new InfluxDBReporter(); + // Kamon.addReporter("influxdb", influxDBReporter); + } + + @Override + public void stop() { + // This method is called by the application when the plugin is stopped. + } + + @Override + public void delete() { + // This method is called by the application when the plugin is deleted. + } +} diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java new file mode 100644 index 00000000..83fbb5fb --- /dev/null +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -0,0 +1,218 @@ +package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; + +import static java.util.Optional.ofNullable; + +import dev.cookiecode.rika2mqtt.plugins.api.StoveStatusExtension; +import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import kamon.Kamon; +import lombok.NonNull; +import org.pf4j.Extension; + +@Extension +public class StoveStatusHook implements StoveStatusExtension { + + private static final String STOVE_ID = "STOVE_ID"; + private static final String STOVE_NAME = "STOVE_NAME"; + private static final String ERROR_NUMBER = "ERROR_NUMBER"; + private static final String DEBUG_NUMBER = "DEBUG_NUMBER"; + + private static final int MAX_ERROR_NUMBER = 18; + private static final int MAX_DEBUG_NUMBER = 4; + + @Override + public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { + System.out.println( + "STOVE STATUS POLLED HOOKED INSIDE PLUGIN WOOHOO!"); // TODO: add flogger support + + // Kamon + // .gauge("LAST_SEEN_MINUTES", "Last time the stove communicated with rika-firenet + // servers.") // TODO: should we store it that way or with date? + // .withTag(STOVE_ID, stoveStatus.getStoveId()) + // .withTag(STOVE_NAME, stoveStatus.getName()) + // .update(stoveStatus.getLastSeenMinutes()); + + // TODO: last confirmed revision is a timestamp how should we deal with that ? a boolean the + // date it happened ? can we ? + // Kamon + // .gauge() + + System.out.println("TATATATA"); + + exportSensorsMetrics(stoveStatus); + } + + + private void getPropertyValue( + @NonNull StoveStatus stoveStatus, @NonNull final String propertyName) { + try { + Class clazz = StoveStatus.class; + + // Get a reference to the append() method + var getterMethodName = + "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + var getterMethod = clazz.getMethod(getterMethodName); + + var result = getterMethod.invoke(stoveStatus); + System.out.println(result); + System.out.println(result); + System.out.println(result); + System.out.println(result); + } catch (Exception ex) { + ex.printStackTrace(); // TODO: refactor + } + } + + private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { + + System.out.println(stoveStatus.toString()); + + ofNullable(stoveStatus.getSensors().getParameterRuntimePellets()) + .ifPresent( + value -> + Kamon.gauge("parameterRuntimePellets") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterRuntimeLogs()) + .ifPresent( + value -> + Kamon.gauge("parameterRuntimeLogs") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterFeedRateTotal()) + .ifPresent( + value -> + Kamon.gauge("parameterFeedRateTotal") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterFeedRateService()) + .ifPresent( + value -> + Kamon.gauge("parameterFeedRateService") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterServiceCountdownKg()) + .ifPresent( + value -> + // TODO: maybe should be reverse (think about) + Kamon.gauge("parameterServiceCountdownKg") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterServiceCountdownTime()) + .ifPresent( + value -> + Kamon.gauge("parameterServiceCountdownTime") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterIgnitionCount()) + .ifPresent( + value -> + Kamon.gauge("parameterIgnitionCount") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterOnOffCycleCount()) + .ifPresent( + value -> + Kamon.gauge("parameterOnOffCycleCount") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterFlameSensorOffset()) + .ifPresent( + value -> + Kamon.gauge("parameterFlameSensorOffset") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterPressureSensorOffset()) + .ifPresent( + value -> + Kamon.gauge("parameterPressureSensorOffset") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + // TODO: as these values rarely changes see how they get stored (not that we store one value + // each minute that would consume disk for no value + + ofNullable(stoveStatus.getSensors().getParameterSpiralMotorsTuning()) + .ifPresent( + value -> + Kamon.gauge("parameterSpiralMotorsTuning") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterIdFanTuning()) + .ifPresent( + value -> + Kamon.gauge("parameterIdFanTuning") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterCleanIntervalBig()) + .ifPresent( + value -> + Kamon.gauge("parameterCleanIntervalBig") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + ofNullable(stoveStatus.getSensors().getParameterKgTillCleaning()) + .ifPresent( + value -> + Kamon.gauge( + "parameterKgTillCleaning", + "Amount of Kg of pellets to burn before the stove will warn about cleaning it.") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + + // misc metrics + stoveStatus.getSensors().getParametersErrorCount() + .forEach( + parameterErrorCount -> { + Kamon.gauge("parameterErrorCount") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(ERROR_NUMBER, parameterErrorCount.getNumber()) + .update(parameterErrorCount.getValue()); + } + ); + + stoveStatus.getSensors().getParametersDebug() + .forEach( + parameterDebug -> { + Kamon.gauge("parameterDebug") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) + .update(parameterDebug.getValue()); + } + ); + + ofNullable(stoveStatus.getControls().getTargetTemperature()) + .ifPresent( + value -> + Kamon.gauge("controlsTargetTemperature", "Target temperature defined by the user.") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + } +} diff --git a/rika2mqtt-flux-metrics-plugin/src/main/resources/application.conf b/rika2mqtt-flux-metrics-plugin/src/main/resources/application.conf new file mode 100644 index 00000000..c02f0238 --- /dev/null +++ b/rika2mqtt-flux-metrics-plugin/src/main/resources/application.conf @@ -0,0 +1,69 @@ +# ====================================== # +# kamon-influxdb reference configuration # +# ====================================== # + +kamon { + influxdb { + + # Hostname and port in which your InfluxDB is running + hostname = "127.0.0.1" + port = 8086 + + # The database where to write in InfluxDB. + database = "mydb" + + # For histograms, which percentiles to count + percentiles = [50.0, 70.0, 90.0, 95.0, 99.0, 99.9] + + # The protocol to use when used to connect to your InfluxDB: HTTP/HTTPS + protocol = "http" + # Whether or not to submit distributions with count = 0 to influxdb (with 0 values) + post-empty-distributions = false + + # The precision to report the period timestamp in. Corresponds with what influx will accept, minus hours and minutes + # [ns,u,µ,ms,s] + precision = "s" + + # Client authentication credentials for connection to the InfluxDB server. There is no authentication by default. + # You can enable authentication by adding an authentication section to your configuration file with either token or + # user/password settings in it. If you specify both, token authentication will be used. + # + # authentication { + # token = "your_api_token" + # + # // OR + # + # user = "user" + # password = "password" + # + # } + + # Allow including environment information as tags on all reported metrics. + environment-tags { + + # Define whether specific environment settings will be included as tags in all exposed metrics. When enabled, + # the service, host and instance tags will be added using the values from Kamon.environment(). + include-service = no + include-host = no + include-instance = no + + # Specifies which Kamon environment tags should be ignored. All unmatched tags will be always added to al metrics. + exclude = [] + } + + tag-filter { + includes = ["**"] + excludes = [] + } + + } + + modules { + influxdb { + enabled = true + name = "InfluxReporter" + description = "Influxdb http reporter" + factory = "kamon.influxdb.InfluxDBReporterFactory" + } + } +} From f09cd7c1875fa645c8b4692d00c1f07ca63b540b Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 11:29:10 +0100 Subject: [PATCH 02/42] Stable status before review --- .../cookiecode/rika2mqtt/bridge/Bridge.java | 20 +- .../bridge/AbstractBaseIntegrationTest.java | 6 +- .../rika2mqtt/bridge/BridgeTest.java | 4 +- plugins-api/pom.xml | 22 +- .../rika2mqtt/plugins/api/model/Auth.java | 41 -- .../rika2mqtt/plugins/api/model/Controls.java | 76 --- .../plugins/api/{ => v1}/Rika2MqttPlugin.java | 2 +- .../api/{ => v1}/StoveStatusExtension.java | 4 +- .../plugins/api/v1/model/Controls.java | 148 +++++ .../plugins/api/v1/model/ConvectionFan.java | 12 + .../api/{ => v1}/model/ParameterDebug.java | 2 +- .../{ => v1}/model/ParameterErrorCount.java | 2 +- .../plugins/api/{ => v1}/model/Sensors.java | 65 ++- .../plugins/api/{ => v1}/model/StoveId.java | 2 +- .../api/{ => v1}/model/StoveStatus.java | 2 +- .../plugins/api/v1/model/TimeDefinition.java | 26 + .../plugins/api/v1/model/TimeRange.java | 18 + .../api/{ => v1}/model/UpdatableControls.java | 2 +- .../internal/Rika2MqttPluginManager.java | 54 -- .../internal/event/Rika2MqttPluginEvent.java | 3 - .../internal/mapper/ControlsMapper.java | 12 - .../internal/mapper/StoveStatusMapper.java | 13 - .../internal/v1/Rika2MqttPluginManager.java | 43 ++ .../event/PolledStoveStatusEvent.java | 4 +- .../v1/event/Rika2MqttPluginEvent.java | 3 + .../internal/v1/mapper/ControlsMapper.java | 110 ++++ .../v1/mapper/ConvectionFanMapper.java | 24 + .../{ => v1}/mapper/SensorsMapper.java | 8 +- .../internal/v1/mapper/StoveStatusMapper.java | 15 + .../internal/v1/mapper/TimeRangeMapper.java | 34 ++ .../v1/pf4j/Pf4jPluginManagerConfig.java | 15 + .../v1/Rika2MqttPluginManagerTest.java | 72 +++ .../v1/mapper/ControlsMapperTest.java | 94 ++++ .../v1/mapper/ConvectionFanMapperTest.java | 33 ++ .../{ => v1}/mapper/SensorsMapperTest.java | 3 +- .../mapper/StoveStatusMapperTest.java | 11 +- .../v1/mapper/TimeRangeMapperTest.java | 40 ++ .../helper/ControlsMapperEmptyImpl.java | 20 + .../mapper/helper/SensorsMapperEmptyImpl.java | 20 + pom.xml | 2 +- .../rika/firenet/model/Controls.java | 3 + .../rika/firenet/RikaFirenetServiceTest.java | 2 +- .../plugins/example/ExampleHook.java | 4 +- .../plugins/example/ExamplePlugin.java | 2 +- .../metrics/Rika2MqttInfluxMetricsPlugin.java | 25 +- .../influxdb/metrics/StoveStatusHook.java | 527 ++++++++++++------ .../metrics/reflection/ReflectionUtils.java | 15 + 47 files changed, 1212 insertions(+), 453 deletions(-) delete mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Auth.java delete mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Controls.java rename plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/{ => v1}/Rika2MqttPlugin.java (59%) rename plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/{ => v1}/StoveStatusExtension.java (77%) create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java rename plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/{ => v1}/model/ParameterDebug.java (71%) rename plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/{ => v1}/model/ParameterErrorCount.java (71%) rename plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/{ => v1}/model/Sensors.java (89%) rename plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/{ => v1}/model/StoveId.java (95%) rename plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/{ => v1}/model/StoveStatus.java (96%) create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java rename plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/{ => v1}/model/UpdatableControls.java (98%) delete mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/Rika2MqttPluginManager.java delete mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/Rika2MqttPluginEvent.java delete mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/ControlsMapper.java delete mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapper.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java rename plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/{ => v1}/event/PolledStoveStatusEvent.java (61%) create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java rename plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/{ => v1}/mapper/SensorsMapper.java (93%) create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java rename plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/{ => v1}/mapper/SensorsMapperTest.java (99%) rename plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/{ => v1}/mapper/StoveStatusMapperTest.java (78%) create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java create mode 100644 rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java diff --git a/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java b/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java index 69477d5b..b257990c 100644 --- a/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java +++ b/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java @@ -25,9 +25,9 @@ import com.google.common.annotations.VisibleForTesting; import com.google.gson.Gson; import dev.cookiecode.rika2mqtt.bridge.misc.EmailObfuscator; -import dev.cookiecode.rika2mqtt.plugins.internal.Rika2MqttPluginManager; -import dev.cookiecode.rika2mqtt.plugins.internal.event.PolledStoveStatusEvent; -import dev.cookiecode.rika2mqtt.plugins.internal.mapper.StoveStatusMapper; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.Rika2MqttPluginManager; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.event.PolledStoveStatusEvent; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.StoveStatusMapper; import dev.cookiecode.rika2mqtt.rika.firenet.RikaFirenetService; import dev.cookiecode.rika2mqtt.rika.firenet.exception.CouldNotAuthenticateToRikaFirenetException; import dev.cookiecode.rika2mqtt.rika.firenet.exception.InvalidStoveIdException; @@ -138,19 +138,7 @@ void publishToMqtt() { applicationEventPublisher.publishEvent( PolledStoveStatusEvent.builder() - .stoveStatus( - stoveStatusMapper.toApiStoveStatus(status) - // RIKA API data structure has some drawback, the mapper address - // it by adding some convenience for plugin developers. (i.e: - // error0 error1 -> errors[]) - // gson.fromJson( - // jsonStatus, - // - // dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus - // .class) // TODO: a bit a hack mapstruct - // would help having something - // // nicer I believe - ) + .stoveStatus(stoveStatusMapper.toApiStoveStatus(status)) .build()); } catch (InvalidStoveIdException e) { // TODO: could occurs if a stove is added later (after deployment of this rika2mqtt diff --git a/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/AbstractBaseIntegrationTest.java b/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/AbstractBaseIntegrationTest.java index f5df471c..36667bfb 100644 --- a/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/AbstractBaseIntegrationTest.java +++ b/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/AbstractBaseIntegrationTest.java @@ -69,13 +69,13 @@ public static void stopMqttBrokerTestContainer() { } /** - * @implNote The DynamicPropertyRegistry overides application-test.properties in the resource + * @implNote The DynamicPropertyRegistry overrides application-test.properties in the resource * folder, with value in the container static methods. */ @DynamicPropertySource static void overrideTestProperties(DynamicPropertyRegistry registry) { registry.add("mqtt.host", MQTT_SERVER::getHost); - registry.add("mqtt.port", MQTT_SERVER::getFirstMappedPort); // TODO: not safe IMHO + registry.add("mqtt.port", MQTT_SERVER::getFirstMappedPort); } @DynamicPropertySource @@ -269,7 +269,7 @@ public static void initStoveStatusMock(final StoveId stoveId) { "convectionFan2Area": 7, "frostProtectionActive": true, "frostProtectionTemperature": "4", - "temperatureOffset": "0", + "temperatureOffset": "-0.5", "RoomPowerRequest": 3, "debug0": 0, "debug1": 0, diff --git a/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java b/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java index 9c66a358..107e1d40 100644 --- a/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java +++ b/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java @@ -36,8 +36,8 @@ import com.google.gson.Gson; import dev.cookiecode.rika2mqtt.bridge.misc.EmailObfuscator; -import dev.cookiecode.rika2mqtt.plugins.internal.Rika2MqttPluginManager; -import dev.cookiecode.rika2mqtt.plugins.internal.mapper.StoveStatusMapper; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.Rika2MqttPluginManager; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.StoveStatusMapper; import dev.cookiecode.rika2mqtt.rika.firenet.RikaFirenetService; import dev.cookiecode.rika2mqtt.rika.firenet.model.StoveId; import dev.cookiecode.rika2mqtt.rika.mqtt.MqttService; diff --git a/plugins-api/pom.xml b/plugins-api/pom.xml index 09377660..28172d16 100644 --- a/plugins-api/pom.xml +++ b/plugins-api/pom.xml @@ -18,7 +18,6 @@ ${source.encoding} - org.pf4j @@ -26,4 +25,25 @@ ${pf4j.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.sdk.version} + ${java.sdk.version} + + + org.projectlombok + lombok + ${lombok.version} + + + true + + + + diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Auth.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Auth.java deleted file mode 100644 index cb3db67a..00000000 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Auth.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * The MIT License - * Copyright © 2022 Sebastien Vermeille - * - * 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 dev.cookiecode.rika2mqtt.plugins.api.model; - -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; - -/** - * @author Sebastien Vermeille - */ -@Builder -@Getter -@ToString -@EqualsAndHashCode -public class Auth { - - private final String email; - private final String password; -} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Controls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Controls.java deleted file mode 100644 index 56c4a571..00000000 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Controls.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * The MIT License - * Copyright © 2022 Sebastien Vermeille - * - * 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 dev.cookiecode.rika2mqtt.plugins.api.model; - -import lombok.Builder; -import lombok.Data; - -/** - * @author Sebastien Vermeille - */ -@Data -@Builder -public class Controls { - - private Long revision; - private Boolean onOff; - private Integer operatingMode; - private Integer heatingPower; - private Integer targetTemperature; - private Integer bakeTemperature; - private Boolean ecoMode; - private String heatingTimeMon1; - private String heatingTimeMon2; - private String heatingTimeTue1; - private String heatingTimeTue2; - private String heatingTimeWed1; - private String heatingTimeWed2; - private String heatingTimeThu1; - private String heatingTimeThu2; - private String heatingTimeFri1; - private String heatingTimeFri2; - private String heatingTimeSat1; - private String heatingTimeSat2; - private String heatingTimeSun1; - private String heatingTimeSun2; - private Boolean heatingTimesActiveForComfort; - private Integer setBackTemperature; - private Boolean convectionFan1Active; - private Integer convectionFan1Level; - private Integer convectionFan1Area; - private Boolean convectionFan2Active; - private Integer convectionFan2Level; - private Integer convectionFan2Area; - private Boolean frostProtectionActive; - private Integer frostProtectionTemperature; - private Integer temperatureOffset; - - // @SerializedName("RoomPowerRequest") // for coherence (the rest of the api is using camelCase) - private Integer roomPowerRequest; - - private Integer debug0; - private Integer debug1; - private Integer debug2; - private Integer debug3; - private Integer debug4; -} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java similarity index 59% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Rika2MqttPlugin.java rename to plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java index 2647b288..d9fb3e46 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Rika2MqttPlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java @@ -1,4 +1,4 @@ -package dev.cookiecode.rika2mqtt.plugins.api; +package dev.cookiecode.rika2mqtt.plugins.api.v1; import org.pf4j.Plugin; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/StoveStatusExtension.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java similarity index 77% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/StoveStatusExtension.java rename to plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java index c443c6e3..1668ce79 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/StoveStatusExtension.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java @@ -1,6 +1,6 @@ -package dev.cookiecode.rika2mqtt.plugins.api; +package dev.cookiecode.rika2mqtt.plugins.api.v1; -import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; import org.pf4j.ExtensionPoint; public interface StoveStatusExtension extends ExtensionPoint { diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java new file mode 100644 index 00000000..64995243 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java @@ -0,0 +1,148 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; + +import static lombok.AccessLevel.NONE; + +import java.time.DayOfWeek; +import java.util.List; +import java.util.Map; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; + +/** + * @author Sebastien Vermeille + */ +@Data +@Builder +public class Controls { + + private long revision; + private boolean on; + private int operatingMode; + private int heatingPower; + private int targetTemperature; + private int bakeTemperature; + private boolean ecoModeEnabled; + + // region HeatingTime + // TODO: consider removing these properties handled in getHeatingTimes() + @Getter(NONE) + private TimeRange heatingTimeMon1; + + @Getter(NONE) + private TimeRange heatingTimeMon2; + + @Getter(NONE) + private TimeRange heatingTimeTue1; + + @Getter(NONE) + private TimeRange heatingTimeTue2; + + @Getter(NONE) + private TimeRange heatingTimeWed1; + + @Getter(NONE) + private TimeRange heatingTimeWed2; + + @Getter(NONE) + private TimeRange heatingTimeThu1; + + @Getter(NONE) + private TimeRange heatingTimeThu2; + + @Getter(NONE) + private TimeRange heatingTimeFri1; + + @Getter(NONE) + private TimeRange heatingTimeFri2; + + @Getter(NONE) + private TimeRange heatingTimeSat1; + + @Getter(NONE) + private TimeRange heatingTimeSat2; + + @Getter(NONE) + private TimeRange heatingTimeSun1; + + @Getter(NONE) + private TimeRange heatingTimeSun2; + + // endregion + private Map> heatingTimes; + + private Boolean heatingTimesActiveForComfort; + private Integer setBackTemperature; + + // region Fan + // TODO: consider removing these properties handled in getFans() + @Getter(NONE) + private boolean convectionFan1Active; + + @Getter(NONE) + private int convectionFan1Level; + + @Getter(NONE) + private int convectionFan1Area; + + @Getter(NONE) + private boolean convectionFan2Active; + + @Getter(NONE) + private int convectionFan2Level; + + @Getter(NONE) + private int convectionFan2Area; + + // endregion + private List fans; + + private boolean frostProtectionActive; + private int frostProtectionTemperature; + private double temperatureOffset; + + // @SerializedName("RoomPowerRequest") // for coherence (the rest of the api is using camelCase) + private int roomPowerRequest; + + // region Debug + // TODO: consider removing these properties handled in getDebugs() + @Getter(NONE) + private int debug0; + + @Getter(NONE) + private int debug1; + + @Getter(NONE) + private int debug2; + + @Getter(NONE) + private int debug3; + + @Getter(NONE) + private int debug4; + + // endregion + private List debugs; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java new file mode 100644 index 00000000..8d047082 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java @@ -0,0 +1,12 @@ +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; + +import lombok.*; + +@Data +@Builder +public class ConvectionFan { + private int identifier; + private boolean active; + private int level; + private int area; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterDebug.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java similarity index 71% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterDebug.java rename to plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java index 924e1ca7..037bef37 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterDebug.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java @@ -1,4 +1,4 @@ -package dev.cookiecode.rika2mqtt.plugins.api.model; +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; import lombok.Builder; import lombok.Data; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterErrorCount.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java similarity index 71% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterErrorCount.java rename to plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java index da64ec50..12c3c49c 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/ParameterErrorCount.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java @@ -1,4 +1,4 @@ -package dev.cookiecode.rika2mqtt.plugins.api.model; +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; import lombok.Builder; import lombok.Data; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Sensors.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java similarity index 89% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Sensors.java rename to plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java index 4d5f9029..2e078e72 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/Sensors.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java @@ -20,11 +20,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package dev.cookiecode.rika2mqtt.plugins.api.model; +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; + +import static lombok.AccessLevel.NONE; import java.util.List; import lombok.Builder; import lombok.Data; +import lombok.Getter; /** * @author Sebastien Vermeille @@ -42,13 +45,8 @@ public class Sensors { private Integer statusService; private Integer outputDischargeMotor; private Integer outputDischargeCurrent; - - // @SerializedName("outputIDFan") // for coherence (the rest of the api is using camelCase) private Integer outputIdFan; - - // @SerializedName("outputIDFanTarget") // for coherence (the rest of the api is using camelCase) private Integer outputIdFanTarget; - private Integer outputInsertionMotor; private Integer outputInsertionCurrent; private Integer outputAirFlaps; @@ -123,27 +121,70 @@ public class Sensors { private Integer parameterOnOffCycleCount; private Integer parameterFlameSensorOffset; private Integer parameterPressureSensorOffset; + + // region HeatingTime + // TODO: consider removing these properties handled in getParametersErrorCount() + @Getter(NONE) private Integer parameterErrorCount0; + + @Getter(NONE) private Integer parameterErrorCount1; + + @Getter(NONE) private Integer parameterErrorCount2; + + @Getter(NONE) private Integer parameterErrorCount3; + + @Getter(NONE) private Integer parameterErrorCount4; + + @Getter(NONE) private Integer parameterErrorCount5; + + @Getter(NONE) private Integer parameterErrorCount6; + + @Getter(NONE) private Integer parameterErrorCount7; + + @Getter(NONE) private Integer parameterErrorCount8; + + @Getter(NONE) private Integer parameterErrorCount9; + + @Getter(NONE) private Integer parameterErrorCount10; + + @Getter(NONE) private Integer parameterErrorCount11; + + @Getter(NONE) private Integer parameterErrorCount12; + + @Getter(NONE) private Integer parameterErrorCount13; + + @Getter(NONE) private Integer parameterErrorCount14; + + @Getter(NONE) private Integer parameterErrorCount15; + + @Getter(NONE) private Integer parameterErrorCount16; + + @Getter(NONE) private Integer parameterErrorCount17; + + @Getter(NONE) private Integer parameterErrorCount18; + + @Getter(NONE) private Integer parameterErrorCount19; + // endregion private List parametersErrorCount; private Boolean statusHeatingTimesNotProgrammed; @@ -156,11 +197,23 @@ public class Sensors { private Integer parameterCleanIntervalBig; private Integer parameterKgTillCleaning; + + // region ParameterDebug + @Getter(NONE) private Integer parameterDebug0; + + @Getter(NONE) private Integer parameterDebug1; + + @Getter(NONE) private Integer parameterDebug2; + + @Getter(NONE) private Integer parameterDebug3; + + @Getter(NONE) private Integer parameterDebug4; + // endregion private List parametersDebug; } diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveId.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveId.java similarity index 95% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveId.java rename to plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveId.java index 7a4c2b8d..8936c8df 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveId.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveId.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package dev.cookiecode.rika2mqtt.plugins.api.model; +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; /** * @author Sebastien Vermeille diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveStatus.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java similarity index 96% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveStatus.java rename to plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java index 309b2b43..5091d979 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/StoveStatus.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package dev.cookiecode.rika2mqtt.plugins.api.model; +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; import lombok.Data; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java new file mode 100644 index 00000000..d67763a9 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java @@ -0,0 +1,26 @@ +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +/** Class to represent time schedules such as : 10:00 - 12:30 */ +@Builder +@Getter +@EqualsAndHashCode +public class TimeDefinition { + private final int hours; + private final int minutes; + + private static final Double ONE_MINUTE_DURATION_IN_SECONDS = 60.0; + + @Override + public String toString() { + return String.format("%s:%02d", hours, minutes); + } + + /** Helper method to return 9.5 given 9h30 */ + public Double asDecimal() { + return this.getHours() + (this.getMinutes() / ONE_MINUTE_DURATION_IN_SECONDS); + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java new file mode 100644 index 00000000..8246b9a4 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java @@ -0,0 +1,18 @@ +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Builder +@Getter +@EqualsAndHashCode +public class TimeRange { + private final TimeDefinition from; + private final TimeDefinition to; + + @Override + public String toString() { + return String.format("%s - %s", from.toString(), to.toString()); + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/UpdatableControls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java similarity index 98% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/UpdatableControls.java rename to plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java index 813fd88e..6fbca363 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/model/UpdatableControls.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package dev.cookiecode.rika2mqtt.plugins.api.model; +package dev.cookiecode.rika2mqtt.plugins.api.v1.model; import static lombok.AccessLevel.NONE; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/Rika2MqttPluginManager.java deleted file mode 100644 index 1f644bdf..00000000 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/Rika2MqttPluginManager.java +++ /dev/null @@ -1,54 +0,0 @@ -package dev.cookiecode.rika2mqtt.plugins.internal; - -import dev.cookiecode.rika2mqtt.plugins.api.StoveStatusExtension; -import dev.cookiecode.rika2mqtt.plugins.internal.event.PolledStoveStatusEvent; -import jakarta.annotation.PostConstruct; -import lombok.RequiredArgsConstructor; -import lombok.extern.flogger.Flogger; -import org.pf4j.DefaultPluginManager; -import org.pf4j.PluginManager; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; - -// TODO: add javadoc -@Service -@RequiredArgsConstructor -@Flogger -public class Rika2MqttPluginManager { - - private PluginManager pluginManager; - - @PostConstruct - void init() { - pluginManager = new DefaultPluginManager(); - } - - public void start() { - log.atInfo().log("Plugin manager starting ..."); - pluginManager.loadPlugins(); - pluginManager.startPlugins(); - } - - @EventListener - public void handlePolledStoveStatusEvent(PolledStoveStatusEvent event) { - System.out.println("HOOK FOR PLUGINS received a polled status event!"); - var extensions = pluginManager.getExtensions(StoveStatusExtension.class); - - if (extensions.isEmpty()) { - - log.atSevere().log(); - log.atSevere().log(); - log.atSevere().log(); - log.atSevere().log("NO EXTENSION POINT FOUND for StoveStatusExtension!"); - log.atSevere().log(); - log.atSevere().log(); - log.atSevere().log(); - } - - extensions.forEach( - stoveStatusExtension -> { - log.atInfo().log("INVOKE EXTENSION POINT"); - stoveStatusExtension.onPollStoveStatusSucceed(event.getStoveStatus()); - }); - } -} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/Rika2MqttPluginEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/Rika2MqttPluginEvent.java deleted file mode 100644 index ab0a1b87..00000000 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/Rika2MqttPluginEvent.java +++ /dev/null @@ -1,3 +0,0 @@ -package dev.cookiecode.rika2mqtt.plugins.internal.event; - -public interface Rika2MqttPluginEvent {} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/ControlsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/ControlsMapper.java deleted file mode 100644 index 65ceefb5..00000000 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/ControlsMapper.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.cookiecode.rika2mqtt.plugins.internal.mapper; - -import static org.mapstruct.ReportingPolicy.IGNORE; - -import dev.cookiecode.rika2mqtt.plugins.api.model.Controls; -import lombok.NonNull; -import org.mapstruct.Mapper; - -@Mapper(unmappedTargetPolicy = IGNORE) // ignore as we are using a map -public interface ControlsMapper { - Controls toApiControls(@NonNull dev.cookiecode.rika2mqtt.rika.firenet.model.Controls controls); -} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapper.java deleted file mode 100644 index 21b8bfb7..00000000 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapper.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.cookiecode.rika2mqtt.plugins.internal.mapper; - -import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; -import lombok.NonNull; -import org.mapstruct.Mapper; -import org.mapstruct.ReportingPolicy; - -@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) // ignore as we are using a map -public interface StoveStatusMapper { - - StoveStatus toApiStoveStatus( - @NonNull dev.cookiecode.rika2mqtt.rika.firenet.model.StoveStatus stoveStatus); -} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java new file mode 100644 index 00000000..eb5d5b6f --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java @@ -0,0 +1,43 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.event.PolledStoveStatusEvent; +import lombok.RequiredArgsConstructor; +import lombok.extern.flogger.Flogger; +import org.pf4j.PluginManager; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +/** + * Manage plugin loading, start etc. + * + * @author Sebastien Vermeille + */ +@Service +@RequiredArgsConstructor +@Flogger +public class Rika2MqttPluginManager { + + private final PluginManager pluginManager; + + public void start() { + log.atInfo().log("Plugin manager starting ..."); + pluginManager.loadPlugins(); + pluginManager.startPlugins(); + } + + @EventListener + public void handlePolledStoveStatusEvent(PolledStoveStatusEvent event) { + var extensions = pluginManager.getExtensions(StoveStatusExtension.class); + + if (extensions.isEmpty()) { + log.atFinest().log( + "None of the %s plugin(s) registered a hook for extension %s, not forwarding stove status.", + pluginManager.getPlugins().size(), StoveStatusExtension.class.getSimpleName()); + } else { + extensions.forEach( + stoveStatusExtension -> + stoveStatusExtension.onPollStoveStatusSucceed(event.getStoveStatus())); + } + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/PolledStoveStatusEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java similarity index 61% rename from plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/PolledStoveStatusEvent.java rename to plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java index 41f86ede..e62cb3b8 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/event/PolledStoveStatusEvent.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java @@ -1,6 +1,6 @@ -package dev.cookiecode.rika2mqtt.plugins.internal.event; +package dev.cookiecode.rika2mqtt.plugins.internal.v1.event; -import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; import lombok.*; @RequiredArgsConstructor diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java new file mode 100644 index 00000000..51d49845 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java @@ -0,0 +1,3 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.event; + +public interface Rika2MqttPluginEvent {} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java new file mode 100644 index 00000000..7aec220b --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java @@ -0,0 +1,110 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import static com.google.common.collect.ImmutableList.of; +import static java.time.DayOfWeek.*; +import static org.mapstruct.ReportingPolicy.IGNORE; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Controls; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeRange; +import java.time.DayOfWeek; +import java.util.List; +import lombok.NonNull; +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; + +@Mapper( + unmappedTargetPolicy = IGNORE, + uses = {TimeRangeMapper.class, ConvectionFanMapper.class}) // ignore as we are using a map +public interface ControlsMapper { + @Mapping(source = "onOff", target = "on") + @Mapping(source = "ecoMode", target = "ecoModeEnabled") + Controls toApiControls(@NonNull dev.cookiecode.rika2mqtt.rika.firenet.model.Controls controls); + + @AfterMapping + default void afterMapping( + dev.cookiecode.rika2mqtt.rika.firenet.model.Controls source, + @MappingTarget Controls.ControlsBuilder target) { + + // init mappers + final var convectionFanMapper = new ConvectionFanMapperImpl(); + final var timeRangeMapper = new TimeRangeMapperImpl(); + + // Convection fan API Object Oriented way + final var fan1 = + convectionFanMapper.map( + 1, + source.getConvectionFan1Active(), + source.getConvectionFan1Level(), + source.getConvectionFan1Area()); + final var fan2 = + convectionFanMapper.map( + 2, + source.getConvectionFan2Active(), + source.getConvectionFan2Level(), + source.getConvectionFan2Area()); + + final var fans = of(fan1, fan2); + target.fans(fans); + + // Heating times API Object Oriented way + final var heatingTimes = + ImmutableMap.>builder() + .put( + MONDAY, + of( + timeRangeMapper.map(source.getHeatingTimeMon1()), + timeRangeMapper.map(source.getHeatingTimeMon2()))) + .put( + TUESDAY, + of( + timeRangeMapper.map(source.getHeatingTimeTue1()), + timeRangeMapper.map(source.getHeatingTimeTue2()))) + .put( + WEDNESDAY, + of( + timeRangeMapper.map(source.getHeatingTimeWed1()), + timeRangeMapper.map(source.getHeatingTimeWed2()))) + .put( + THURSDAY, + of( + timeRangeMapper.map(source.getHeatingTimeThu1()), + timeRangeMapper.map(source.getHeatingTimeThu2()))) + .put( + FRIDAY, + of( + timeRangeMapper.map(source.getHeatingTimeFri1()), + timeRangeMapper.map(source.getHeatingTimeFri2()))) + .put( + SATURDAY, + of( + timeRangeMapper.map(source.getHeatingTimeSat1()), + timeRangeMapper.map(source.getHeatingTimeSat2()))) + .put( + SUNDAY, + of( + timeRangeMapper.map(source.getHeatingTimeSun1()), + timeRangeMapper.map(source.getHeatingTimeSun2()))) + .build(); + + target.heatingTimes(heatingTimes); + + // debugs API Object Oriented way + var debugs = + ImmutableList.of( + source.getDebug0(), + source.getDebug1(), + source.getDebug2(), + source.getDebug3(), + source.getDebug4()); + + target.debugs(debugs); + + // patch: for some reason mapstruct do not autodetect + // probably do to a property called "set" ... :/ + target.setBackTemperature(source.getSetBackTemperature()); + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java new file mode 100644 index 00000000..a3ad0fea --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java @@ -0,0 +1,24 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import static org.mapstruct.ReportingPolicy.IGNORE; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.ConvectionFan; +import lombok.NonNull; +import org.mapstruct.Mapper; + +@Mapper(unmappedTargetPolicy = IGNORE) // ignore as we are using a map +public interface ConvectionFanMapper { + + default ConvectionFan map( + @NonNull Integer identifier, + @NonNull Boolean active, + @NonNull Integer level, + @NonNull Integer area) { + return ConvectionFan.builder() + .identifier(identifier) + .active(active) + .level(level) + .area(area) + .build(); + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java similarity index 93% rename from plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapper.java rename to plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java index 7e7e4710..0e12addd 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java @@ -1,10 +1,10 @@ -package dev.cookiecode.rika2mqtt.plugins.internal.mapper; +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.mapstruct.ReportingPolicy.IGNORE; -import dev.cookiecode.rika2mqtt.plugins.api.model.ParameterDebug; -import dev.cookiecode.rika2mqtt.plugins.api.model.ParameterErrorCount; -import dev.cookiecode.rika2mqtt.plugins.api.model.Sensors; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.ParameterDebug; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.ParameterErrorCount; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Sensors; import java.util.ArrayList; import java.util.List; import lombok.NonNull; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java new file mode 100644 index 00000000..f577bad1 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java @@ -0,0 +1,15 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; +import lombok.NonNull; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +@Mapper( + unmappedTargetPolicy = ReportingPolicy.IGNORE, + uses = {SensorsMapper.class, ControlsMapper.class}) // ignore as we are using a map +public interface StoveStatusMapper { + + StoveStatus toApiStoveStatus( + @NonNull dev.cookiecode.rika2mqtt.rika.firenet.model.StoveStatus stoveStatus); +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java new file mode 100644 index 00000000..aed9a07e --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java @@ -0,0 +1,34 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import static java.lang.Integer.parseInt; +import static org.mapstruct.ReportingPolicy.IGNORE; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeDefinition; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeRange; +import lombok.NonNull; +import org.mapstruct.Mapper; + +@Mapper(unmappedTargetPolicy = IGNORE) // ignore as we are using a map +public interface TimeRangeMapper { + /** + * @param rikaTimeRange i.e: `13302200` + * @return a TimeRange from 13:30 to 22:00 + */ + default TimeRange map(@NonNull String rikaTimeRange) { + + var from = rikaTimeRange.substring(0, 4); + var to = rikaTimeRange.substring(4); + return TimeRange.builder() + .from( + TimeDefinition.builder() + .hours(parseInt(from.substring(0, 2))) + .minutes(parseInt(from.substring(2))) + .build()) + .to( + TimeDefinition.builder() + .hours(parseInt(to.substring(0, 2))) + .minutes(parseInt(to.substring(2))) + .build()) + .build(); + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java new file mode 100644 index 00000000..1b787c06 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java @@ -0,0 +1,15 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; + +import org.pf4j.DefaultPluginManager; +import org.pf4j.PluginManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class Pf4jPluginManagerConfig { + + @Bean + public PluginManager pluginManager() { + return new DefaultPluginManager(); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java new file mode 100644 index 00000000..e0754a50 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java @@ -0,0 +1,72 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1; + +import static org.mockito.Mockito.*; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.event.PolledStoveStatusEvent; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.pf4j.PluginManager; + +/** Test class */ +@ExtendWith(MockitoExtension.class) +class Rika2MqttPluginManagerTest { + + @InjectMocks private Rika2MqttPluginManager rika2MqttPluginManager; + + @Mock private PluginManager pluginManager; + + @Test + void startShouldInvokeLoadPlugins() { + + // GIVEN + // nothing particular + + // WHEN + rika2MqttPluginManager.start(); + + // THEN + verify(pluginManager, times(1)).loadPlugins(); + } + + @Test + void startShouldInvokeStartPlugins() { + + // GIVEN + // nothing particular + + // WHEN + rika2MqttPluginManager.start(); + + // THEN + verify(pluginManager, times(1)).startPlugins(); + } + + @Test + void + handlePolledStoveStatusEventShouldPropagateTheEventToAllRegisteredExtensionsGivenThereAreTwo() { + + // GIVEN + final var event = mock(PolledStoveStatusEvent.class); + final var stoveStatus = mock(StoveStatus.class); + when(event.getStoveStatus()).thenReturn(stoveStatus); + + // two plugins extensions + final var extensionAlpha = mock(StoveStatusExtension.class); + final var extensionBeta = mock(StoveStatusExtension.class); + final var extensions = List.of(extensionAlpha, extensionBeta); + when(pluginManager.getExtensions(StoveStatusExtension.class)).thenReturn(extensions); + + // WHEN + rika2MqttPluginManager.handlePolledStoveStatusEvent(event); + + // THEN + verify(extensionAlpha, times(1)).onPollStoveStatusSucceed(stoveStatus); + verify(extensionBeta, times(1)).onPollStoveStatusSucceed(stoveStatus); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java new file mode 100644 index 00000000..614a707c --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java @@ -0,0 +1,94 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import dev.cookiecode.rika2mqtt.rika.firenet.model.Controls; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** Test class */ +@SpringBootTest( + classes = {ControlsMapperImpl.class, TimeRangeMapperImpl.class, ConvectionFanMapperImpl.class}) +class ControlsMapperTest { + + @Autowired private ControlsMapper mapper; + + @Test + void mapShouldProperlyMapGivenProperties() { + + // GIVEN + final var on = true; + final var revision = 1232132L; + final var operatingMode = 1; + final var heatingPower = 80; + final var targetTemperature = 19; + final var bakeTemperature = 20; + final var ecoMode = true; + final var fan1Active = true; + final var fan1Area = 50; + final var fan1Level = 12; + final var fan2Active = false; + final var fan2Area = 54; + final var fan2Level = 13; + final var frostProtection = true; + final var debug0 = 0; + final var debug1 = 1; + final var debug2 = 2; + final var debug3 = 3; + final var debug4 = 4; + final Controls controls = + Controls.builder() + .onOff(on) + .revision(revision) + .operatingMode(operatingMode) + .heatingPower(heatingPower) + .targetTemperature(targetTemperature) + .bakeTemperature(bakeTemperature) + .ecoMode(ecoMode) + .convectionFan1Active(fan1Active) + .convectionFan1Area(fan1Area) + .convectionFan1Level(fan1Level) + .convectionFan2Active(fan2Active) + .convectionFan2Area(fan2Area) + .convectionFan2Level(fan2Level) + .frostProtectionActive(frostProtection) + // TODO: heating time are provided here because could not mock the mapper + // at the moment. The conversion will not be checked in this class however) + .heatingTimeMon1("08001200") + .heatingTimeMon2("12041800") + .heatingTimeTue1("08001200") + .heatingTimeTue2("12041800") + .heatingTimeWed1("08001200") + .heatingTimeWed2("12041800") + .heatingTimeThu1("08001200") + .heatingTimeThu2("12041800") + .heatingTimeFri1("08001200") + .heatingTimeFri2("12041800") + .heatingTimeSat1("08001200") + .heatingTimeSat2("12041800") + .heatingTimeSun1("08001200") + .heatingTimeSun2("12041800") + .debug0(debug0) + .debug1(debug1) + .debug2(debug2) + .debug3(debug3) + .debug4(debug4) + .build(); + + // WHEN + final var result = mapper.toApiControls(controls); + + // THEN + assertThat(result.isOn()).isEqualTo(on); + assertThat(result.getRevision()).isEqualTo(revision); + assertThat(result.getOperatingMode()).isEqualTo(operatingMode); + assertThat(result.getHeatingPower()).isEqualTo(heatingPower); + assertThat(result.getTargetTemperature()).isEqualTo(targetTemperature); + assertThat(result.getBakeTemperature()).isEqualTo(bakeTemperature); + assertThat(result.isEcoModeEnabled()).isEqualTo(ecoMode); + + assertThat(result.getFans().get(0).isActive()).isEqualTo(fan1Active); + assertThat(result.getFans().get(1).isActive()).isEqualTo(fan2Active); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java new file mode 100644 index 00000000..c6e6aaf9 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java @@ -0,0 +1,33 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** Test class */ +@SpringBootTest(classes = {ConvectionFanMapperImpl.class}) +class ConvectionFanMapperTest { + + @Autowired private ConvectionFanMapper mapper; + + @Test + void mapShouldConvertSuccessfullyGivenFanProperties() { + + // GIVEN + final var identifier = 1; + final var active = true; + final var level = 12; + final var area = 50; + + // WHEN + final var result = mapper.map(identifier, active, level, area); + + // THEN + assertThat(result.getIdentifier()).isEqualTo(identifier); + assertThat(result.isActive()).isEqualTo(active); + assertThat(result.getLevel()).isEqualTo(level); + assertThat(result.getArea()).isEqualTo(area); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java similarity index 99% rename from plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapperTest.java rename to plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java index a692bdf1..dc5c5094 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/SensorsMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java @@ -1,7 +1,6 @@ -package dev.cookiecode.rika2mqtt.plugins.internal.mapper; +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; import dev.cookiecode.rika2mqtt.rika.firenet.model.Sensors; import org.junit.jupiter.api.Test; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java similarity index 78% rename from plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapperTest.java rename to plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java index 437cfea6..4b76f5ea 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/mapper/StoveStatusMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java @@ -1,7 +1,9 @@ -package dev.cookiecode.rika2mqtt.plugins.internal.mapper; +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.helper.ControlsMapperEmptyImpl; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.helper.SensorsMapperEmptyImpl; import dev.cookiecode.rika2mqtt.rika.firenet.model.Controls; import dev.cookiecode.rika2mqtt.rika.firenet.model.Sensors; import dev.cookiecode.rika2mqtt.rika.firenet.model.StoveStatus; @@ -10,7 +12,12 @@ import org.springframework.boot.test.context.SpringBootTest; /** Test class */ -@SpringBootTest(classes = {StoveStatusMapperImpl.class}) +@SpringBootTest( + classes = { + StoveStatusMapperImpl.class, + SensorsMapperEmptyImpl.class, + ControlsMapperEmptyImpl.class + }) class StoveStatusMapperTest { @Autowired private StoveStatusMapper mapper; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java new file mode 100644 index 00000000..06f541a2 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java @@ -0,0 +1,40 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** Test class */ +@SpringBootTest(classes = {TimeRangeMapperImpl.class}) +class TimeRangeMapperTest { + + @Autowired private TimeRangeMapper mapper; + + @Test + void mapShouldConvertSuccessfullyGivenAValidRikaTimeRangeInputGivenDoubleDigitTimes() { + + // GIVEN + final var rikaTimeRange = "13302215"; + + // WHEN + final var result = mapper.map(rikaTimeRange); + + // THEN + assertThat(result.toString()).isEqualTo("13:30 - 22:15"); + } + + @Test + void mapShouldConvertSuccessfullyGivenAValidRikaTimeRangeInputGivenSingleDigitTimes() { + + // GIVEN + final var rikaTimeRange = "13012205"; + + // WHEN + final var result = mapper.map(rikaTimeRange); + + // THEN + assertThat(result.toString()).isEqualTo("13:01 - 22:05"); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java new file mode 100644 index 00000000..a7ae7c1b --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java @@ -0,0 +1,20 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.helper; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Controls; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.ControlsMapper; +import lombok.NonNull; +import org.springframework.stereotype.Component; + +/** + * This class is intentionally very empty (It allows to keep tests valuable and not repeating + * themselves) + */ +@Component +public class ControlsMapperEmptyImpl implements ControlsMapper { + + @Override + public Controls toApiControls( + dev.cookiecode.rika2mqtt.rika.firenet.model.@NonNull Controls controls) { + return Controls.builder().build(); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java new file mode 100644 index 00000000..7fcc7167 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java @@ -0,0 +1,20 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.helper; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Sensors; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.SensorsMapper; +import lombok.NonNull; +import org.springframework.stereotype.Component; + +/** + * This class is intentionally very empty (It allows to keep tests valuable and not repeating + * themselves) + */ +@Component +public class SensorsMapperEmptyImpl implements SensorsMapper { + + @Override + public Sensors toApiSensors( + dev.cookiecode.rika2mqtt.rika.firenet.model.@NonNull Sensors sensors) { + return Sensors.builder().build(); + } +} diff --git a/pom.xml b/pom.xml index 87bc1bd0..aa98e75d 100644 --- a/pom.xml +++ b/pom.xml @@ -387,7 +387,7 @@ ${rika2mqtt.root}/.licenses/licenses.xml false true - true + false true diff --git a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/Controls.java b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/Controls.java index 5bb2df4d..b936b571 100644 --- a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/Controls.java +++ b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/model/Controls.java @@ -40,6 +40,8 @@ public class Controls { private Integer targetTemperature; private Integer bakeTemperature; private Boolean ecoMode; + + // Heating time format: heatingTimeWed2: "13302200" (13:30 - 22:00) private String heatingTimeMon1; private String heatingTimeMon2; private String heatingTimeTue1; @@ -54,6 +56,7 @@ public class Controls { private String heatingTimeSat2; private String heatingTimeSun1; private String heatingTimeSun2; + private Boolean heatingTimesActiveForComfort; private Integer setBackTemperature; private Boolean convectionFan1Active; diff --git a/rika-firenet/src/test/java/dev/cookiecode/rika2mqtt/rika/firenet/RikaFirenetServiceTest.java b/rika-firenet/src/test/java/dev/cookiecode/rika2mqtt/rika/firenet/RikaFirenetServiceTest.java index f1cb854e..8000061f 100644 --- a/rika-firenet/src/test/java/dev/cookiecode/rika2mqtt/rika/firenet/RikaFirenetServiceTest.java +++ b/rika-firenet/src/test/java/dev/cookiecode/rika2mqtt/rika/firenet/RikaFirenetServiceTest.java @@ -680,7 +680,7 @@ private void initStoveStatusMock(final StoveId stoveId) { "convectionFan2Area": 7, "frostProtectionActive": true, "frostProtectionTemperature": "4", - "temperatureOffset": "0", + "temperatureOffset": "-0.5", "RoomPowerRequest": 3, "debug0": 0, "debug1": 0, diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java index 4ca02f21..d616353c 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java @@ -1,7 +1,7 @@ package dev.cookiecode.rika2mqtt.plugins.example; -import dev.cookiecode.rika2mqtt.plugins.api.StoveStatusExtension; -import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; import org.pf4j.Extension; @Extension diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java index 246a79b5..7ad9946f 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java @@ -1,6 +1,6 @@ package dev.cookiecode.rika2mqtt.plugins.example; -import dev.cookiecode.rika2mqtt.plugins.api.Rika2MqttPlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; public class ExamplePlugin extends Rika2MqttPlugin { diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java index 7ab50cca..4ce8ae65 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java @@ -1,7 +1,7 @@ package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; import com.typesafe.config.ConfigFactory; -import dev.cookiecode.rika2mqtt.plugins.api.Rika2MqttPlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; import java.util.HashMap; import kamon.Kamon; @@ -10,20 +10,11 @@ public class Rika2MqttInfluxMetricsPlugin extends Rika2MqttPlugin { @Override public void start() { - // This method is called by the application when the plugin is started. - System.out.println("Started plugin influxmetrics !"); - // Config config = ConfigFactory.load("application.conf"); - - // var mergedConfig = config.withFallback(ConfigFactory.defaultReference()); - Kamon.init(); var defaultConfig = Kamon.config(); - // var codeConfig = ConfigFactory.parseString("kamon.influxdb.port = 8086"); - // var codeConfig2 = ConfigFactory.parseString("kamon.influxdb.hostname = 0.0.0.0"); - // var codeConfig3 = ConfigFactory.parseString("kamon.influxdb.database = mydb"); - + // TODO: have to be customizable var props = new HashMap(); props.put("kamon.influxdb.port", 8086); props.put("kamon.influxdb.hostname", "0.0.0.0"); @@ -35,19 +26,11 @@ public void start() { var newConfig = codeConfig.withFallback(defaultConfig); Kamon.reconfigure(newConfig); Kamon.loadModules(); - // Kamon.initWithoutAttaching(ConfigFactory.defaultReference()); - // - // InfluxDBReporter influxDBReporter = new InfluxDBReporter(); - // Kamon.addReporter("influxdb", influxDBReporter); } @Override public void stop() { - // This method is called by the application when the plugin is stopped. - } - - @Override - public void delete() { - // This method is called by the application when the plugin is deleted. + log.atInfo().log("Stopping Kamon..."); + Kamon.stop(); } } diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index 83fbb5fb..c7f52bfa 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -1,218 +1,381 @@ package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; -import static java.util.Optional.ofNullable; - -import dev.cookiecode.rika2mqtt.plugins.api.StoveStatusExtension; -import dev.cookiecode.rika2mqtt.plugins.api.model.StoveStatus; +import static dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection.ReflectionUtils.isBooleanProperty; +import static java.lang.Boolean.TRUE; +import static java.util.concurrent.TimeUnit.HOURS; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Controls; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Sensors; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; +import java.util.Optional; +import java.util.stream.IntStream; import kamon.Kamon; import lombok.NonNull; +import lombok.extern.flogger.Flogger; import org.pf4j.Extension; @Extension +@Flogger public class StoveStatusHook implements StoveStatusExtension { + // Tags used for influx storage (ease filtering) private static final String STOVE_ID = "STOVE_ID"; private static final String STOVE_NAME = "STOVE_NAME"; + private static final String FAN_ID = "FAN_ID"; + private static final String DAY_OF_WEEK = "DAY_OF_WEEK"; + private static final String TIME_RANGE_INDEX = "TIME_RANGE_INDEX"; private static final String ERROR_NUMBER = "ERROR_NUMBER"; private static final String DEBUG_NUMBER = "DEBUG_NUMBER"; - private static final int MAX_ERROR_NUMBER = 18; - private static final int MAX_DEBUG_NUMBER = 4; - @Override public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { - System.out.println( - "STOVE STATUS POLLED HOOKED INSIDE PLUGIN WOOHOO!"); // TODO: add flogger support + log.atInfo().atMostEvery(1, HOURS).log( + "Stove status is being continuously forwarded to Influx"); - // Kamon - // .gauge("LAST_SEEN_MINUTES", "Last time the stove communicated with rika-firenet - // servers.") // TODO: should we store it that way or with date? - // .withTag(STOVE_ID, stoveStatus.getStoveId()) - // .withTag(STOVE_NAME, stoveStatus.getName()) - // .update(stoveStatus.getLastSeenMinutes()); + exportSensorsMetrics(stoveStatus); + exportControlsMetrics(stoveStatus); - // TODO: last confirmed revision is a timestamp how should we deal with that ? a boolean the - // date it happened ? can we ? - // Kamon - // .gauge() + exportProperty(stoveStatus, "lastSeenMinutes", Long.class); + exportProperty(stoveStatus, "lastConfirmedRevision", Long.class); + } - System.out.println("TATATATA"); + private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { + exportProperty(stoveStatus, "controls.revision", Long.class); + exportProperty(stoveStatus, "controls.on", Boolean.class); + exportProperty(stoveStatus, "controls.operatingMode", Integer.class); + exportProperty(stoveStatus, "controls.heatingPower", Integer.class); + exportProperty(stoveStatus, "controls.targetTemperature", Integer.class); + exportProperty(stoveStatus, "controls.bakeTemperature", Integer.class); + exportProperty(stoveStatus, "controls.ecoMode", Boolean.class); + exportProperty(stoveStatus, "controls.heatingTimesActiveForComfort", Boolean.class); + exportProperty(stoveStatus, "controls.setBackTemperature", Integer.class); + exportProperty(stoveStatus, "controls.frostProtectionActive", Boolean.class); + exportProperty(stoveStatus, "controls.frostProtectionTemperature", Integer.class); + exportProperty(stoveStatus, "controls.temperatureOffset", Double.class); + exportProperty(stoveStatus, "controls.roomPowerRequest", Integer.class); + + // following metrics are requiring special treatment + stoveStatus + .getControls() + .getFans() + .forEach( + convectionFan -> { + Kamon.gauge("controls.convectionFan.area") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(FAN_ID, convectionFan.getIdentifier()) // extra tag + .update(convectionFan.getArea()); + + Kamon.gauge("controls.convectionFan.level") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(FAN_ID, convectionFan.getIdentifier()) // extra tag + .update(convectionFan.getLevel()); + + Kamon.gauge("controls.convectionFan.active") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(FAN_ID, convectionFan.getIdentifier()) // extra tag + .update(convectionFan.isActive() ? 1 : 0); + }); + + stoveStatus + .getControls() + .getHeatingTimes() + .forEach( + (dayOfWeek, timeRanges) -> + IntStream.range(0, timeRanges.size()) + .forEach( + index -> { + final var timeRange = timeRanges.get(index); + // from + Kamon.gauge("controls.heatingTime.from.hours") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag + .withTag(TIME_RANGE_INDEX, index) + .update(timeRange.getFrom().getHours()); + Kamon.gauge("controls.heatingTime.from.minutes") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag + .withTag(TIME_RANGE_INDEX, index) + .update(timeRange.getFrom().getMinutes()); + Kamon.gauge("controls.heatingTime.from.decimal") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag + .withTag(TIME_RANGE_INDEX, index) + .update(timeRange.getFrom().asDecimal()); - exportSensorsMetrics(stoveStatus); + // to + Kamon.gauge("controls.heatingTime.to.hours") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag + .withTag(TIME_RANGE_INDEX, index) + .update(timeRange.getTo().getHours()); + Kamon.gauge("controls.heatingTime.to.minutes") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag + .withTag(TIME_RANGE_INDEX, index) + .update(timeRange.getTo().getMinutes()); + + Kamon.gauge("controls.heatingTime.to.decimal") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag + .withTag(TIME_RANGE_INDEX, index) + .update(timeRange.getTo().asDecimal()); + })); + + final var controlsDebugs = stoveStatus.getControls().getDebugs(); + IntStream.range(0, controlsDebugs.size()) + .forEach( + index -> { + final var debugValue = controlsDebugs.get(index); + Kamon.gauge("controls.debug") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DEBUG_NUMBER, index) // extra tag + .update(debugValue); + }); } + private void exportProperty( + @NonNull StoveStatus stoveStatus, @NonNull final String propertyName, Class returnType) { + if (returnType == Double.class) { + getDoublePropertyValue(stoveStatus, propertyName) + .ifPresentOrElse( + value -> + Kamon.gauge(propertyName) + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value), + () -> + log.atWarning().log( + "Could not export property %s, it could not be retrieved.", propertyName)); + } else if (returnType == Integer.class) { + getIntegerPropertyValue(stoveStatus, propertyName) + .ifPresentOrElse( + value -> + Kamon.gauge(propertyName) + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value), + () -> + log.atWarning().log( + "Could not export property %s, it could not be retrieved.", propertyName)); + ; + } else if (returnType == Long.class) { + getLongPropertyValue(stoveStatus, propertyName) + .ifPresentOrElse( + value -> + Kamon.gauge(propertyName) + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value), + () -> + log.atWarning().log( + "Could not export property %s, it could not be retrieved.", propertyName)); + ; + } else if (returnType == Boolean.class) { + getBooleanPropertyValue(stoveStatus, propertyName) + .map(value -> value == TRUE ? 1 : 0) + .map( + value -> + Kamon.gauge(propertyName) + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .update(value)); + } else { + log.atSevere().log( + "Could not extract property %s because it's return type %s is unsupported.", + propertyName, returnType); + } + } - private void getPropertyValue( + private Optional getPropertyValue( @NonNull StoveStatus stoveStatus, @NonNull final String propertyName) { - try { - Class clazz = StoveStatus.class; - - // Get a reference to the append() method - var getterMethodName = - "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); - var getterMethod = clazz.getMethod(getterMethodName); - - var result = getterMethod.invoke(stoveStatus); - System.out.println(result); - System.out.println(result); - System.out.println(result); - System.out.println(result); - } catch (Exception ex) { - ex.printStackTrace(); // TODO: refactor + if (propertyName.startsWith("sensors.")) { + try { + + Class clazz = Sensors.class; + final var shortName = propertyName.replace("sensors.", ""); + String getterMethodName; + if (isBooleanProperty(clazz, shortName)) { + getterMethodName = + "is" + shortName.substring(0, 1).toUpperCase() + shortName.substring(1); + } else { + getterMethodName = + "get" + shortName.substring(0, 1).toUpperCase() + shortName.substring(1); + } + // Get a reference to the append() method + var getterMethod = clazz.getMethod(getterMethodName); + + var result = getterMethod.invoke(stoveStatus.getSensors()); + return Optional.ofNullable(result.toString()); + } catch (Exception ex) { + log.atSevere().withCause(ex).log("Could not get property %s", propertyName); + return Optional.empty(); + } + } else if (propertyName.startsWith("controls.")) { + try { + + final var shortName = propertyName.replace("controls.", ""); + + Class clazz = Controls.class; + + String getterMethodName; + if (isBooleanProperty(clazz, shortName)) { + getterMethodName = + "is" + shortName.substring(0, 1).toUpperCase() + shortName.substring(1); + } else { + getterMethodName = + "get" + shortName.substring(0, 1).toUpperCase() + shortName.substring(1); + } + + // Get a reference to the append() method + var getterMethod = clazz.getMethod(getterMethodName); + var result = Optional.ofNullable(getterMethod.invoke(stoveStatus.getControls())); + return result.map(Object::toString); + } catch (Exception ex) { + log.atSevere().withCause(ex).log("Could not get property %s", propertyName); + return Optional.empty(); + } + } else { + try { + + Class clazz = StoveStatus.class; + + // Get a reference to the append() method + var getterMethodName = + "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + var getterMethod = clazz.getMethod(getterMethodName); + + var result = getterMethod.invoke(stoveStatus); + return Optional.ofNullable(result.toString()); + } catch (Exception ex) { + log.atSevere().withCause(ex).log("Could not get property %s", propertyName); + return Optional.empty(); + } } } - private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { - - System.out.println(stoveStatus.toString()); - - ofNullable(stoveStatus.getSensors().getParameterRuntimePellets()) - .ifPresent( - value -> - Kamon.gauge("parameterRuntimePellets") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterRuntimeLogs()) - .ifPresent( - value -> - Kamon.gauge("parameterRuntimeLogs") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterFeedRateTotal()) - .ifPresent( - value -> - Kamon.gauge("parameterFeedRateTotal") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterFeedRateService()) - .ifPresent( - value -> - Kamon.gauge("parameterFeedRateService") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterServiceCountdownKg()) - .ifPresent( - value -> - // TODO: maybe should be reverse (think about) - Kamon.gauge("parameterServiceCountdownKg") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterServiceCountdownTime()) - .ifPresent( - value -> - Kamon.gauge("parameterServiceCountdownTime") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterIgnitionCount()) - .ifPresent( - value -> - Kamon.gauge("parameterIgnitionCount") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterOnOffCycleCount()) - .ifPresent( - value -> - Kamon.gauge("parameterOnOffCycleCount") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterFlameSensorOffset()) - .ifPresent( - value -> - Kamon.gauge("parameterFlameSensorOffset") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterPressureSensorOffset()) - .ifPresent( - value -> - Kamon.gauge("parameterPressureSensorOffset") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); + private Optional getDoublePropertyValue( + @NonNull StoveStatus stoveStatus, @NonNull final String propertyName) { + return getPropertyValue(stoveStatus, propertyName).map(Double::valueOf); + } - // TODO: as these values rarely changes see how they get stored (not that we store one value - // each minute that would consume disk for no value + private Optional getIntegerPropertyValue( + @NonNull StoveStatus stoveStatus, @NonNull final String propertyName) { + return getPropertyValue(stoveStatus, propertyName).map(Integer::valueOf); + } - ofNullable(stoveStatus.getSensors().getParameterSpiralMotorsTuning()) - .ifPresent( - value -> - Kamon.gauge("parameterSpiralMotorsTuning") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); + private Optional getLongPropertyValue( + @NonNull StoveStatus stoveStatus, @NonNull final String propertyName) { + return getPropertyValue(stoveStatus, propertyName).map(Long::valueOf); + } - ofNullable(stoveStatus.getSensors().getParameterIdFanTuning()) - .ifPresent( - value -> - Kamon.gauge("parameterIdFanTuning") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); + private Optional getBooleanPropertyValue( + @NonNull StoveStatus stoveStatus, @NonNull final String propertyName) { + return getPropertyValue(stoveStatus, propertyName).map(Boolean::valueOf); + } - ofNullable(stoveStatus.getSensors().getParameterCleanIntervalBig()) - .ifPresent( - value -> - Kamon.gauge("parameterCleanIntervalBig") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); - - ofNullable(stoveStatus.getSensors().getParameterKgTillCleaning()) - .ifPresent( - value -> - Kamon.gauge( - "parameterKgTillCleaning", - "Amount of Kg of pellets to burn before the stove will warn about cleaning it.") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); + private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { - // misc metrics - stoveStatus.getSensors().getParametersErrorCount() - .forEach( - parameterErrorCount -> { - Kamon.gauge("parameterErrorCount") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .withTag(ERROR_NUMBER, parameterErrorCount.getNumber()) - .update(parameterErrorCount.getValue()); - } - ); - - stoveStatus.getSensors().getParametersDebug() - .forEach( - parameterDebug -> { - Kamon.gauge("parameterDebug") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) - .update(parameterDebug.getValue()); - } - ); - - ofNullable(stoveStatus.getControls().getTargetTemperature()) - .ifPresent( - value -> - Kamon.gauge("controlsTargetTemperature", "Target temperature defined by the user.") + exportProperty(stoveStatus, "sensors.inputRoomTemperature", Double.class); + exportProperty(stoveStatus, "sensors.inputFlameTemperature", Integer.class); + exportProperty(stoveStatus, "sensors.inputBakeTemperature", Integer.class); + exportProperty(stoveStatus, "sensors.statusError", Integer.class); + exportProperty(stoveStatus, "sensors.statusSubError", Integer.class); + exportProperty(stoveStatus, "sensors.statusWarning", Integer.class); + exportProperty(stoveStatus, "sensors.statusService", Integer.class); + exportProperty(stoveStatus, "sensors.outputDischargeMotor", Integer.class); + exportProperty(stoveStatus, "sensors.outputDischargeCurrent", Integer.class); + exportProperty(stoveStatus, "sensors.outputIdFan", Integer.class); + exportProperty(stoveStatus, "sensors.outputIdFanTarget", Integer.class); + exportProperty(stoveStatus, "sensors.outputInsertionMotor", Integer.class); + exportProperty(stoveStatus, "sensors.outputInsertionCurrent", Integer.class); + exportProperty(stoveStatus, "sensors.outputAirFlaps", Integer.class); + exportProperty(stoveStatus, "sensors.outputAirFlapsTargetPosition", Integer.class); + exportProperty(stoveStatus, "sensors.outputBurnBackFlapMagnet", Boolean.class); + exportProperty(stoveStatus, "sensors.outputGridMotor", Boolean.class); + exportProperty(stoveStatus, "sensors.outputIgnition", Boolean.class); + exportProperty(stoveStatus, "sensors.inputUpperTemperatureLimiter", Boolean.class); + exportProperty(stoveStatus, "sensors.inputPressureSwitch", Boolean.class); + exportProperty(stoveStatus, "sensors.inputPressureSensor", Integer.class); + exportProperty(stoveStatus, "sensors.inputGridContact", Boolean.class); + exportProperty(stoveStatus, "sensors.inputDoor", Boolean.class); + exportProperty(stoveStatus, "sensors.inputCover", Boolean.class); + exportProperty(stoveStatus, "sensors.inputExternalRequest", Boolean.class); + exportProperty(stoveStatus, "sensors.inputBurnBackFlapSwitch", Boolean.class); + exportProperty(stoveStatus, "sensors.inputFlueGasFlapSwitch", Boolean.class); + exportProperty(stoveStatus, "sensors.inputBoardTemperature", Double.class); + exportProperty(stoveStatus, "sensors.inputCurrentStage", Integer.class); + exportProperty(stoveStatus, "sensors.inputTargetStagePid", Integer.class); + exportProperty(stoveStatus, "sensors.inputCurrentStagePid", Integer.class); + + exportProperty(stoveStatus, "sensors.statusMainState", Integer.class); + exportProperty(stoveStatus, "sensors.statusSubState", Integer.class); + exportProperty(stoveStatus, "sensors.statusWifiStrength", Integer.class); + exportProperty(stoveStatus, "sensors.parameterEcoModePossible", Boolean.class); + exportProperty(stoveStatus, "sensors.parameterFabricationNumber", Integer.class); + exportProperty(stoveStatus, "sensors.parameterStoveTypeNumber", Integer.class); + exportProperty(stoveStatus, "sensors.parameterLanguageNumber", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionMainBoard", Integer.class); + + exportProperty(stoveStatus, "sensors.parameterVersionTft", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionWifi", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionMainBoardBootLoader", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionTftBootLoader", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionWifiBootLoader", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionMainBoardSub", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionTftSub", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionWifiSub", Integer.class); + + exportProperty(stoveStatus, "sensors.parameterRuntimePellets", Integer.class); + exportProperty(stoveStatus, "sensors.parameterRuntimeLogs", Integer.class); + exportProperty(stoveStatus, "sensors.parameterFeedRateTotal", Integer.class); + exportProperty(stoveStatus, "sensors.parameterFeedRateService", Integer.class); + exportProperty(stoveStatus, "sensors.parameterServiceCountdownKg", Integer.class); + exportProperty(stoveStatus, "sensors.parameterServiceCountdownTime", Integer.class); + exportProperty(stoveStatus, "sensors.parameterIgnitionCount", Integer.class); + exportProperty(stoveStatus, "sensors.parameterOnOffCycleCount", Integer.class); + exportProperty(stoveStatus, "sensors.parameterFlameSensorOffset", Integer.class); + exportProperty(stoveStatus, "sensors.parameterPressureSensorOffset", Integer.class); + + exportProperty(stoveStatus, "sensors.statusHeatingTimesNotProgrammed", Boolean.class); + exportProperty(stoveStatus, "sensors.statusFrostStarted", Boolean.class); + exportProperty(stoveStatus, "sensors.parameterSpiralMotorsTuning", Integer.class); + exportProperty(stoveStatus, "sensors.parameterIdFanTuning", Integer.class); + exportProperty(stoveStatus, "sensors.parameterCleanIntervalBig", Integer.class); + exportProperty(stoveStatus, "sensors.parameterKgTillCleaning", Integer.class); + + // following metrics are requiring special treatment + stoveStatus + .getSensors() + .getParametersErrorCount() + .forEach( + parameterErrorCount -> + Kamon.gauge("sensors.parameterErrorCount") .withTag(STOVE_ID, stoveStatus.getStoveId()) .withTag(STOVE_NAME, stoveStatus.getName()) - .update(value)); + .withTag(ERROR_NUMBER, parameterErrorCount.getNumber()) // extra tag + .update(parameterErrorCount.getValue())); + + stoveStatus + .getSensors() + .getParametersDebug() + .forEach( + parameterDebug -> { + Kamon.gauge("sensors.parameterDebug") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag + .update(parameterDebug.getValue()); + }); } } diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java new file mode 100644 index 00000000..46413fd2 --- /dev/null +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java @@ -0,0 +1,15 @@ +package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection; + +public class ReflectionUtils { + + public static boolean isBooleanProperty(Class clazz, String propertyName) { + String booleanGetterMethodName = + "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + try { + clazz.getMethod(booleanGetterMethodName); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } +} From f293f4f749dfbd1da5464783e2aff4c94bb9fd6b Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 11:29:15 +0100 Subject: [PATCH 03/42] Stable status before review --- rika2mqtt-flux-metrics-plugin/pom.xml | 63 --------------------------- 1 file changed, 63 deletions(-) diff --git a/rika2mqtt-flux-metrics-plugin/pom.xml b/rika2mqtt-flux-metrics-plugin/pom.xml index 93d12aa9..1e3bd4f6 100644 --- a/rika2mqtt-flux-metrics-plugin/pom.xml +++ b/rika2mqtt-flux-metrics-plugin/pom.xml @@ -25,12 +25,6 @@ 1.1.0 provided - - - - - - io.kamon kamon-bundle_2.13 @@ -47,21 +41,6 @@ - - - - - - - - - - - - - - - org.apache.maven.plugins maven-shade-plugin @@ -95,48 +74,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - maven-antrun-plugin 3.1.0 From f2af3238d2924464171978129204dd1431ce85d8 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 11:33:26 +0100 Subject: [PATCH 04/42] Fix format --- .../rika2mqtt/plugins/api/v1/model/UpdatableControls.java | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java index 6fbca363..cd850f23 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java @@ -31,6 +31,7 @@ public class UpdatableControls { private Long revision; + /** Fields below (some, or all) can be sent using MQTT command to pilot RIKA. */ private Integer operatingMode; From 22785f1e32397d2d4f65ecf3cf99c0df54acd188 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 11:47:32 +0100 Subject: [PATCH 05/42] Add missing header --- plugins-api/pom.xml | 24 +++++++++++++++++++ .../plugins/api/v1/Rika2MqttPlugin.java | 22 +++++++++++++++++ .../plugins/api/v1/StoveStatusExtension.java | 22 +++++++++++++++++ .../plugins/api/v1/model/ConvectionFan.java | 22 +++++++++++++++++ .../plugins/api/v1/model/ParameterDebug.java | 22 +++++++++++++++++ .../api/v1/model/ParameterErrorCount.java | 22 +++++++++++++++++ .../plugins/api/v1/model/TimeDefinition.java | 22 +++++++++++++++++ .../plugins/api/v1/model/TimeRange.java | 22 +++++++++++++++++ plugins-internal/pom.xml | 24 +++++++++++++++++++ .../internal/v1/Rika2MqttPluginManager.java | 22 +++++++++++++++++ .../v1/event/PolledStoveStatusEvent.java | 22 +++++++++++++++++ .../v1/event/Rika2MqttPluginEvent.java | 22 +++++++++++++++++ .../internal/v1/mapper/ControlsMapper.java | 22 +++++++++++++++++ .../v1/mapper/ConvectionFanMapper.java | 22 +++++++++++++++++ .../internal/v1/mapper/SensorsMapper.java | 22 +++++++++++++++++ .../internal/v1/mapper/StoveStatusMapper.java | 22 +++++++++++++++++ .../internal/v1/mapper/TimeRangeMapper.java | 22 +++++++++++++++++ .../v1/pf4j/Pf4jPluginManagerConfig.java | 22 +++++++++++++++++ .../v1/Rika2MqttPluginManagerTest.java | 22 +++++++++++++++++ .../v1/mapper/ControlsMapperTest.java | 22 +++++++++++++++++ .../v1/mapper/ConvectionFanMapperTest.java | 22 +++++++++++++++++ .../internal/v1/mapper/SensorsMapperTest.java | 22 +++++++++++++++++ .../v1/mapper/StoveStatusMapperTest.java | 22 +++++++++++++++++ .../v1/mapper/TimeRangeMapperTest.java | 22 +++++++++++++++++ .../helper/ControlsMapperEmptyImpl.java | 22 +++++++++++++++++ .../mapper/helper/SensorsMapperEmptyImpl.java | 22 +++++++++++++++++ rika2mqtt-example-plugin/pom.xml | 24 +++++++++++++++++++ .../plugins/example/ExampleHook.java | 22 +++++++++++++++++ .../plugins/example/ExamplePlugin.java | 22 +++++++++++++++++ rika2mqtt-flux-metrics-plugin/pom.xml | 24 +++++++++++++++++++ .../metrics/Rika2MqttInfluxMetricsPlugin.java | 22 +++++++++++++++++ .../influxdb/metrics/StoveStatusHook.java | 22 +++++++++++++++++ .../metrics/reflection/ReflectionUtils.java | 22 +++++++++++++++++ 33 files changed, 734 insertions(+) diff --git a/plugins-api/pom.xml b/plugins-api/pom.xml index 28172d16..7b7cb96d 100644 --- a/plugins-api/pom.xml +++ b/plugins-api/pom.xml @@ -1,4 +1,28 @@ + diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java index d9fb3e46..b52e65cc 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1; import org.pf4j.Plugin; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java index 1668ce79..70e2be52 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java index 8d047082..140e2983 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; import lombok.*; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java index 037bef37..6ae331a0 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; import lombok.Builder; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java index 12c3c49c..bfbd3f52 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; import lombok.Builder; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java index d67763a9..f4075003 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; import lombok.Builder; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java index 8246b9a4..e45420ad 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; import lombok.Builder; diff --git a/plugins-internal/pom.xml b/plugins-internal/pom.xml index fe0a105f..d19cfad9 100644 --- a/plugins-internal/pom.xml +++ b/plugins-internal/pom.xml @@ -1,4 +1,28 @@ + diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java index eb5d5b6f..07098996 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1; import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java index e62cb3b8..eeb59010 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.event; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java index 51d49845..ab415429 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.event; public interface Rika2MqttPluginEvent {} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java index 7aec220b..1f4121ec 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static com.google.common.collect.ImmutableList.of; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java index a3ad0fea..f9b7f882 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.mapstruct.ReportingPolicy.IGNORE; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java index 0e12addd..881ed024 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.mapstruct.ReportingPolicy.IGNORE; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java index f577bad1..27b24347 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java index aed9a07e..9ca4b054 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static java.lang.Integer.parseInt; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java index 1b787c06..ff736192 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; import org.pf4j.DefaultPluginManager; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java index e0754a50..e08de912 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1; import static org.mockito.Mockito.*; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java index 614a707c..a00a147b 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java index c6e6aaf9..44c3c436 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java index dc5c5094..5dcc84a4 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java index 4b76f5ea..e20d8c27 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java index 06f541a2..67309234 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java index a7ae7c1b..edc19394 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.helper; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Controls; diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java index 7fcc7167..172a9a30 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.helper; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Sensors; diff --git a/rika2mqtt-example-plugin/pom.xml b/rika2mqtt-example-plugin/pom.xml index 3b4e0810..91dac6e1 100644 --- a/rika2mqtt-example-plugin/pom.xml +++ b/rika2mqtt-example-plugin/pom.xml @@ -1,4 +1,28 @@ + diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java index d616353c..d725507c 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.example; import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java index 7ad9946f..3950d3d9 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.example; import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; diff --git a/rika2mqtt-flux-metrics-plugin/pom.xml b/rika2mqtt-flux-metrics-plugin/pom.xml index 1e3bd4f6..d9b289ff 100644 --- a/rika2mqtt-flux-metrics-plugin/pom.xml +++ b/rika2mqtt-flux-metrics-plugin/pom.xml @@ -1,4 +1,28 @@ + diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java index 4ce8ae65..7d622fc3 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; import com.typesafe.config.ConfigFactory; diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index c7f52bfa..f3f506c3 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; import static dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection.ReflectionUtils.isBooleanProperty; diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java index 46413fd2..c6e5d5d5 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection; public class ReflectionUtils { From 31ac6199718d6c43e1e5738434e48275e82fb1bb Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 11:48:59 +0100 Subject: [PATCH 06/42] Document useful command lines --- DEV.md | 5 +++++ pom.xml | 1 + 2 files changed, 6 insertions(+) diff --git a/DEV.md b/DEV.md index 0c24dd2e..46270e85 100644 --- a/DEV.md +++ b/DEV.md @@ -20,6 +20,11 @@ To ensure the code style is applied, mvn will automatically format the files at You can if desired install the auto formatter in your IDE: https://github.com/google/google-java-format/blob/master/README.md#using-the-formatter +A few useful command lines to run before opening a PR (otherwise the CI will fail): + +`mvn com.spotify.fmt:fmt-maven-plugin:format` - Autofix java files so that they follow google code style +`mvn license:format` - Autofix missing headers in files + ## Local run There is a docker-compose.yml diff --git a/pom.xml b/pom.xml index aa98e75d..13b50124 100644 --- a/pom.xml +++ b/pom.xml @@ -366,6 +366,7 @@ src/main/resources/** Makefile lombok.config + disabled.txt From 51b2c446e1c2781324b5211e7fea1c07ef694510 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 11:50:12 +0100 Subject: [PATCH 07/42] exclude disabled.txt --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 13b50124..447650b9 100644 --- a/pom.xml +++ b/pom.xml @@ -366,7 +366,7 @@ src/main/resources/** Makefile lombok.config - disabled.txt + plugins/disabled.txt From 06cdd5f0f8e975fca087f7a37e7748a11a23e1a2 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 13:36:07 +0100 Subject: [PATCH 08/42] Add unit tests for API module --- .../api/v1/model/TimeDefinitionTest.java | 59 +++++++++++++++++++ .../plugins/api/v1/model/TimeRangeTest.java | 48 +++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinitionTest.java create mode 100644 plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRangeTest.java diff --git a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinitionTest.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinitionTest.java new file mode 100644 index 00000000..237c7a15 --- /dev/null +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinitionTest.java @@ -0,0 +1,59 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** Test class */ +class TimeDefinitionTest { + + @Test + void asDecimalShouldReturnTheDecimalValueOfTheGivenHoursAndMinutes() { + // GIVEN + final var timeDefinition = TimeDefinition.builder().hours(12).minutes(30).build(); + + // WHEN + final var timeAsDecimal = timeDefinition.asDecimal(); + + // THEN + assertThat(timeAsDecimal).isEqualTo(12.5); + } + + @Test + void toStringShouldReturnHumanReadableTimeGivenHoursAndMinutesBeingLessThan10() { + // GIVEN + final var timeDefinition = + TimeDefinition.builder() + .hours(12) + .minutes(5) // intentionally a number < 10 so that we can check that "05" is printed + .build(); + + // WHEN + final var stringValue = timeDefinition.toString(); + + // THEN + assertThat(stringValue).isEqualTo("12:05"); + } +} diff --git a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRangeTest.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRangeTest.java new file mode 100644 index 00000000..0831fccf --- /dev/null +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRangeTest.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; + +/** Test class */ +class TimeRangeTest { + + @Test + void toStringShouldReturnADeterminedFormat() { + + // GIVEN + final var from = TimeDefinition.builder().hours(12).minutes(5).build(); + + final var to = TimeDefinition.builder().hours(15).minutes(30).build(); + + final var timeRange = TimeRange.builder().from(from).to(to).build(); + + // WHEN + final var result = timeRange.toString(); + + // THEN + assertThat(result).isEqualTo("12:05 - 15:30"); + } +} From 930db5170948a1f4ddc061ea1997b5a8ef965218 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 14:48:53 +0100 Subject: [PATCH 09/42] Cleanup example plugin code --- .../rika2mqtt/plugins/example/ExampleHook.java | 12 +++++++++++- .../rika2mqtt/plugins/example/ExamplePlugin.java | 13 +++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java index d725507c..f08dc720 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java @@ -24,13 +24,23 @@ import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; +import lombok.extern.flogger.Flogger; import org.pf4j.Extension; +import static dev.cookiecode.rika2mqtt.plugins.example.ExamplePlugin.PLUGIN_NAME; + @Extension +@Flogger public class ExampleHook implements StoveStatusExtension { @Override public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { - System.out.println("EXAMPLE PLUGIN >> onPollStoveStatusSucceed invoked"); + log.atInfo() + .log("%s >> onPollStoveStatusSucceed invoked", PLUGIN_NAME); + } + + public void stop() { + log.atInfo() + .log("%s >> %s stopped", PLUGIN_NAME, ExampleHook.class.getSimpleName()); } } diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java index 3950d3d9..ba4d0892 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java @@ -23,19 +23,24 @@ package dev.cookiecode.rika2mqtt.plugins.example; import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; +import lombok.extern.flogger.Flogger; +@Flogger public class ExamplePlugin extends Rika2MqttPlugin { - ExampleHook hook; + static final String PLUGIN_NAME = "ExamplePlugin"; + + private ExampleHook hook; @Override public void start() { - System.out.println("EXAMPLE PLUGIN >> STARTED"); - hook = new ExampleHook(); + log.atInfo().log("%s >> STARTED", PLUGIN_NAME); + hook = new ExampleHook(); // instantiate/register hook (otherwise it won't be triggered) } @Override public void stop() { - System.out.println("EXAMPLE PLUGIN >> STOPPED"); + log.atInfo().log("%s >> STOPPED", PLUGIN_NAME); + hook.stop(); } } From f2bc82cc1a489fb4cf76a013d09e32f13ca93956 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 15:39:43 +0100 Subject: [PATCH 10/42] Refactore/add some tests --- .../influxdb/metrics/StoveStatusHook.java | 238 ++++++++---------- .../metrics/reflection/ReflectionUtils.java | 37 ++- .../reflection/ReflectionUtilsTest.java | 99 ++++++++ .../metrics/reflection/TestedClass.java | 10 + 4 files changed, 248 insertions(+), 136 deletions(-) create mode 100644 rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java create mode 100644 rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/TestedClass.java diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index f3f506c3..b52be24f 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -22,8 +22,9 @@ */ package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; -import static dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection.ReflectionUtils.isBooleanProperty; +import static dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection.ReflectionUtils.*; import static java.lang.Boolean.TRUE; +import static java.util.Optional.ofNullable; import static java.util.concurrent.TimeUnit.HOURS; import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; @@ -62,6 +63,101 @@ public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { exportProperty(stoveStatus, "lastConfirmedRevision", Long.class); } + private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { + + exportProperty(stoveStatus, "sensors.inputRoomTemperature", Double.class); + exportProperty(stoveStatus, "sensors.inputFlameTemperature", Integer.class); + exportProperty(stoveStatus, "sensors.inputBakeTemperature", Integer.class); + exportProperty(stoveStatus, "sensors.statusError", Integer.class); + exportProperty(stoveStatus, "sensors.statusSubError", Integer.class); + exportProperty(stoveStatus, "sensors.statusWarning", Integer.class); + exportProperty(stoveStatus, "sensors.statusService", Integer.class); + exportProperty(stoveStatus, "sensors.outputDischargeMotor", Integer.class); + exportProperty(stoveStatus, "sensors.outputDischargeCurrent", Integer.class); + exportProperty(stoveStatus, "sensors.outputIdFan", Integer.class); + exportProperty(stoveStatus, "sensors.outputIdFanTarget", Integer.class); + exportProperty(stoveStatus, "sensors.outputInsertionMotor", Integer.class); + exportProperty(stoveStatus, "sensors.outputInsertionCurrent", Integer.class); + exportProperty(stoveStatus, "sensors.outputAirFlaps", Integer.class); + exportProperty(stoveStatus, "sensors.outputAirFlapsTargetPosition", Integer.class); + exportProperty(stoveStatus, "sensors.outputBurnBackFlapMagnet", Boolean.class); + exportProperty(stoveStatus, "sensors.outputGridMotor", Boolean.class); + exportProperty(stoveStatus, "sensors.outputIgnition", Boolean.class); + exportProperty(stoveStatus, "sensors.inputUpperTemperatureLimiter", Boolean.class); + exportProperty(stoveStatus, "sensors.inputPressureSwitch", Boolean.class); + exportProperty(stoveStatus, "sensors.inputPressureSensor", Integer.class); + exportProperty(stoveStatus, "sensors.inputGridContact", Boolean.class); + exportProperty(stoveStatus, "sensors.inputDoor", Boolean.class); + exportProperty(stoveStatus, "sensors.inputCover", Boolean.class); + exportProperty(stoveStatus, "sensors.inputExternalRequest", Boolean.class); + exportProperty(stoveStatus, "sensors.inputBurnBackFlapSwitch", Boolean.class); + exportProperty(stoveStatus, "sensors.inputFlueGasFlapSwitch", Boolean.class); + exportProperty(stoveStatus, "sensors.inputBoardTemperature", Double.class); + exportProperty(stoveStatus, "sensors.inputCurrentStage", Integer.class); + exportProperty(stoveStatus, "sensors.inputTargetStagePid", Integer.class); + exportProperty(stoveStatus, "sensors.inputCurrentStagePid", Integer.class); + + exportProperty(stoveStatus, "sensors.statusMainState", Integer.class); + exportProperty(stoveStatus, "sensors.statusSubState", Integer.class); + exportProperty(stoveStatus, "sensors.statusWifiStrength", Integer.class); + exportProperty(stoveStatus, "sensors.parameterEcoModePossible", Boolean.class); + exportProperty(stoveStatus, "sensors.parameterFabricationNumber", Integer.class); + exportProperty(stoveStatus, "sensors.parameterStoveTypeNumber", Integer.class); + exportProperty(stoveStatus, "sensors.parameterLanguageNumber", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionMainBoard", Integer.class); + + exportProperty(stoveStatus, "sensors.parameterVersionTft", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionWifi", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionMainBoardBootLoader", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionTftBootLoader", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionWifiBootLoader", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionMainBoardSub", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionTftSub", Integer.class); + exportProperty(stoveStatus, "sensors.parameterVersionWifiSub", Integer.class); + + exportProperty(stoveStatus, "sensors.parameterRuntimePellets", Integer.class); + exportProperty(stoveStatus, "sensors.parameterRuntimeLogs", Integer.class); + exportProperty(stoveStatus, "sensors.parameterFeedRateTotal", Integer.class); + exportProperty(stoveStatus, "sensors.parameterFeedRateService", Integer.class); + exportProperty(stoveStatus, "sensors.parameterServiceCountdownKg", Integer.class); + exportProperty(stoveStatus, "sensors.parameterServiceCountdownTime", Integer.class); + exportProperty(stoveStatus, "sensors.parameterIgnitionCount", Integer.class); + exportProperty(stoveStatus, "sensors.parameterOnOffCycleCount", Integer.class); + exportProperty(stoveStatus, "sensors.parameterFlameSensorOffset", Integer.class); + exportProperty(stoveStatus, "sensors.parameterPressureSensorOffset", Integer.class); + + exportProperty(stoveStatus, "sensors.statusHeatingTimesNotProgrammed", Boolean.class); + exportProperty(stoveStatus, "sensors.statusFrostStarted", Boolean.class); + exportProperty(stoveStatus, "sensors.parameterSpiralMotorsTuning", Integer.class); + exportProperty(stoveStatus, "sensors.parameterIdFanTuning", Integer.class); + exportProperty(stoveStatus, "sensors.parameterCleanIntervalBig", Integer.class); + exportProperty(stoveStatus, "sensors.parameterKgTillCleaning", Integer.class); + + // following metrics are requiring special treatment + stoveStatus + .getSensors() + .getParametersErrorCount() + .forEach( + parameterErrorCount -> + Kamon.gauge("sensors.parameterErrorCount") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(ERROR_NUMBER, parameterErrorCount.getNumber()) // extra tag + .update(parameterErrorCount.getValue())); + + stoveStatus + .getSensors() + .getParametersDebug() + .forEach( + parameterDebug -> { + Kamon.gauge("sensors.parameterDebug") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag + .update(parameterDebug.getValue()); + }); + } + private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { exportProperty(stoveStatus, "controls.revision", Long.class); exportProperty(stoveStatus, "controls.on", Boolean.class); @@ -223,62 +319,31 @@ private Optional getPropertyValue( @NonNull StoveStatus stoveStatus, @NonNull final String propertyName) { if (propertyName.startsWith("sensors.")) { try { - - Class clazz = Sensors.class; final var shortName = propertyName.replace("sensors.", ""); - String getterMethodName; - if (isBooleanProperty(clazz, shortName)) { - getterMethodName = - "is" + shortName.substring(0, 1).toUpperCase() + shortName.substring(1); - } else { - getterMethodName = - "get" + shortName.substring(0, 1).toUpperCase() + shortName.substring(1); - } - // Get a reference to the append() method - var getterMethod = clazz.getMethod(getterMethodName); + final var getterMethod = getPropertyGetterMethod(Sensors.class, shortName); var result = getterMethod.invoke(stoveStatus.getSensors()); - return Optional.ofNullable(result.toString()); + return ofNullable(result.toString()); } catch (Exception ex) { log.atSevere().withCause(ex).log("Could not get property %s", propertyName); return Optional.empty(); } } else if (propertyName.startsWith("controls.")) { try { - final var shortName = propertyName.replace("controls.", ""); - - Class clazz = Controls.class; - - String getterMethodName; - if (isBooleanProperty(clazz, shortName)) { - getterMethodName = - "is" + shortName.substring(0, 1).toUpperCase() + shortName.substring(1); - } else { - getterMethodName = - "get" + shortName.substring(0, 1).toUpperCase() + shortName.substring(1); - } - - // Get a reference to the append() method - var getterMethod = clazz.getMethod(getterMethodName); - var result = Optional.ofNullable(getterMethod.invoke(stoveStatus.getControls())); - return result.map(Object::toString); + var getterMethod = getPropertyGetterMethod(Controls.class, shortName); + return ofNullable(getterMethod.invoke(stoveStatus.getControls())).map(Object::toString); } catch (Exception ex) { log.atSevere().withCause(ex).log("Could not get property %s", propertyName); return Optional.empty(); } } else { try { - - Class clazz = StoveStatus.class; - - // Get a reference to the append() method - var getterMethodName = - "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); - var getterMethod = clazz.getMethod(getterMethodName); + final var shortName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + final var getterMethod = getPropertyGetterMethod(StoveStatus.class, shortName); var result = getterMethod.invoke(stoveStatus); - return Optional.ofNullable(result.toString()); + return ofNullable(result.toString()); } catch (Exception ex) { log.atSevere().withCause(ex).log("Could not get property %s", propertyName); return Optional.empty(); @@ -305,99 +370,4 @@ private Optional getBooleanPropertyValue( @NonNull StoveStatus stoveStatus, @NonNull final String propertyName) { return getPropertyValue(stoveStatus, propertyName).map(Boolean::valueOf); } - - private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { - - exportProperty(stoveStatus, "sensors.inputRoomTemperature", Double.class); - exportProperty(stoveStatus, "sensors.inputFlameTemperature", Integer.class); - exportProperty(stoveStatus, "sensors.inputBakeTemperature", Integer.class); - exportProperty(stoveStatus, "sensors.statusError", Integer.class); - exportProperty(stoveStatus, "sensors.statusSubError", Integer.class); - exportProperty(stoveStatus, "sensors.statusWarning", Integer.class); - exportProperty(stoveStatus, "sensors.statusService", Integer.class); - exportProperty(stoveStatus, "sensors.outputDischargeMotor", Integer.class); - exportProperty(stoveStatus, "sensors.outputDischargeCurrent", Integer.class); - exportProperty(stoveStatus, "sensors.outputIdFan", Integer.class); - exportProperty(stoveStatus, "sensors.outputIdFanTarget", Integer.class); - exportProperty(stoveStatus, "sensors.outputInsertionMotor", Integer.class); - exportProperty(stoveStatus, "sensors.outputInsertionCurrent", Integer.class); - exportProperty(stoveStatus, "sensors.outputAirFlaps", Integer.class); - exportProperty(stoveStatus, "sensors.outputAirFlapsTargetPosition", Integer.class); - exportProperty(stoveStatus, "sensors.outputBurnBackFlapMagnet", Boolean.class); - exportProperty(stoveStatus, "sensors.outputGridMotor", Boolean.class); - exportProperty(stoveStatus, "sensors.outputIgnition", Boolean.class); - exportProperty(stoveStatus, "sensors.inputUpperTemperatureLimiter", Boolean.class); - exportProperty(stoveStatus, "sensors.inputPressureSwitch", Boolean.class); - exportProperty(stoveStatus, "sensors.inputPressureSensor", Integer.class); - exportProperty(stoveStatus, "sensors.inputGridContact", Boolean.class); - exportProperty(stoveStatus, "sensors.inputDoor", Boolean.class); - exportProperty(stoveStatus, "sensors.inputCover", Boolean.class); - exportProperty(stoveStatus, "sensors.inputExternalRequest", Boolean.class); - exportProperty(stoveStatus, "sensors.inputBurnBackFlapSwitch", Boolean.class); - exportProperty(stoveStatus, "sensors.inputFlueGasFlapSwitch", Boolean.class); - exportProperty(stoveStatus, "sensors.inputBoardTemperature", Double.class); - exportProperty(stoveStatus, "sensors.inputCurrentStage", Integer.class); - exportProperty(stoveStatus, "sensors.inputTargetStagePid", Integer.class); - exportProperty(stoveStatus, "sensors.inputCurrentStagePid", Integer.class); - - exportProperty(stoveStatus, "sensors.statusMainState", Integer.class); - exportProperty(stoveStatus, "sensors.statusSubState", Integer.class); - exportProperty(stoveStatus, "sensors.statusWifiStrength", Integer.class); - exportProperty(stoveStatus, "sensors.parameterEcoModePossible", Boolean.class); - exportProperty(stoveStatus, "sensors.parameterFabricationNumber", Integer.class); - exportProperty(stoveStatus, "sensors.parameterStoveTypeNumber", Integer.class); - exportProperty(stoveStatus, "sensors.parameterLanguageNumber", Integer.class); - exportProperty(stoveStatus, "sensors.parameterVersionMainBoard", Integer.class); - - exportProperty(stoveStatus, "sensors.parameterVersionTft", Integer.class); - exportProperty(stoveStatus, "sensors.parameterVersionWifi", Integer.class); - exportProperty(stoveStatus, "sensors.parameterVersionMainBoardBootLoader", Integer.class); - exportProperty(stoveStatus, "sensors.parameterVersionTftBootLoader", Integer.class); - exportProperty(stoveStatus, "sensors.parameterVersionWifiBootLoader", Integer.class); - exportProperty(stoveStatus, "sensors.parameterVersionMainBoardSub", Integer.class); - exportProperty(stoveStatus, "sensors.parameterVersionTftSub", Integer.class); - exportProperty(stoveStatus, "sensors.parameterVersionWifiSub", Integer.class); - - exportProperty(stoveStatus, "sensors.parameterRuntimePellets", Integer.class); - exportProperty(stoveStatus, "sensors.parameterRuntimeLogs", Integer.class); - exportProperty(stoveStatus, "sensors.parameterFeedRateTotal", Integer.class); - exportProperty(stoveStatus, "sensors.parameterFeedRateService", Integer.class); - exportProperty(stoveStatus, "sensors.parameterServiceCountdownKg", Integer.class); - exportProperty(stoveStatus, "sensors.parameterServiceCountdownTime", Integer.class); - exportProperty(stoveStatus, "sensors.parameterIgnitionCount", Integer.class); - exportProperty(stoveStatus, "sensors.parameterOnOffCycleCount", Integer.class); - exportProperty(stoveStatus, "sensors.parameterFlameSensorOffset", Integer.class); - exportProperty(stoveStatus, "sensors.parameterPressureSensorOffset", Integer.class); - - exportProperty(stoveStatus, "sensors.statusHeatingTimesNotProgrammed", Boolean.class); - exportProperty(stoveStatus, "sensors.statusFrostStarted", Boolean.class); - exportProperty(stoveStatus, "sensors.parameterSpiralMotorsTuning", Integer.class); - exportProperty(stoveStatus, "sensors.parameterIdFanTuning", Integer.class); - exportProperty(stoveStatus, "sensors.parameterCleanIntervalBig", Integer.class); - exportProperty(stoveStatus, "sensors.parameterKgTillCleaning", Integer.class); - - // following metrics are requiring special treatment - stoveStatus - .getSensors() - .getParametersErrorCount() - .forEach( - parameterErrorCount -> - Kamon.gauge("sensors.parameterErrorCount") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .withTag(ERROR_NUMBER, parameterErrorCount.getNumber()) // extra tag - .update(parameterErrorCount.getValue())); - - stoveStatus - .getSensors() - .getParametersDebug() - .forEach( - parameterDebug -> { - Kamon.gauge("sensors.parameterDebug") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag - .update(parameterDebug.getValue()); - }); - } } diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java index c6e5d5d5..a73a3b75 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java @@ -22,16 +22,49 @@ */ package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection; +import lombok.NonNull; + +import java.lang.reflect.Method; + public class ReflectionUtils { - public static boolean isBooleanProperty(Class clazz, String propertyName) { - String booleanGetterMethodName = + /** + * @param clazz the class owning the property + * @param propertyName the property name + * @return true when the property of the class is a primitive boolean type + */ + public static boolean isBooleanPrimitiveProperty(@NonNull Class clazz, @NonNull String propertyName) { + final var booleanGetterMethodName = "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); try { + // TODO: exception driven code... code smell (more elegant solution should be privileged) clazz.getMethod(booleanGetterMethodName); return true; } catch (NoSuchMethodException e) { return false; } } + + public static String getPropertyGetterMethodName(@NonNull Class clazz, @NonNull String propertyName){ + if(propertyName.isEmpty()){ + throw new IllegalArgumentException("propertyName is empty. Please provide a valid propertyName."); + } + String getterMethodName; + + if (isBooleanPrimitiveProperty(clazz, propertyName)) { + getterMethodName = + "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + } else { + getterMethodName = + "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + } + + return getterMethodName; + } + + public static Method getPropertyGetterMethod(@NonNull Class clazz, @NonNull String propertyName) throws NoSuchMethodException { + final var propertyGetterMethodName = getPropertyGetterMethodName(clazz, propertyName); + + return clazz.getMethod(propertyGetterMethodName); + } } diff --git a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java new file mode 100644 index 00000000..177e767c --- /dev/null +++ b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java @@ -0,0 +1,99 @@ +package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection; + +import org.junit.jupiter.api.Test; + +import static dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection.ReflectionUtils.getPropertyGetterMethodName; +import static dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection.ReflectionUtils.isBooleanPrimitiveProperty; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Test class + */ +class ReflectionUtilsTest { + + @Test + void isBooleanPropertyShouldReturnFalseGivenThePropertyIsABooleanObjectNotPrimitive(){ + + // GIVEN + final var clazz = TestedClass.class; + final var booleanObjectPropertyName = "enabled"; + + // WHEN + final var result = isBooleanPrimitiveProperty(clazz, booleanObjectPropertyName); + + // THEN + assertThat(result).isFalse(); + } + + @Test + void isBooleanPropertyShouldReturnTrueGivenThePropertyIsABooleanPrimitive(){ + + // GIVEN + final var clazz = TestedClass.class; + final var booleanPrimitivePropertyName = "disabled"; + + // WHEN + final var result = isBooleanPrimitiveProperty(clazz, booleanPrimitivePropertyName); + + // THEN + assertThat(result).isTrue(); + } + + @Test + void isBooleanPropertyShouldReturnFalseGivenThePropertyIsAString(){ + + // GIVEN + final var clazz = TestedClass.class; + final var stringPropertyName = "name"; + + // WHEN + final var result = isBooleanPrimitiveProperty(clazz, stringPropertyName); + + // THEN + assertThat(result).isFalse(); + } + + @Test + void getPropertyGetterMethodNameShouldReturnAGetterStartingWithIsGivenAPrimitiveBooleanProperty(){ + + // GIVEN + final var clazz = TestedClass.class; + final var primitiveBooleanPropertyName = "disabled"; + + // WHEN + final var result = getPropertyGetterMethodName(clazz, primitiveBooleanPropertyName); + + // THEN + assertThat(result).startsWith("is"); + assertThat(result).isEqualTo("isDisabled"); + } + + @Test + void getPropertyGetterMethodNameShouldReturnAGetterStartingWithIsGivenAnObjectBooleanProperty(){ + + // GIVEN + final var clazz = TestedClass.class; + final var objectBooleanPropertyName = "enabled"; + + // WHEN + final var result = getPropertyGetterMethodName(clazz, objectBooleanPropertyName); + + // THEN + assertThat(result).startsWith("get"); + assertThat(result).isEqualTo("getEnabled"); + } + + @Test + void getPropertyGetterMethodNameShouldThrowAnExceptionGivenEmptyPropertyName(){ + // GIVEN + final var clazz = TestedClass.class; + final var emptyPropertyName = ""; + + // WHEN/THEN + assertThatThrownBy(() -> + getPropertyGetterMethodName(clazz, emptyPropertyName) + ) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/TestedClass.java b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/TestedClass.java new file mode 100644 index 00000000..e0acb57b --- /dev/null +++ b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/TestedClass.java @@ -0,0 +1,10 @@ +package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection; + +import lombok.Getter; + +@Getter +public class TestedClass { + private Boolean enabled; + private boolean disabled; + private String name; +} From 3de59a885ee9e09b30dd24b26d7fdb18a25432bc Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 15:41:52 +0100 Subject: [PATCH 11/42] Refactore/add some tests --- .../plugins/example/ExampleHookTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java diff --git a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java new file mode 100644 index 00000000..873f8913 --- /dev/null +++ b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java @@ -0,0 +1,15 @@ +package dev.cookiecode.rika2mqtt.plugins.example; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class ExampleHookTest { + + @Test + void dummyTest(){ + assertThat(true).isTrue(); // TODO: add more complexity to the example plugin so that we can write real tests + } + +} From 07fa0b7e4f605c3036b6f15be715f54e7496bf1f Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 15:42:00 +0100 Subject: [PATCH 12/42] Refactore/add some tests --- .../cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java index 873f8913..e6fa723c 100644 --- a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java +++ b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java @@ -3,7 +3,6 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; class ExampleHookTest { From 8463522720f6004cc2ac837f385e98f0d9393b84 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 16:16:32 +0100 Subject: [PATCH 13/42] Refactore/add some tests --- .../plugins/example/ExampleHook.java | 10 +- .../plugins/example/ExampleHookTest.java | 37 ++++- .../influxdb/metrics/StoveStatusHook.java | 41 ++--- .../metrics/reflection/ReflectionUtils.java | 23 +-- .../reflection/ReflectionUtilsTest.java | 153 ++++++++++-------- .../metrics/reflection/TestedClass.java | 28 +++- 6 files changed, 179 insertions(+), 113 deletions(-) diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java index f08dc720..2d2732fc 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java @@ -22,25 +22,23 @@ */ package dev.cookiecode.rika2mqtt.plugins.example; +import static dev.cookiecode.rika2mqtt.plugins.example.ExamplePlugin.PLUGIN_NAME; + import dev.cookiecode.rika2mqtt.plugins.api.v1.StoveStatusExtension; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; import lombok.extern.flogger.Flogger; import org.pf4j.Extension; -import static dev.cookiecode.rika2mqtt.plugins.example.ExamplePlugin.PLUGIN_NAME; - @Extension @Flogger public class ExampleHook implements StoveStatusExtension { @Override public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { - log.atInfo() - .log("%s >> onPollStoveStatusSucceed invoked", PLUGIN_NAME); + log.atInfo().log("%s >> onPollStoveStatusSucceed invoked", PLUGIN_NAME); } public void stop() { - log.atInfo() - .log("%s >> %s stopped", PLUGIN_NAME, ExampleHook.class.getSimpleName()); + log.atInfo().log("%s >> %s stopped", PLUGIN_NAME, ExampleHook.class.getSimpleName()); } } diff --git a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java index e6fa723c..828a980d 100644 --- a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java +++ b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java @@ -1,14 +1,37 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.example; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; -class ExampleHookTest { +import org.junit.jupiter.api.Test; - @Test - void dummyTest(){ - assertThat(true).isTrue(); // TODO: add more complexity to the example plugin so that we can write real tests - } +class ExampleHookTest { + @Test + void dummyTest() { + assertThat(true) + .isTrue(); // TODO: add more complexity to the example plugin so that we can write real + // tests + } } diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index b52be24f..b52ffabe 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -135,27 +135,27 @@ private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { // following metrics are requiring special treatment stoveStatus - .getSensors() - .getParametersErrorCount() - .forEach( - parameterErrorCount -> - Kamon.gauge("sensors.parameterErrorCount") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .withTag(ERROR_NUMBER, parameterErrorCount.getNumber()) // extra tag - .update(parameterErrorCount.getValue())); + .getSensors() + .getParametersErrorCount() + .forEach( + parameterErrorCount -> + Kamon.gauge("sensors.parameterErrorCount") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(ERROR_NUMBER, parameterErrorCount.getNumber()) // extra tag + .update(parameterErrorCount.getValue())); stoveStatus - .getSensors() - .getParametersDebug() - .forEach( - parameterDebug -> { - Kamon.gauge("sensors.parameterDebug") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag - .update(parameterDebug.getValue()); - }); + .getSensors() + .getParametersDebug() + .forEach( + parameterDebug -> { + Kamon.gauge("sensors.parameterDebug") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag + .update(parameterDebug.getValue()); + }); } private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { @@ -339,7 +339,8 @@ private Optional getPropertyValue( } } else { try { - final var shortName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + final var shortName = + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); final var getterMethod = getPropertyGetterMethod(StoveStatus.class, shortName); var result = getterMethod.invoke(stoveStatus); diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java index a73a3b75..05c30833 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java @@ -22,9 +22,8 @@ */ package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection; -import lombok.NonNull; - import java.lang.reflect.Method; +import lombok.NonNull; public class ReflectionUtils { @@ -33,7 +32,8 @@ public class ReflectionUtils { * @param propertyName the property name * @return true when the property of the class is a primitive boolean type */ - public static boolean isBooleanPrimitiveProperty(@NonNull Class clazz, @NonNull String propertyName) { + public static boolean isBooleanPrimitiveProperty( + @NonNull Class clazz, @NonNull String propertyName) { final var booleanGetterMethodName = "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); try { @@ -45,25 +45,28 @@ public static boolean isBooleanPrimitiveProperty(@NonNull Class clazz, @NonNu } } - public static String getPropertyGetterMethodName(@NonNull Class clazz, @NonNull String propertyName){ - if(propertyName.isEmpty()){ - throw new IllegalArgumentException("propertyName is empty. Please provide a valid propertyName."); + public static String getPropertyGetterMethodName( + @NonNull Class clazz, @NonNull String propertyName) { + if (propertyName.isEmpty()) { + throw new IllegalArgumentException( + "propertyName is empty. Please provide a valid propertyName."); } String getterMethodName; if (isBooleanPrimitiveProperty(clazz, propertyName)) { getterMethodName = - "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + "is" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); } else { getterMethodName = - "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); } return getterMethodName; } - public static Method getPropertyGetterMethod(@NonNull Class clazz, @NonNull String propertyName) throws NoSuchMethodException { - final var propertyGetterMethodName = getPropertyGetterMethodName(clazz, propertyName); + public static Method getPropertyGetterMethod( + @NonNull Class clazz, @NonNull String propertyName) throws NoSuchMethodException { + final var propertyGetterMethodName = getPropertyGetterMethodName(clazz, propertyName); return clazz.getMethod(propertyGetterMethodName); } diff --git a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java index 177e767c..9c5aa5d3 100644 --- a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java +++ b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java @@ -1,99 +1,118 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection; -import org.junit.jupiter.api.Test; - import static dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection.ReflectionUtils.getPropertyGetterMethodName; import static dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection.ReflectionUtils.isBooleanPrimitiveProperty; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -/** - * Test class - */ +import org.junit.jupiter.api.Test; + +/** Test class */ class ReflectionUtilsTest { - @Test - void isBooleanPropertyShouldReturnFalseGivenThePropertyIsABooleanObjectNotPrimitive(){ + @Test + void isBooleanPropertyShouldReturnFalseGivenThePropertyIsABooleanObjectNotPrimitive() { - // GIVEN - final var clazz = TestedClass.class; - final var booleanObjectPropertyName = "enabled"; + // GIVEN + final var clazz = TestedClass.class; + final var booleanObjectPropertyName = "enabled"; - // WHEN - final var result = isBooleanPrimitiveProperty(clazz, booleanObjectPropertyName); + // WHEN + final var result = isBooleanPrimitiveProperty(clazz, booleanObjectPropertyName); - // THEN - assertThat(result).isFalse(); - } + // THEN + assertThat(result).isFalse(); + } - @Test - void isBooleanPropertyShouldReturnTrueGivenThePropertyIsABooleanPrimitive(){ + @Test + void isBooleanPropertyShouldReturnTrueGivenThePropertyIsABooleanPrimitive() { - // GIVEN - final var clazz = TestedClass.class; - final var booleanPrimitivePropertyName = "disabled"; + // GIVEN + final var clazz = TestedClass.class; + final var booleanPrimitivePropertyName = "disabled"; - // WHEN - final var result = isBooleanPrimitiveProperty(clazz, booleanPrimitivePropertyName); + // WHEN + final var result = isBooleanPrimitiveProperty(clazz, booleanPrimitivePropertyName); - // THEN - assertThat(result).isTrue(); - } + // THEN + assertThat(result).isTrue(); + } - @Test - void isBooleanPropertyShouldReturnFalseGivenThePropertyIsAString(){ + @Test + void isBooleanPropertyShouldReturnFalseGivenThePropertyIsAString() { - // GIVEN - final var clazz = TestedClass.class; - final var stringPropertyName = "name"; + // GIVEN + final var clazz = TestedClass.class; + final var stringPropertyName = "name"; - // WHEN - final var result = isBooleanPrimitiveProperty(clazz, stringPropertyName); + // WHEN + final var result = isBooleanPrimitiveProperty(clazz, stringPropertyName); - // THEN - assertThat(result).isFalse(); - } + // THEN + assertThat(result).isFalse(); + } - @Test - void getPropertyGetterMethodNameShouldReturnAGetterStartingWithIsGivenAPrimitiveBooleanProperty(){ + @Test + void + getPropertyGetterMethodNameShouldReturnAGetterStartingWithIsGivenAPrimitiveBooleanProperty() { - // GIVEN - final var clazz = TestedClass.class; - final var primitiveBooleanPropertyName = "disabled"; + // GIVEN + final var clazz = TestedClass.class; + final var primitiveBooleanPropertyName = "disabled"; - // WHEN - final var result = getPropertyGetterMethodName(clazz, primitiveBooleanPropertyName); + // WHEN + final var result = getPropertyGetterMethodName(clazz, primitiveBooleanPropertyName); - // THEN - assertThat(result).startsWith("is"); - assertThat(result).isEqualTo("isDisabled"); - } + // THEN + assertThat(result).startsWith("is"); + assertThat(result).isEqualTo("isDisabled"); + } - @Test - void getPropertyGetterMethodNameShouldReturnAGetterStartingWithIsGivenAnObjectBooleanProperty(){ + @Test + void getPropertyGetterMethodNameShouldReturnAGetterStartingWithIsGivenAnObjectBooleanProperty() { - // GIVEN - final var clazz = TestedClass.class; - final var objectBooleanPropertyName = "enabled"; + // GIVEN + final var clazz = TestedClass.class; + final var objectBooleanPropertyName = "enabled"; - // WHEN - final var result = getPropertyGetterMethodName(clazz, objectBooleanPropertyName); + // WHEN + final var result = getPropertyGetterMethodName(clazz, objectBooleanPropertyName); - // THEN - assertThat(result).startsWith("get"); - assertThat(result).isEqualTo("getEnabled"); - } + // THEN + assertThat(result).startsWith("get"); + assertThat(result).isEqualTo("getEnabled"); + } - @Test - void getPropertyGetterMethodNameShouldThrowAnExceptionGivenEmptyPropertyName(){ - // GIVEN - final var clazz = TestedClass.class; - final var emptyPropertyName = ""; + @Test + void getPropertyGetterMethodNameShouldThrowAnExceptionGivenEmptyPropertyName() { + // GIVEN + final var clazz = TestedClass.class; + final var emptyPropertyName = ""; - // WHEN/THEN - assertThatThrownBy(() -> - getPropertyGetterMethodName(clazz, emptyPropertyName) - ) + // WHEN/THEN + assertThatThrownBy(() -> getPropertyGetterMethodName(clazz, emptyPropertyName)) .isInstanceOf(IllegalArgumentException.class); - } + } } diff --git a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/TestedClass.java b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/TestedClass.java index e0acb57b..0a460a59 100644 --- a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/TestedClass.java +++ b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/TestedClass.java @@ -1,10 +1,32 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.reflection; import lombok.Getter; @Getter public class TestedClass { - private Boolean enabled; - private boolean disabled; - private String name; + private Boolean enabled; + private boolean disabled; + private String name; } From d8e27ecff5b738026376bd2974fc983b975eb114 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 16:19:03 +0100 Subject: [PATCH 14/42] Refactore/add some tests --- .../cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java index 828a980d..334cd1bf 100644 --- a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java +++ b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java @@ -32,6 +32,6 @@ class ExampleHookTest { void dummyTest() { assertThat(true) .isTrue(); // TODO: add more complexity to the example plugin so that we can write real - // tests + // tests } } From 69516b97c3bfbe2ee92aa8737c69e12cefb1194e Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 16:35:54 +0100 Subject: [PATCH 15/42] Adapt for sonar analysis --- plugins-api/pom.xml | 2 ++ plugins-internal/pom.xml | 2 ++ rika2mqtt-example-plugin/pom.xml | 2 ++ rika2mqtt-flux-metrics-plugin/pom.xml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/plugins-api/pom.xml b/plugins-api/pom.xml index 7b7cb96d..32901483 100644 --- a/plugins-api/pom.xml +++ b/plugins-api/pom.xml @@ -40,6 +40,8 @@ ${java.sdk.version} ${java.sdk.version} ${source.encoding} + + ${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId} diff --git a/plugins-internal/pom.xml b/plugins-internal/pom.xml index d19cfad9..05d18ad4 100644 --- a/plugins-internal/pom.xml +++ b/plugins-internal/pom.xml @@ -40,6 +40,8 @@ ${java.sdk.version} ${java.sdk.version} ${source.encoding} + + ${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId} diff --git a/rika2mqtt-example-plugin/pom.xml b/rika2mqtt-example-plugin/pom.xml index 91dac6e1..7bf51cf3 100644 --- a/rika2mqtt-example-plugin/pom.xml +++ b/rika2mqtt-example-plugin/pom.xml @@ -40,6 +40,8 @@ ${java.sdk.version} ${java.sdk.version} ${source.encoding} + + ${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId} diff --git a/rika2mqtt-flux-metrics-plugin/pom.xml b/rika2mqtt-flux-metrics-plugin/pom.xml index d9b289ff..816a0109 100644 --- a/rika2mqtt-flux-metrics-plugin/pom.xml +++ b/rika2mqtt-flux-metrics-plugin/pom.xml @@ -41,6 +41,8 @@ ${java.sdk.version} ${source.encoding} 2.5.9 + + ${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId} From 3196db753187b21b06d14687be4d1a6e7e86828b Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 16:53:32 +0100 Subject: [PATCH 16/42] Adapt for sonar analysis --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e80671f8..9a641395 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,5 +56,9 @@ jobs: bridge/target mqtt/target rika-firenet/target + plugins-api/target + plugins-internal/target + rika2mqtt-example-plugin/target + rika2mqtt-flux-metrics-plugin/target retention-days: 1 if-no-files-found: error From b28b6b2b8061075ec053b778130fbc37ce586f33 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 17:11:24 +0100 Subject: [PATCH 17/42] Fix sonar issues --- .../plugins/internal/v1/mapper/ControlsMapper.java | 2 +- .../plugins/influxdb/metrics/StoveStatusHook.java | 9 ++++----- .../influxdb/metrics/reflection/ReflectionUtils.java | 2 ++ .../metrics/reflection/ReflectionUtilsTest.java | 10 ++++++---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java index 1f4121ec..edaa18cb 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java @@ -116,7 +116,7 @@ default void afterMapping( // debugs API Object Oriented way var debugs = - ImmutableList.of( + List.of( source.getDebug0(), source.getDebug1(), source.getDebug2(), diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index b52ffabe..a0117338 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -50,6 +50,7 @@ public class StoveStatusHook implements StoveStatusExtension { private static final String TIME_RANGE_INDEX = "TIME_RANGE_INDEX"; private static final String ERROR_NUMBER = "ERROR_NUMBER"; private static final String DEBUG_NUMBER = "DEBUG_NUMBER"; + private static final String COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED = "Could not export property %s, it could not be retrieved."; @Override public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { @@ -274,7 +275,7 @@ private void exportProperty( .update(value), () -> log.atWarning().log( - "Could not export property %s, it could not be retrieved.", propertyName)); + COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); } else if (returnType == Integer.class) { getIntegerPropertyValue(stoveStatus, propertyName) .ifPresentOrElse( @@ -285,8 +286,7 @@ private void exportProperty( .update(value), () -> log.atWarning().log( - "Could not export property %s, it could not be retrieved.", propertyName)); - ; + COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); } else if (returnType == Long.class) { getLongPropertyValue(stoveStatus, propertyName) .ifPresentOrElse( @@ -297,8 +297,7 @@ private void exportProperty( .update(value), () -> log.atWarning().log( - "Could not export property %s, it could not be retrieved.", propertyName)); - ; + COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); } else if (returnType == Boolean.class) { getBooleanPropertyValue(stoveStatus, propertyName) .map(value -> value == TRUE ? 1 : 0) diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java index 05c30833..ed726bc4 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtils.java @@ -24,7 +24,9 @@ import java.lang.reflect.Method; import lombok.NonNull; +import lombok.experimental.UtilityClass; +@UtilityClass public class ReflectionUtils { /** diff --git a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java index 9c5aa5d3..2097d598 100644 --- a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java +++ b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java @@ -86,8 +86,9 @@ void isBooleanPropertyShouldReturnFalseGivenThePropertyIsAString() { final var result = getPropertyGetterMethodName(clazz, primitiveBooleanPropertyName); // THEN - assertThat(result).startsWith("is"); - assertThat(result).isEqualTo("isDisabled"); + assertThat(result) + .startsWith("is") + .isEqualTo("isDisabled"); } @Test @@ -101,8 +102,9 @@ void getPropertyGetterMethodNameShouldReturnAGetterStartingWithIsGivenAnObjectBo final var result = getPropertyGetterMethodName(clazz, objectBooleanPropertyName); // THEN - assertThat(result).startsWith("get"); - assertThat(result).isEqualTo("getEnabled"); + assertThat(result) + .startsWith("get") + .isEqualTo("getEnabled"); } @Test From 0d70b41db7b95361231d741651f67ca219ff34c2 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 17:13:33 +0100 Subject: [PATCH 18/42] Fix sonar issues --- .../rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java index edaa18cb..2220ae49 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java @@ -26,7 +26,6 @@ import static java.time.DayOfWeek.*; import static org.mapstruct.ReportingPolicy.IGNORE; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Controls; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeRange; From 6461ccf0e1bdea4ed362e6e05b9836e977e3a067 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 17:16:25 +0100 Subject: [PATCH 19/42] Fix sonar issues --- .../plugins/influxdb/metrics/StoveStatusHook.java | 9 +++++---- .../influxdb/metrics/reflection/ReflectionUtilsTest.java | 8 ++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index a0117338..5447a49c 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -50,7 +50,8 @@ public class StoveStatusHook implements StoveStatusExtension { private static final String TIME_RANGE_INDEX = "TIME_RANGE_INDEX"; private static final String ERROR_NUMBER = "ERROR_NUMBER"; private static final String DEBUG_NUMBER = "DEBUG_NUMBER"; - private static final String COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED = "Could not export property %s, it could not be retrieved."; + private static final String COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED = + "Could not export property %s, it could not be retrieved."; @Override public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { @@ -275,7 +276,7 @@ private void exportProperty( .update(value), () -> log.atWarning().log( - COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); + COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); } else if (returnType == Integer.class) { getIntegerPropertyValue(stoveStatus, propertyName) .ifPresentOrElse( @@ -286,7 +287,7 @@ private void exportProperty( .update(value), () -> log.atWarning().log( - COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); + COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); } else if (returnType == Long.class) { getLongPropertyValue(stoveStatus, propertyName) .ifPresentOrElse( @@ -297,7 +298,7 @@ private void exportProperty( .update(value), () -> log.atWarning().log( - COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); + COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED, propertyName)); } else if (returnType == Boolean.class) { getBooleanPropertyValue(stoveStatus, propertyName) .map(value -> value == TRUE ? 1 : 0) diff --git a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java index 2097d598..339f9ceb 100644 --- a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java +++ b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/reflection/ReflectionUtilsTest.java @@ -86,9 +86,7 @@ void isBooleanPropertyShouldReturnFalseGivenThePropertyIsAString() { final var result = getPropertyGetterMethodName(clazz, primitiveBooleanPropertyName); // THEN - assertThat(result) - .startsWith("is") - .isEqualTo("isDisabled"); + assertThat(result).startsWith("is").isEqualTo("isDisabled"); } @Test @@ -102,9 +100,7 @@ void getPropertyGetterMethodNameShouldReturnAGetterStartingWithIsGivenAnObjectBo final var result = getPropertyGetterMethodName(clazz, objectBooleanPropertyName); // THEN - assertThat(result) - .startsWith("get") - .isEqualTo("getEnabled"); + assertThat(result).startsWith("get").isEqualTo("getEnabled"); } @Test From 6eaaba38c83d6729ddb49022f56e376a525a59a9 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Mon, 18 Dec 2023 17:27:20 +0100 Subject: [PATCH 20/42] Fix sonar issues --- .../plugins/internal/v1/mapper/ControlsMapper.java | 4 ++-- .../plugins/influxdb/metrics/StoveStatusHook.java | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java index 2220ae49..48d8d2a1 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java @@ -22,8 +22,8 @@ */ package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; -import static com.google.common.collect.ImmutableList.of; import static java.time.DayOfWeek.*; +import static java.util.List.of; import static org.mapstruct.ReportingPolicy.IGNORE; import com.google.common.collect.ImmutableMap; @@ -115,7 +115,7 @@ default void afterMapping( // debugs API Object Oriented way var debugs = - List.of( + of( source.getDebug0(), source.getDebug1(), source.getDebug2(), diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index 5447a49c..fff74116 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -52,6 +52,7 @@ public class StoveStatusHook implements StoveStatusExtension { private static final String DEBUG_NUMBER = "DEBUG_NUMBER"; private static final String COULD_NOT_EXPORT_PROPERTY_S_IT_COULD_NOT_BE_RETRIEVED = "Could not export property %s, it could not be retrieved."; + private static final String COULD_NOT_GET_PROPERTY_S = "Could not get property %s"; @Override public void onPollStoveStatusSucceed(StoveStatus stoveStatus) { @@ -325,7 +326,7 @@ private Optional getPropertyValue( var result = getterMethod.invoke(stoveStatus.getSensors()); return ofNullable(result.toString()); } catch (Exception ex) { - log.atSevere().withCause(ex).log("Could not get property %s", propertyName); + log.atSevere().withCause(ex).log(COULD_NOT_GET_PROPERTY_S, propertyName); return Optional.empty(); } } else if (propertyName.startsWith("controls.")) { @@ -334,7 +335,7 @@ private Optional getPropertyValue( var getterMethod = getPropertyGetterMethod(Controls.class, shortName); return ofNullable(getterMethod.invoke(stoveStatus.getControls())).map(Object::toString); } catch (Exception ex) { - log.atSevere().withCause(ex).log("Could not get property %s", propertyName); + log.atSevere().withCause(ex).log(COULD_NOT_GET_PROPERTY_S, propertyName); return Optional.empty(); } } else { @@ -346,7 +347,7 @@ private Optional getPropertyValue( var result = getterMethod.invoke(stoveStatus); return ofNullable(result.toString()); } catch (Exception ex) { - log.atSevere().withCause(ex).log("Could not get property %s", propertyName); + log.atSevere().withCause(ex).log(COULD_NOT_GET_PROPERTY_S, propertyName); return Optional.empty(); } } From 428cf5cd2dc1d0d89213af1327304bbe7a605098 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Wed, 20 Dec 2023 09:47:51 +0100 Subject: [PATCH 21/42] Introduce @Beta annotation to mark features that should not be used for production --- .../java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java | 8 ++++++++ .../rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java | 5 +++++ .../rika2mqtt/plugins/api/v1/StoveStatusExtension.java | 2 ++ .../rika2mqtt/plugins/api/v1/model/Controls.java | 3 +++ .../rika2mqtt/plugins/api/v1/model/ConvectionFan.java | 2 ++ .../rika2mqtt/plugins/api/v1/model/ParameterDebug.java | 2 ++ .../plugins/api/v1/model/ParameterErrorCount.java | 2 ++ .../rika2mqtt/plugins/api/v1/model/Sensors.java | 3 +++ .../rika2mqtt/plugins/api/v1/model/StoveId.java | 3 +++ .../rika2mqtt/plugins/api/v1/model/StoveStatus.java | 2 ++ .../rika2mqtt/plugins/api/v1/model/TimeDefinition.java | 2 ++ .../rika2mqtt/plugins/api/v1/model/TimeRange.java | 2 ++ .../rika2mqtt/plugins/api/v1/model/UpdatableControls.java | 3 +++ 13 files changed, 39 insertions(+) create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java new file mode 100644 index 00000000..149a6855 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java @@ -0,0 +1,8 @@ +package dev.cookiecode.rika2mqtt.plugins.api; + +/** + * Document beta features that might be removed/updated with breaking changes + * use it carefully being aware of this + */ +public @interface Beta { +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java index b52e65cc..3ced7e07 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java @@ -22,6 +22,11 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import org.pf4j.Plugin; +/** + * Base class for Rika2Mqtt plugins + */ +@Beta public class Rika2MqttPlugin extends Plugin {} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java index 70e2be52..06e1b882 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java @@ -22,9 +22,11 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; import org.pf4j.ExtensionPoint; +@Beta public interface StoveStatusExtension extends ExtensionPoint { /** diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java index 64995243..c16255be 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java @@ -27,6 +27,8 @@ import java.time.DayOfWeek; import java.util.List; import java.util.Map; + +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Builder; import lombok.Data; import lombok.Getter; @@ -36,6 +38,7 @@ */ @Data @Builder +@Beta public class Controls { private long revision; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java index 140e2983..d4e087e4 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java @@ -22,10 +22,12 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.model; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.*; @Data @Builder +@Beta public class ConvectionFan { private int identifier; private boolean active; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java index 6ae331a0..c6efe851 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java @@ -22,11 +22,13 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.model; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Builder; import lombok.Data; @Data @Builder +@Beta public class ParameterDebug { private int number; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java index bfbd3f52..a42f7e37 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java @@ -22,11 +22,13 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.model; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Builder; import lombok.Data; @Data @Builder +@Beta public class ParameterErrorCount { private int number; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java index 2e078e72..c5a158b3 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java @@ -25,6 +25,8 @@ import static lombok.AccessLevel.NONE; import java.util.List; + +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Builder; import lombok.Data; import lombok.Getter; @@ -34,6 +36,7 @@ */ @Data @Builder +@Beta public class Sensors { private Double inputRoomTemperature; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveId.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveId.java index 8936c8df..b6f0392b 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveId.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveId.java @@ -22,9 +22,12 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.model; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; + /** * @author Sebastien Vermeille */ +@Beta public record StoveId(Long id) { public static StoveId of(Long id) { diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java index 5091d979..98e7fe17 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java @@ -22,12 +22,14 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.model; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Data; /** * @author Sebastien Vermeille */ @Data +@Beta public class StoveStatus { private String name; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java index f4075003..bc677d5e 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java @@ -22,6 +22,7 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.model; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -30,6 +31,7 @@ @Builder @Getter @EqualsAndHashCode +@Beta public class TimeDefinition { private final int hours; private final int minutes; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java index e45420ad..04cfb889 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java @@ -22,6 +22,7 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.model; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -29,6 +30,7 @@ @Builder @Getter @EqualsAndHashCode +@Beta public class TimeRange { private final TimeDefinition from; private final TimeDefinition to; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java index cd850f23..2fda1841 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java @@ -24,10 +24,13 @@ import static lombok.AccessLevel.NONE; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Data; import lombok.NoArgsConstructor; @Data +@Beta +// TODO: check if this is the way we want (no usage yet) public class UpdatableControls { private Long revision; From a9c4eeb442472c37ae3f9e0f8a3324d4e4774c87 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 13:18:19 +0100 Subject: [PATCH 22/42] Introduce configuration plugin (wip) --- DEV.md | 2 +- Makefile | 5 +- .../cookiecode/rika2mqtt/bridge/Bridge.java | 4 +- .../rika2mqtt/bridge/BridgeTest.java | 4 +- docs.json | 3 +- docs/write-a-plugin.mdx | 15 ++ .../rika2mqtt/plugins/api/Beta.java | 29 +++- .../rika2mqtt/plugins/api/PluginContext.java | 15 ++ .../plugins/api/v1/PluginConfiguration.java | 24 ++++ .../plugins/api/v1/Rika2MqttPlugin.java | 15 +- .../v1/annotations/ConfigurablePlugin.java | 47 ++++++ .../plugins/api/v1/annotations/Nullable.java | 26 ++++ .../InvalidPluginConfigurationException.java | 11 ++ .../api/v1/exceptions/PluginException.java | 12 ++ .../plugins/api/v1/model/Controls.java | 3 +- .../plugins/api/v1/model/Sensors.java | 3 +- .../OptionalPluginConfigurationParameter.java | 134 ++++++++++++++++++ .../plugins/PluginConfigurationParameter.java | 95 +++++++++++++ .../PluginConfigurationParameterBuilder.java | 69 +++++++++ .../RequiredPluginConfigurationParameter.java | 114 +++++++++++++++ ...nager.java => Rika2MqttPluginService.java} | 2 +- .../v1/pf4j/Pf4jPluginManagerConfig.java | 3 +- .../v1/pf4j/Rika2MqttPluginFactory.java | 24 ++++ .../v1/pf4j/Rika2MqttPluginManager.java | 122 ++++++++++++++++ ...t.java => Rika2MqttPluginServiceTest.java} | 10 +- pom.xml | 1 + rika2mqtt-example-plugin-using-config/pom.xml | 122 ++++++++++++++++ .../example/ExamplePluginUsingConfig.java | 97 +++++++++++++ .../plugins/example/ExamplePlugin.java | 4 + .../metrics/Rika2MqttInfluxMetricsPlugin.java | 96 +++++++++++-- .../influxdb/metrics/StoveStatusHook.java | 2 +- .../Rika2MqttInfluxMetricsPluginTest.java | 92 ++++++++++++ scripts/codestyle.sh | 33 +++++ 33 files changed, 1199 insertions(+), 39 deletions(-) create mode 100644 docs/write-a-plugin.mdx create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/Nullable.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java create mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java rename plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/{Rika2MqttPluginManager.java => Rika2MqttPluginService.java} (98%) create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java rename plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/{Rika2MqttPluginManagerTest.java => Rika2MqttPluginServiceTest.java} (92%) create mode 100644 rika2mqtt-example-plugin-using-config/pom.xml create mode 100644 rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java create mode 100644 rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPluginTest.java create mode 100755 scripts/codestyle.sh diff --git a/DEV.md b/DEV.md index 46270e85..14953f91 100644 --- a/DEV.md +++ b/DEV.md @@ -50,7 +50,7 @@ Then you can run the project and enjoy :) If you want to run the project in pure CLI there is a `Makefile` available. You can run: - +* `make codestyle` * `make jar` * `make test` * `make docker` diff --git a/Makefile b/Makefile index 4ccbdaf9..5913de0a 100644 --- a/Makefile +++ b/Makefile @@ -10,5 +10,8 @@ test: ## execute tests docker: jar ## build docker image @./scripts/docker.sh +codestyle: ## format code, add missing headers, etc. + @./scripts/codestyle.sh + help: ## print this help - @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {gsub("\\\\n",sprintf("\n%22c",""), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) \ No newline at end of file + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {gsub("\\\\n",sprintf("\n%22c",""), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) diff --git a/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java b/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java index b257990c..f5911afc 100644 --- a/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java +++ b/bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java @@ -25,7 +25,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.gson.Gson; import dev.cookiecode.rika2mqtt.bridge.misc.EmailObfuscator; -import dev.cookiecode.rika2mqtt.plugins.internal.v1.Rika2MqttPluginManager; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.Rika2MqttPluginService; import dev.cookiecode.rika2mqtt.plugins.internal.v1.event.PolledStoveStatusEvent; import dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.StoveStatusMapper; import dev.cookiecode.rika2mqtt.rika.firenet.RikaFirenetService; @@ -82,7 +82,7 @@ public class Bridge { private final Gson gson; private final StoveStatusMapper stoveStatusMapper; - private final Rika2MqttPluginManager pluginManager; + private final Rika2MqttPluginService pluginManager; private final ApplicationEventPublisher applicationEventPublisher; diff --git a/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java b/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java index 107e1d40..99f0d744 100644 --- a/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java +++ b/bridge/src/test/java/dev/cookiecode/rika2mqtt/bridge/BridgeTest.java @@ -36,7 +36,7 @@ import com.google.gson.Gson; import dev.cookiecode.rika2mqtt.bridge.misc.EmailObfuscator; -import dev.cookiecode.rika2mqtt.plugins.internal.v1.Rika2MqttPluginManager; +import dev.cookiecode.rika2mqtt.plugins.internal.v1.Rika2MqttPluginService; import dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper.StoveStatusMapper; import dev.cookiecode.rika2mqtt.rika.firenet.RikaFirenetService; import dev.cookiecode.rika2mqtt.rika.firenet.model.StoveId; @@ -71,7 +71,7 @@ class BridgeTest { @Mock Gson gson; @Mock StoveStatusMapper stoveStatusMapper; - @Mock Rika2MqttPluginManager pluginManager; + @Mock Rika2MqttPluginService pluginManager; @Mock ApplicationEventPublisher applicationEventPublisher; @InjectMocks @Spy Bridge bridge; diff --git a/docs.json b/docs.json index 8fa833bb..527b8a4a 100644 --- a/docs.json +++ b/docs.json @@ -13,7 +13,8 @@ ["Getting Started", [ ["Overview", "/"], ["Getting Started", "/getting-started"], - ["Configuration", "/configuration"] + ["Configuration", "/configuration"], + ["Plugin development", "/write-a-plugin"] ]], ["Examples", [ ["Publish stove status to MQTT", "/example-telemetry-mqtt"], diff --git a/docs/write-a-plugin.mdx b/docs/write-a-plugin.mdx new file mode 100644 index 00000000..61be0349 --- /dev/null +++ b/docs/write-a-plugin.mdx @@ -0,0 +1,15 @@ +# Write a plugin for RIKA2MQTT + +> API for plugins is currently under development and is marked `@Beta`. Be cautious that it can have breaking changes between future releases. + +RIKA2MQTT provide an API for plugin creators. + +A few modules in the project are demonstrating plugins: + +* rika2mqtt-example-plugin +* rika2mqtt-example-plugin-using-config + +And an official plugin `rika2mqtt-flux-metrics-plugin` that exports RIKA stove status to InfluxDB. + + +More documentation will follow in the future when the API is more stable and heavily tested. diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java index 149a6855..3b732a32 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java @@ -1,8 +1,29 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api; /** - * Document beta features that might be removed/updated with breaking changes - * use it carefully being aware of this + * Document beta features that might be removed/updated with breaking changes use it carefully being + * aware of this */ -public @interface Beta { -} +public @interface Beta {} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java new file mode 100644 index 00000000..55ea05e8 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java @@ -0,0 +1,15 @@ +package dev.cookiecode.rika2mqtt.plugins.api; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * An instance of this class is provided to plugins in their constructor. This class facilitates + * communication with Rika2Mqtt and plugin manager. + */ +@Getter +@NoArgsConstructor +public class PluginContext { + + private String name = "test"; +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java new file mode 100644 index 00000000..3f00bc57 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java @@ -0,0 +1,24 @@ +package dev.cookiecode.rika2mqtt.plugins.api.v1; + +import static java.util.Optional.ofNullable; + +import java.util.Map; +import java.util.Optional; +import lombok.*; + +@Builder +@Getter +@ToString +@EqualsAndHashCode +public class PluginConfiguration { + + private Map parameters; // paramName, value + + public String getParameter(@NonNull String parameter) { + return getOptionalParameter(parameter).orElseThrow(); + } + + public Optional getOptionalParameter(@NonNull String parameter) { + return ofNullable(parameters.get(parameter)); + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java index 3ced7e07..4f39dced 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java @@ -25,8 +25,15 @@ import dev.cookiecode.rika2mqtt.plugins.api.Beta; import org.pf4j.Plugin; -/** - * Base class for Rika2Mqtt plugins - */ +/** Base class for Rika2Mqtt plugins */ @Beta -public class Rika2MqttPlugin extends Plugin {} +public abstract class Rika2MqttPlugin extends Plugin { + + // protected final PluginContext pluginContext; + // + // protected Rika2MqttPlugin(PluginContext pluginContext) { + // super(); + // this.pluginContext = pluginContext; + // } + +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java new file mode 100644 index 00000000..7297e26e --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java @@ -0,0 +1,47 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.annotations; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.OptionalPluginConfigurationParameter; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.PluginConfigurationParameter; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.RequiredPluginConfigurationParameter; +import java.util.List; + +public interface ConfigurablePlugin { + + /** + * Declare parameters supported by a plugin. This allows to define some mandatory parameters, + * specify their types and default values. At startup when loading plugins Rika2Mqtt will check + * for availability of required parameters and display log errors in case of failure. + * + *

These parameters can be build using fluent coding via the following builders: {@link + * RequiredPluginConfigurationParameter#builder()}, {@link + * OptionalPluginConfigurationParameter#builder()} + * + * @return a list of plugin configuration parameters + */ + List declarePluginConfigurationParameters(); + + void onConfigurationLoaded(PluginConfiguration pluginConfiguration); +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/Nullable.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/Nullable.java new file mode 100644 index 00000000..d2f811b4 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/Nullable.java @@ -0,0 +1,26 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.annotations; + +/** Explicitly document that a parameter can be null */ +public @interface Nullable {} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java new file mode 100644 index 00000000..c3e76226 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java @@ -0,0 +1,11 @@ +package dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions; + +public class InvalidPluginConfigurationException extends PluginException { + public InvalidPluginConfigurationException(String message) { + super(message); + } + + public InvalidPluginConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java new file mode 100644 index 00000000..e465c054 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java @@ -0,0 +1,12 @@ +package dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions; + +public class PluginException extends Exception { + + public PluginException(String message) { + super(message); + } + + public PluginException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java index c16255be..dfb738ef 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Controls.java @@ -24,11 +24,10 @@ import static lombok.AccessLevel.NONE; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import java.time.DayOfWeek; import java.util.List; import java.util.Map; - -import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.Builder; import lombok.Data; import lombok.Getter; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java index c5a158b3..ec1ee1d1 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/Sensors.java @@ -24,9 +24,8 @@ import static lombok.AccessLevel.NONE; -import java.util.List; - import dev.cookiecode.rika2mqtt.plugins.api.Beta; +import java.util.List; import lombok.Builder; import lombok.Data; import lombok.Getter; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java new file mode 100644 index 00000000..74ab7768 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java @@ -0,0 +1,134 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins; + +import java.util.Optional; +import lombok.Getter; + +@Getter +public class OptionalPluginConfigurationParameter { + private final String parameterName; + private final String description; + private final Class valueType; + + private final String example; + private final Object defaultValue; + + private OptionalPluginConfigurationParameter(Builder builder) { + parameterName = builder.parameterName; + description = builder.description; + valueType = builder.valueType; + example = builder.example; + defaultValue = builder.defaultValue; + } + + public static IParameterName builder() { + return new Builder(); + } + + public interface IBuild { + IBuild withDefaultValue(Object val); + + IBuild withExample(String val); + + PluginConfigurationParameter build(); + } + + public interface IDefaultValue { + IBuild withDefaultValue(Object val); + } + + public interface IExample { + IBuild withExample(String val); + } + + public interface IValueType { + IBuild withValueType(Class val); + } + + public interface IDescription { + IValueType withDescription(String val); + } + + public interface IParameterName { + IDescription withParameterName(String val); + } + + public static final class Builder + implements IDefaultValue, IExample, IValueType, IDescription, IParameterName, IBuild { + private Object defaultValue; + private String example; + private Class valueType; + private String description; + private String parameterName; + + private Builder() {} + + @Override + public IBuild withDefaultValue(Object val) { + defaultValue = val; + return this; + } + + @Override + public IBuild withExample(String val) { + example = val; + return this; + } + + @Override + public IBuild withValueType(Class val) { + valueType = val; + return this; + } + + @Override + public IValueType withDescription(String val) { + description = val; + return this; + } + + @Override + public IDescription withParameterName(String val) { + parameterName = val; + return this; + } + + public PluginConfigurationParameter build() { + return asPluginConfigurationParameter(new OptionalPluginConfigurationParameter(this)); + } + } + + public Optional getDefaultValue() { + return Optional.ofNullable(defaultValue); + } + + public Optional getExample() { + return Optional.ofNullable(example); + } + + static PluginConfigurationParameter asPluginConfigurationParameter( + OptionalPluginConfigurationParameter optionalPluginConfigurationParameter) { + return new PluginConfigurationParameter(optionalPluginConfigurationParameter); + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java new file mode 100644 index 00000000..67d6d370 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java @@ -0,0 +1,95 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins; + +import dev.cookiecode.rika2mqtt.plugins.api.Beta; +import java.util.Optional; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode +@Beta +public class PluginConfigurationParameter { + + private final String parameterName; + private final String description; + private final Class valueType; + private final String example; + private final boolean required; + private final Object defaultValue; + + protected PluginConfigurationParameter( + @NonNull OptionalPluginConfigurationParameter optionalParameter) { + required = false; + parameterName = optionalParameter.getParameterName(); + description = optionalParameter.getDescription(); + valueType = optionalParameter.getValueType(); + example = optionalParameter.getExample().orElse(null); + defaultValue = optionalParameter.getDefaultValue().orElse(null); + + checkCoherence(); + } + + public PluginConfigurationParameter( + @NonNull RequiredPluginConfigurationParameter requiredParameter) { + required = true; + parameterName = requiredParameter.getParameterName(); + description = requiredParameter.getDescription(); + valueType = requiredParameter.getValueType(); + example = requiredParameter.getExample().orElse(null); + defaultValue = null; + } + + private void checkCoherence() { + if (this.required && this.defaultValue != null) { + throw new IllegalArgumentException( + String.format( + """ + Dear developer, please take position. + Either '%s' is optional and have a default value, + either it is required and the user must provide a value for it. + """, + this.parameterName)); + } + } + + public Optional getDefaultValue() { + return Optional.ofNullable(defaultValue); + } + + public Optional getExample() { + return Optional.ofNullable(example); + } + + public boolean isOptional() { + return !isRequired(); + } + + public static RequiredPluginConfigurationParameter.Builder newRequiredParameter() { + return new RequiredPluginConfigurationParameter.Builder(); + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java new file mode 100644 index 00000000..67c87530 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java @@ -0,0 +1,69 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins; + +import dev.cookiecode.rika2mqtt.plugins.api.Beta; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@EqualsAndHashCode +@Beta +public class PluginConfigurationParameterBuilder { + + private final boolean required; + + PluginConfigurationParameterBuilder(Builder builder) { + required = builder.required; + } + + public static IRequired builder() { + return new Builder(); + } + + public interface IBuild { + PluginConfigurationParameterBuilder build(); + } + + public interface IRequired { + IBuild withRequired(boolean val); + } + + public static final class Builder implements IRequired, IBuild { + private boolean required; + + private Builder() {} + + @Override + public IBuild withRequired(boolean val) { + required = val; + return this; + } + + public PluginConfigurationParameterBuilder build() { + return new PluginConfigurationParameterBuilder(this); + } + } +} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java new file mode 100644 index 00000000..16b194f6 --- /dev/null +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java @@ -0,0 +1,114 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins; + +import java.util.Optional; +import lombok.Getter; + +@Getter +public class RequiredPluginConfigurationParameter { + private final String parameterName; + private final String description; + private final Class valueType; + private String example; + + private RequiredPluginConfigurationParameter(Builder builder) { + parameterName = builder.parameterName; + description = builder.description; + valueType = builder.valueType; + example = builder.example; + } + + public static IParameterName builder() { + return new Builder(); + } + + public interface IBuild { + IBuild withExample(String val); + + PluginConfigurationParameter build(); + } + + public interface IExample { + IBuild withExample(String val); + } + + public interface IValueType { + IBuild withValueType(Class val); + } + + public interface IDescription { + IValueType withDescription(String val); + } + + public interface IParameterName { + IDescription withParameterName(String val); + } + + public static final class Builder + implements IExample, IValueType, IDescription, IParameterName, IBuild { + private String example; + private Class valueType; + private String description; + private String parameterName; + + Builder() {} + + @Override + public IBuild withExample(String val) { + example = val; + return this; + } + + @Override + public IBuild withValueType(Class val) { + valueType = val; + return this; + } + + @Override + public IValueType withDescription(String val) { + description = val; + return this; + } + + @Override + public IDescription withParameterName(String val) { + parameterName = val; + return this; + } + + public PluginConfigurationParameter build() { + return asPluginConfigurationParameter(new RequiredPluginConfigurationParameter(this)); + } + } + + public Optional getExample() { + return Optional.ofNullable(example); + } + + public static PluginConfigurationParameter asPluginConfigurationParameter( + RequiredPluginConfigurationParameter requiredPluginConfigurationParameter) { + return new PluginConfigurationParameter(requiredPluginConfigurationParameter); + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java similarity index 98% rename from plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java rename to plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java index 07098996..b095c0ff 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java @@ -38,7 +38,7 @@ @Service @RequiredArgsConstructor @Flogger -public class Rika2MqttPluginManager { +public class Rika2MqttPluginService { private final PluginManager pluginManager; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java index ff736192..424d5aaf 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java @@ -22,7 +22,6 @@ */ package dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; -import org.pf4j.DefaultPluginManager; import org.pf4j.PluginManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,6 +31,6 @@ public class Pf4jPluginManagerConfig { @Bean public PluginManager pluginManager() { - return new DefaultPluginManager(); + return new Rika2MqttPluginManager(); } } diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java new file mode 100644 index 00000000..fe6da201 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java @@ -0,0 +1,24 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; + +import dev.cookiecode.rika2mqtt.plugins.api.PluginContext; +import lombok.extern.flogger.Flogger; +import org.pf4j.DefaultPluginFactory; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; + +@Flogger +public class Rika2MqttPluginFactory extends DefaultPluginFactory { + + @Override + protected Plugin createInstance(Class pluginClass, PluginWrapper pluginWrapper) { + + final var pluginContext = new PluginContext(); + try { + final var constructor = pluginClass.getConstructor(PluginContext.class); + return (Plugin) constructor.newInstance(pluginContext); + } catch (Exception e) { + log.atSevere().withCause(e).log(e.getMessage()); + return null; + } + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java new file mode 100644 index 00000000..065af021 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -0,0 +1,122 @@ +package dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; +import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions.InvalidPluginConfigurationException; +import java.util.*; +import lombok.NonNull; +import lombok.extern.flogger.Flogger; +import org.pf4j.*; + +@Flogger +public class Rika2MqttPluginManager extends DefaultPluginManager { + // @Override + // protected PluginFactory createPluginFactory() { + // return new Rika2MqttPluginFactory(); + // } + + @Override + public void startPlugins() { + System.out.println("START PLUGINS"); + for (PluginWrapper pluginWrapper : resolvedPlugins) { + PluginState pluginState = pluginWrapper.getPluginState(); + if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { + try { + // configurable plugins + if (pluginWrapper.getPlugin() instanceof ConfigurablePlugin configurablePlugin) { + final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); + log.atInfo().log("Check plugin '%s' configuration", pluginName); + final var pluginConfiguration = loadPluginConfiguration(configurablePlugin); + if (isPluginConfigurationValid(configurablePlugin, pluginConfiguration)) { + log.atInfo().log("Start configurable plugin '%s'", pluginName); + // configurablePlugin.onConfigurationLoaded(pluginConfiguration); + ((ConfigurablePlugin) pluginWrapper.getPlugin()) + .onConfigurationLoaded(pluginConfiguration); + // todo: this line kills everything + pluginWrapper.getPlugin().start(); + pluginWrapper.setPluginState(PluginState.STARTED); + pluginWrapper.setFailedException(null); + startedPlugins.add(pluginWrapper); + } else { + log.atSevere().log( + "Plugin '%s' configuration is invalid. Aborting load of the plugin", pluginName); + stopPlugin(pluginWrapper.getPluginId()); + pluginWrapper.setPluginState(PluginState.FAILED); + pluginWrapper.setFailedException( + new InvalidPluginConfigurationException( + String.format( + "Plugin '%s' configuration is invalid. Aborting load of the plugin", + pluginName))); + } + } else { + log.atInfo().log("Start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); + pluginWrapper.getPlugin().start(); + pluginWrapper.setPluginState(PluginState.STARTED); + pluginWrapper.setFailedException(null); + startedPlugins.add(pluginWrapper); + } + } catch (Exception | LinkageError e) { + pluginWrapper.setPluginState(PluginState.FAILED); + pluginWrapper.setFailedException(e); + log.atSevere().withCause(e).log( + "Unable to start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); + } finally { + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } + } + } + } + + private boolean isPluginConfigurationValid( + @NonNull ConfigurablePlugin configurablePlugin, + @NonNull PluginConfiguration pluginConfiguration) { + + final var parameters = configurablePlugin.declarePluginConfigurationParameters(); + + final List errors = new ArrayList<>(); + + for (final var param : parameters) { + // check required params are provided + if (param.isRequired() + && pluginConfiguration.getParameter(param.getParameterName()).isEmpty()) { + errors.add( + String.format( + "Parameter '%s' is required for this plugin to work properly. However unable to find any ENV named: 'PLUGIN_%s' declaring any value", + param.getParameterName(), param.getParameterName())); + } + } + + if (errors.isEmpty()) { + return true; + } else { + log.atSevere().log("%s", errors); + return false; + } + } + + private PluginConfiguration loadPluginConfiguration(ConfigurablePlugin configurablePlugin) { + + Map configuration = new HashMap<>(); + + for (var parameter : configurablePlugin.declarePluginConfigurationParameters()) { + + var value = + getEnvironmentVariable("PLUGIN_" + parameter.getParameterName()) + .orElseGet( + () -> { + if (parameter.isOptional() && parameter.getDefaultValue().isPresent()) { + return parameter.getDefaultValue().get().toString(); + } else { + return null; + } + }); + configuration.put(parameter.getParameterName(), value); + } + + return PluginConfiguration.builder().parameters(configuration).build(); + } + + private Optional getEnvironmentVariable(@NonNull String environmentVariableName) { + return Optional.ofNullable(System.getenv(environmentVariableName)); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java similarity index 92% rename from plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java rename to plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java index e08de912..9bd7043a 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginManagerTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java @@ -37,9 +37,9 @@ /** Test class */ @ExtendWith(MockitoExtension.class) -class Rika2MqttPluginManagerTest { +class Rika2MqttPluginServiceTest { - @InjectMocks private Rika2MqttPluginManager rika2MqttPluginManager; + @InjectMocks private Rika2MqttPluginService rika2MqttPluginService; @Mock private PluginManager pluginManager; @@ -50,7 +50,7 @@ void startShouldInvokeLoadPlugins() { // nothing particular // WHEN - rika2MqttPluginManager.start(); + rika2MqttPluginService.start(); // THEN verify(pluginManager, times(1)).loadPlugins(); @@ -63,7 +63,7 @@ void startShouldInvokeStartPlugins() { // nothing particular // WHEN - rika2MqttPluginManager.start(); + rika2MqttPluginService.start(); // THEN verify(pluginManager, times(1)).startPlugins(); @@ -85,7 +85,7 @@ void startShouldInvokeStartPlugins() { when(pluginManager.getExtensions(StoveStatusExtension.class)).thenReturn(extensions); // WHEN - rika2MqttPluginManager.handlePolledStoveStatusEvent(event); + rika2MqttPluginService.handlePolledStoveStatusEvent(event); // THEN verify(extensionAlpha, times(1)).onPollStoveStatusSucceed(stoveStatus); diff --git a/pom.xml b/pom.xml index 447650b9..65610f41 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,7 @@ rika2mqtt-flux-metrics-plugin plugins-internal rika2mqtt-example-plugin + rika2mqtt-example-plugin-using-config diff --git a/rika2mqtt-example-plugin-using-config/pom.xml b/rika2mqtt-example-plugin-using-config/pom.xml new file mode 100644 index 00000000..8dfff8f5 --- /dev/null +++ b/rika2mqtt-example-plugin-using-config/pom.xml @@ -0,0 +1,122 @@ + + + + 4.0.0 + + dev.cookiecode + rika2mqtt-parent + 1.1.0 + + + rika2mqtt-example-plugin-using-config + + + ${basedir}/.. + ${java.sdk.version} + ${java.sdk.version} + ${source.encoding} + + ${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId} + + + + + dev.cookiecode + plugins-api + 1.1.0 + provided + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.6.0 + + + jar-with-dependencies + + ${project.artifactId}-${project.version} + false + false + + + true + true + + + dev.cookiecode.rika2mqtt.plugins.example.ExamplePluginUsingConfig + example-plugin-using-config + 0.0.1 + 2.0.0 + This plugin demonstrate usage of external configuration + Sebastien Vermeille + MIT + + + + + + + + make-assembly + package + + single + + + + + + + + maven-antrun-plugin + 3.1.0 + + + + copy-to-lib + + run + + package + + + + + + + + + + + diff --git a/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java b/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java new file mode 100644 index 00000000..cd3f5efc --- /dev/null +++ b/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java @@ -0,0 +1,97 @@ +package dev.cookiecode.rika2mqtt.plugins.example; + +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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. + */ + +import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; +import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.OptionalPluginConfigurationParameter; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.PluginConfigurationParameter; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.RequiredPluginConfigurationParameter; +import java.util.List; +import lombok.extern.flogger.Flogger; + +@Flogger +public class ExamplePluginUsingConfig extends Rika2MqttPlugin implements ConfigurablePlugin { + + static final String PLUGIN_NAME = "ExamplePlugin"; + + // protected ExamplePluginUsingConfig(PluginContext pluginContext) { + // super(pluginContext); + // } + + @Override + public void start() { + log.atInfo().log("%s >> STARTED", PLUGIN_NAME); + } + + @Override + public void stop() { + log.atInfo().log("%s >> STOPPED", PLUGIN_NAME); + } + + @Override + public List declarePluginConfigurationParameters() { + return List.of( + // Optional Parameters + + // exhaustiveDeclaration + OptionalPluginConfigurationParameter.builder() + .withParameterName("example.plugin.postgres.port") + .withDescription("Postgres server port") + .withValueType(Integer.class) + .withDefaultValue(5432) + .withExample("5432") + .build(), + + // short declaration (no default, no example) + OptionalPluginConfigurationParameter.builder() + .withParameterName("example.plugin.influxdb.auth-token") + .withDescription("Influxdb authentication token") + .withValueType(String.class) + .build(), + + // Required parameters + + // exhaustive declaration + RequiredPluginConfigurationParameter.builder() + .withParameterName("example.plugin.postgres.host") + .withDescription("Postgres server hostname or IP") + .withValueType(String.class) + .withExample("hostname, 127.0.0.1, 0.0.0.0") + .build(), + + // short declaration (no example) + RequiredPluginConfigurationParameter.builder() + .withParameterName("example.plugin.postgres.dbname") + .withDescription("Postgres database name") + .withValueType(String.class) + .build()); + } + + @Override + public void onConfigurationLoaded(PluginConfiguration pluginConfiguration) { + // config loaded + } +} diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java index ba4d0892..a5a40c86 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java @@ -32,6 +32,10 @@ public class ExamplePlugin extends Rika2MqttPlugin { private ExampleHook hook; + // protected ExamplePlugin(PluginContext pluginContext) { + // super(pluginContext); + // } + @Override public void start() { log.atInfo().log("%s >> STARTED", PLUGIN_NAME); diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java index 7d622fc3..0164c30b 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java @@ -22,37 +22,111 @@ */ package dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; +import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.OptionalPluginConfigurationParameter; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.PluginConfigurationParameter; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.RequiredPluginConfigurationParameter; import java.util.HashMap; +import java.util.List; import kamon.Kamon; /** A plugin to export rika2mqtt metrics to InfluxDB */ -public class Rika2MqttInfluxMetricsPlugin extends Rika2MqttPlugin { +public class Rika2MqttInfluxMetricsPlugin extends Rika2MqttPlugin implements ConfigurablePlugin { + private static final String INFLUXDB_HOSTNAME = "INFLUXDB_HOSTNAME"; + private static final String INFLUXDB_PORT = "INFLUXDB_PORT"; + private static final String INFLUXDB_DATABASE = "INFLUXDB_DATABASE"; + private static final String INFLUXDB_PROTOCOL = "INFLUXDB_PROTOCOL"; + private static final String INFLUXDB_AUTHENTICATION_TOKEN = "INFLUXDB_AUTHENTICATION_TOKEN"; + + private PluginConfiguration pluginConfiguration; + + private StoveStatusHook hook; @Override public void start() { Kamon.init(); + Kamon.reconfigure(loadConfig()); + Kamon.loadModules(); + + hook = new StoveStatusHook(); // need a field otherwise it doesnt work (TODO: check why) + } + + @Override + public void stop() { + log.atInfo().log("Stopping Kamon..."); + Kamon.stop(); + } + Config loadConfig() { var defaultConfig = Kamon.config(); // TODO: have to be customizable var props = new HashMap(); - props.put("kamon.influxdb.port", 8086); - props.put("kamon.influxdb.hostname", "0.0.0.0"); - props.put("kamon.influxdb.database", "rika2mqtt"); - props.put("kamon.influxdb.protocol", "http"); - props.put("kamon.influxdb.authentication.token", "admin-token"); + props.put("kamon.influxdb.port", pluginConfiguration.getParameter(INFLUXDB_PORT)); + props.put("kamon.influxdb.hostname", pluginConfiguration.getParameter(INFLUXDB_HOSTNAME)); + props.put("kamon.influxdb.database", pluginConfiguration.getParameter(INFLUXDB_DATABASE)); + props.put("kamon.influxdb.protocol", pluginConfiguration.getParameter(INFLUXDB_PROTOCOL)); + props.put( + "kamon.influxdb.authentication.token", + pluginConfiguration.getParameter(INFLUXDB_AUTHENTICATION_TOKEN)); var codeConfig = ConfigFactory.parseMap(props); var newConfig = codeConfig.withFallback(defaultConfig); - Kamon.reconfigure(newConfig); - Kamon.loadModules(); + return newConfig; } @Override - public void stop() { - log.atInfo().log("Stopping Kamon..."); - Kamon.stop(); + public List declarePluginConfigurationParameters() { + return List.of( + RequiredPluginConfigurationParameter.builder() + .withParameterName(INFLUXDB_HOSTNAME) + .withDescription("Hostname or IP of the influxdb server") + .withValueType(String.class) + .withExample("localhost, 127.0.0.1, 0.0.0.0") + .build(), + OptionalPluginConfigurationParameter.builder() + .withParameterName(INFLUXDB_PORT) + .withDescription("Port of influxdb server") + .withValueType(Integer.class) + .withExample("8086") + .withDefaultValue(8086) + .build(), + RequiredPluginConfigurationParameter.builder() + .withParameterName(INFLUXDB_DATABASE) + .withDescription("Name of the influxdb database") + .withValueType(String.class) + .withExample("rika2mqtt") + .build(), + OptionalPluginConfigurationParameter.builder() + .withParameterName(INFLUXDB_PROTOCOL) + .withDescription("Protocol used to communicate with influxdb") + .withValueType(String.class) + .withExample("http") + .withDefaultValue("http") + .build(), + RequiredPluginConfigurationParameter.builder() + .withParameterName(INFLUXDB_AUTHENTICATION_TOKEN) + .withDescription("Auth token for influxdb database") + .withValueType(String.class) + .build()); + } + + @Override + public void onConfigurationLoaded(PluginConfiguration pluginConfiguration) { + // log.atInfo().log(pluginConfiguration.toString()); + this.pluginConfiguration = pluginConfiguration; + System.out.println("aaaaaa"); + System.out.println("aaaaaa"); + System.out.println("aaaaaa"); + System.out.println("aaaaaa"); + System.out.println("aaaaaa"); + System.out.println("aaaaaa"); + System.out.println("aaaaaa"); + System.out.println("aaaaaa"); + System.out.println("aaaaaa"); } } diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index fff74116..34fc5b50 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -168,7 +168,7 @@ private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { exportProperty(stoveStatus, "controls.heatingPower", Integer.class); exportProperty(stoveStatus, "controls.targetTemperature", Integer.class); exportProperty(stoveStatus, "controls.bakeTemperature", Integer.class); - exportProperty(stoveStatus, "controls.ecoMode", Boolean.class); + exportProperty(stoveStatus, "controls.ecoModeEnabled", Boolean.class); exportProperty(stoveStatus, "controls.heatingTimesActiveForComfort", Boolean.class); exportProperty(stoveStatus, "controls.setBackTemperature", Integer.class); exportProperty(stoveStatus, "controls.frostProtectionActive", Boolean.class); diff --git a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPluginTest.java b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPluginTest.java new file mode 100644 index 00000000..41abe4e6 --- /dev/null +++ b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPluginTest.java @@ -0,0 +1,92 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.influxdb.metrics; + +import static org.mockito.Mockito.*; + +import com.typesafe.config.Config; +import dev.cookiecode.rika2mqtt.plugins.api.PluginContext; +import kamon.Kamon; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +/** Test class */ +@ExtendWith(MockitoExtension.class) +class Rika2MqttInfluxMetricsPluginTest { + + private Rika2MqttInfluxMetricsPlugin plugin; + + private PluginContext pluginContext; + + @BeforeEach + public void beforeEach() { + // pluginContext = mock(PluginContext.class); + // plugin = spy(new Rika2MqttInfluxMetricsPlugin(pluginContext)); + plugin = spy(new Rika2MqttInfluxMetricsPlugin()); + } + + @Mock private Config config; + + @Test + void startShouldInitKamon() { + // GIVEN + doReturn(config).when(plugin).loadConfig(); + + try (var mockedKamon = mockStatic(Kamon.class)) { + // WHEN + plugin.start(); + + // THEN + mockedKamon.verify(Kamon::init, times(1)); + } + } + + @Test + void startShouldLoadKamonModules() { + // GIVEN + doReturn(config).when(plugin).loadConfig(); + + try (var mockedKamon = mockStatic(Kamon.class)) { + // WHEN + plugin.start(); + + // THEN + mockedKamon.verify(Kamon::loadModules, times(1)); + } + } + + @Test + void stopShouldStopKamon() { + // GIVEN + try (var mockedKamon = mockStatic(Kamon.class)) { + // WHEN + plugin.stop(); + + // THEN + mockedKamon.verify(Kamon::stop, times(1)); + } + } +} diff --git a/scripts/codestyle.sh b/scripts/codestyle.sh new file mode 100755 index 00000000..fa17eb63 --- /dev/null +++ b/scripts/codestyle.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# +# The MIT License +# Copyright © 2022 Sebastien Vermeille +# +# 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. +# + + +set -e + +echo "Check missing headers" +./mvnw license:format + +echo "Check code style and auto format" +./mvnw com.spotify.fmt:fmt-maven-plugin:format + From bef7963721a8a6bb454e20765689183113ac7ac8 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 15:27:01 +0100 Subject: [PATCH 23/42] cleanup --- docs/write-a-plugin.mdx | 29 ++++++++++++ .../plugins/api/v1/Rika2MqttPlugin.java | 10 +++++ .../v1/annotations/ConfigurablePlugin.java | 3 -- .../v1/pf4j/Rika2MqttPluginManager.java | 45 ++++++++++--------- .../example/ExamplePluginUsingConfig.java | 6 --- .../plugins/example/ExamplePlugin.java | 4 -- .../metrics/Rika2MqttInfluxMetricsPlugin.java | 36 ++++----------- 7 files changed, 70 insertions(+), 63 deletions(-) diff --git a/docs/write-a-plugin.mdx b/docs/write-a-plugin.mdx index 61be0349..1efeb94c 100644 --- a/docs/write-a-plugin.mdx +++ b/docs/write-a-plugin.mdx @@ -13,3 +13,32 @@ And an official plugin `rika2mqtt-flux-metrics-plugin` that exports RIKA stove s More documentation will follow in the future when the API is more stable and heavily tested. + + +## Make a plugin configurable by the end user +Make your plugin main class implement `ConfigurablePlugin` + +### Define plugin parameters +Implement: `List declarePluginConfigurationParameters()` +Parameters can be built using RequiredPluginConfigurationParameter.builder() or OptionalPluginConfigurationParameter.builder() that provide guided assistance. + +### Retrieve user defined parameters for the plugin + +```java +class YourPlugin extends Rika2MqttPlugin { + + // some code ... + + start(){ + + var influxPort = getPluginConfigurationParameter(INFLUXDB_PORT); + + } + + +} +``` + +> Note: If a parameter is named: INFLUXDB_PORT, an ENV property will have to be passed to the rika2mqtt docker named `PLUGIN_INFLUXDB_PORT=8086` (it must add an extra `PLUGIN_` this help to keep plugin properties separated.) + +Voila! diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java index 4f39dced..7bca196c 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java @@ -23,6 +23,7 @@ package dev.cookiecode.rika2mqtt.plugins.api.v1; import dev.cookiecode.rika2mqtt.plugins.api.Beta; +import lombok.NonNull; import org.pf4j.Plugin; /** Base class for Rika2Mqtt plugins */ @@ -36,4 +37,13 @@ public abstract class Rika2MqttPlugin extends Plugin { // this.pluginContext = pluginContext; // } + private PluginConfiguration pluginConfiguration; + + public void preStart(@NonNull PluginConfiguration pluginConfiguration) { + this.pluginConfiguration = pluginConfiguration; + } + + public String getPluginConfigurationParameter(@NonNull String parameterName) { + return pluginConfiguration.getParameter(parameterName); + } } diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java index 7297e26e..f758e045 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java @@ -22,7 +22,6 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.annotations; -import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.OptionalPluginConfigurationParameter; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.PluginConfigurationParameter; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.RequiredPluginConfigurationParameter; @@ -42,6 +41,4 @@ public interface ConfigurablePlugin { * @return a list of plugin configuration parameters */ List declarePluginConfigurationParameters(); - - void onConfigurationLoaded(PluginConfiguration pluginConfiguration); } diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java index 065af021..a863d853 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -1,6 +1,7 @@ package dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; +import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; import dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions.InvalidPluginConfigurationException; import java.util.*; @@ -10,10 +11,6 @@ @Flogger public class Rika2MqttPluginManager extends DefaultPluginManager { - // @Override - // protected PluginFactory createPluginFactory() { - // return new Rika2MqttPluginFactory(); - // } @Override public void startPlugins() { @@ -22,16 +19,17 @@ public void startPlugins() { PluginState pluginState = pluginWrapper.getPluginState(); if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { try { + + final var pluginConfiguration = + loadPluginConfiguration((Rika2MqttPlugin) pluginWrapper.getPlugin()); + // configurable plugins if (pluginWrapper.getPlugin() instanceof ConfigurablePlugin configurablePlugin) { final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); log.atInfo().log("Check plugin '%s' configuration", pluginName); - final var pluginConfiguration = loadPluginConfiguration(configurablePlugin); if (isPluginConfigurationValid(configurablePlugin, pluginConfiguration)) { log.atInfo().log("Start configurable plugin '%s'", pluginName); - // configurablePlugin.onConfigurationLoaded(pluginConfiguration); - ((ConfigurablePlugin) pluginWrapper.getPlugin()) - .onConfigurationLoaded(pluginConfiguration); + ((Rika2MqttPlugin) pluginWrapper.getPlugin()).preStart(pluginConfiguration); // todo: this line kills everything pluginWrapper.getPlugin().start(); pluginWrapper.setPluginState(PluginState.STARTED); @@ -50,6 +48,7 @@ public void startPlugins() { } } else { log.atInfo().log("Start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); + ((Rika2MqttPlugin) pluginWrapper.getPlugin()).preStart(pluginConfiguration); pluginWrapper.getPlugin().start(); pluginWrapper.setPluginState(PluginState.STARTED); pluginWrapper.setFailedException(null); @@ -94,23 +93,25 @@ private boolean isPluginConfigurationValid( } } - private PluginConfiguration loadPluginConfiguration(ConfigurablePlugin configurablePlugin) { + private PluginConfiguration loadPluginConfiguration(@NonNull Rika2MqttPlugin plugin) { Map configuration = new HashMap<>(); - for (var parameter : configurablePlugin.declarePluginConfigurationParameters()) { - - var value = - getEnvironmentVariable("PLUGIN_" + parameter.getParameterName()) - .orElseGet( - () -> { - if (parameter.isOptional() && parameter.getDefaultValue().isPresent()) { - return parameter.getDefaultValue().get().toString(); - } else { - return null; - } - }); - configuration.put(parameter.getParameterName(), value); + if (plugin instanceof ConfigurablePlugin configurablePlugin) { + for (var parameter : configurablePlugin.declarePluginConfigurationParameters()) { + + var value = + getEnvironmentVariable("PLUGIN_" + parameter.getParameterName()) + .orElseGet( + () -> { + if (parameter.isOptional() && parameter.getDefaultValue().isPresent()) { + return parameter.getDefaultValue().get().toString(); + } else { + return null; + } + }); + configuration.put(parameter.getParameterName(), value); + } } return PluginConfiguration.builder().parameters(configuration).build(); diff --git a/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java b/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java index cd3f5efc..d0cc290c 100644 --- a/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java +++ b/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java @@ -23,7 +23,6 @@ * THE SOFTWARE. */ -import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.OptionalPluginConfigurationParameter; @@ -89,9 +88,4 @@ public List declarePluginConfigurationParameters() .withValueType(String.class) .build()); } - - @Override - public void onConfigurationLoaded(PluginConfiguration pluginConfiguration) { - // config loaded - } } diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java index a5a40c86..ba4d0892 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java @@ -32,10 +32,6 @@ public class ExamplePlugin extends Rika2MqttPlugin { private ExampleHook hook; - // protected ExamplePlugin(PluginContext pluginContext) { - // super(pluginContext); - // } - @Override public void start() { log.atInfo().log("%s >> STARTED", PLUGIN_NAME); diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java index 0164c30b..a7b9d0a5 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java @@ -24,7 +24,6 @@ import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; -import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.OptionalPluginConfigurationParameter; @@ -42,9 +41,7 @@ public class Rika2MqttInfluxMetricsPlugin extends Rika2MqttPlugin implements Con private static final String INFLUXDB_PROTOCOL = "INFLUXDB_PROTOCOL"; private static final String INFLUXDB_AUTHENTICATION_TOKEN = "INFLUXDB_AUTHENTICATION_TOKEN"; - private PluginConfiguration pluginConfiguration; - - private StoveStatusHook hook; + // private StoveStatusHook hook; @Override public void start() { @@ -52,7 +49,7 @@ public void start() { Kamon.reconfigure(loadConfig()); Kamon.loadModules(); - hook = new StoveStatusHook(); // need a field otherwise it doesnt work (TODO: check why) + new StoveStatusHook(); } @Override @@ -64,19 +61,17 @@ public void stop() { Config loadConfig() { var defaultConfig = Kamon.config(); - // TODO: have to be customizable var props = new HashMap(); - props.put("kamon.influxdb.port", pluginConfiguration.getParameter(INFLUXDB_PORT)); - props.put("kamon.influxdb.hostname", pluginConfiguration.getParameter(INFLUXDB_HOSTNAME)); - props.put("kamon.influxdb.database", pluginConfiguration.getParameter(INFLUXDB_DATABASE)); - props.put("kamon.influxdb.protocol", pluginConfiguration.getParameter(INFLUXDB_PROTOCOL)); + props.put("kamon.influxdb.port", getPluginConfigurationParameter(INFLUXDB_PORT)); + props.put("kamon.influxdb.hostname", getPluginConfigurationParameter(INFLUXDB_HOSTNAME)); + props.put("kamon.influxdb.database", getPluginConfigurationParameter(INFLUXDB_DATABASE)); + props.put("kamon.influxdb.protocol", getPluginConfigurationParameter(INFLUXDB_PROTOCOL)); props.put( "kamon.influxdb.authentication.token", - pluginConfiguration.getParameter(INFLUXDB_AUTHENTICATION_TOKEN)); + getPluginConfigurationParameter(INFLUXDB_AUTHENTICATION_TOKEN)); var codeConfig = ConfigFactory.parseMap(props); - var newConfig = codeConfig.withFallback(defaultConfig); - return newConfig; + return codeConfig.withFallback(defaultConfig); } @Override @@ -114,19 +109,4 @@ public List declarePluginConfigurationParameters() .withValueType(String.class) .build()); } - - @Override - public void onConfigurationLoaded(PluginConfiguration pluginConfiguration) { - // log.atInfo().log(pluginConfiguration.toString()); - this.pluginConfiguration = pluginConfiguration; - System.out.println("aaaaaa"); - System.out.println("aaaaaa"); - System.out.println("aaaaaa"); - System.out.println("aaaaaa"); - System.out.println("aaaaaa"); - System.out.println("aaaaaa"); - System.out.println("aaaaaa"); - System.out.println("aaaaaa"); - System.out.println("aaaaaa"); - } } From 4cd999a968c47aa721b9d10f7acdd0f6c0c10c62 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 15:31:23 +0100 Subject: [PATCH 24/42] cleanup --- .../rika2mqtt/plugins/api/PluginContext.java | 22 +++++++++++++++++++ .../plugins/api/v1/PluginConfiguration.java | 22 +++++++++++++++++++ .../InvalidPluginConfigurationException.java | 22 +++++++++++++++++++ .../api/v1/exceptions/PluginException.java | 22 +++++++++++++++++++ .../v1/pf4j/Rika2MqttPluginFactory.java | 22 +++++++++++++++++++ .../v1/pf4j/Rika2MqttPluginManager.java | 22 +++++++++++++++++++ 6 files changed, 132 insertions(+) diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java index 55ea05e8..1b636d13 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api; import lombok.Getter; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java index 3f00bc57..136e217d 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1; import static java.util.Optional.ofNullable; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java index c3e76226..294fbfb0 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions; public class InvalidPluginConfigurationException extends PluginException { diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java index e465c054..11185518 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions; public class PluginException extends Exception { diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java index fe6da201..495b0079 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; import dev.cookiecode.rika2mqtt.plugins.api.PluginContext; diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java index a863d853..f482fbc0 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -1,3 +1,25 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; From 7092289bf32616fec67d4f42aa4f9b131bf39ec1 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 15:42:51 +0100 Subject: [PATCH 25/42] cleanup --- .github/workflows/build.yml | 1 + .../src/test/java/DummyTest.java | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a641395..6b9dae89 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,6 +59,7 @@ jobs: plugins-api/target plugins-internal/target rika2mqtt-example-plugin/target + rika2mqtt-example-plugin-using-config/target rika2mqtt-flux-metrics-plugin/target retention-days: 1 if-no-files-found: error diff --git a/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java b/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java new file mode 100644 index 00000000..783fb6a1 --- /dev/null +++ b/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java @@ -0,0 +1,36 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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. + */ + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class DummyTest { + + @Test + void dummyTest() { + assertThat(true) + .isTrue(); // TODO: add more complexity to the example plugin so that we can write real + // tests + } +} From 5a60ea38b20d6fbe452d986bb2ccbf518029dfdf Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 15:49:22 +0100 Subject: [PATCH 26/42] cleanup --- .../src/test/java/DummyTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java b/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java index 783fb6a1..839489b6 100644 --- a/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java +++ b/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java @@ -21,10 +21,10 @@ * THE SOFTWARE. */ -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + class DummyTest { @Test From dfe333c30cb35cdf592ed6d8f5b66f388f7dd879 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 16:06:24 +0100 Subject: [PATCH 27/42] cleanup --- .../plugins/api/v1/Rika2MqttPlugin.java | 7 ----- .../v1/pf4j/Rika2MqttPluginManager.java | 29 +++++++++---------- .../example/ExamplePluginUsingConfig.java | 4 --- .../metrics/Rika2MqttInfluxMetricsPlugin.java | 2 -- .../influxdb/metrics/StoveStatusHook.java | 12 ++++---- 5 files changed, 18 insertions(+), 36 deletions(-) diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java index 7bca196c..b2f7a706 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java @@ -30,13 +30,6 @@ @Beta public abstract class Rika2MqttPlugin extends Plugin { - // protected final PluginContext pluginContext; - // - // protected Rika2MqttPlugin(PluginContext pluginContext) { - // super(); - // this.pluginContext = pluginContext; - // } - private PluginConfiguration pluginConfiguration; public void preStart(@NonNull PluginConfiguration pluginConfiguration) { diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java index f482fbc0..8aebb454 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -36,7 +36,7 @@ public class Rika2MqttPluginManager extends DefaultPluginManager { @Override public void startPlugins() { - System.out.println("START PLUGINS"); + log.atInfo().log("Start plugins"); for (PluginWrapper pluginWrapper : resolvedPlugins) { PluginState pluginState = pluginWrapper.getPluginState(); if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { @@ -47,17 +47,10 @@ public void startPlugins() { // configurable plugins if (pluginWrapper.getPlugin() instanceof ConfigurablePlugin configurablePlugin) { - final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); - log.atInfo().log("Check plugin '%s' configuration", pluginName); if (isPluginConfigurationValid(configurablePlugin, pluginConfiguration)) { - log.atInfo().log("Start configurable plugin '%s'", pluginName); - ((Rika2MqttPlugin) pluginWrapper.getPlugin()).preStart(pluginConfiguration); - // todo: this line kills everything - pluginWrapper.getPlugin().start(); - pluginWrapper.setPluginState(PluginState.STARTED); - pluginWrapper.setFailedException(null); - startedPlugins.add(pluginWrapper); + startPlugin(pluginWrapper, pluginConfiguration); } else { + final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); log.atSevere().log( "Plugin '%s' configuration is invalid. Aborting load of the plugin", pluginName); stopPlugin(pluginWrapper.getPluginId()); @@ -69,12 +62,7 @@ public void startPlugins() { pluginName))); } } else { - log.atInfo().log("Start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); - ((Rika2MqttPlugin) pluginWrapper.getPlugin()).preStart(pluginConfiguration); - pluginWrapper.getPlugin().start(); - pluginWrapper.setPluginState(PluginState.STARTED); - pluginWrapper.setFailedException(null); - startedPlugins.add(pluginWrapper); + startPlugin(pluginWrapper, pluginConfiguration); } } catch (Exception | LinkageError e) { pluginWrapper.setPluginState(PluginState.FAILED); @@ -88,6 +76,15 @@ public void startPlugins() { } } + private void startPlugin(PluginWrapper pluginWrapper, PluginConfiguration pluginConfiguration){ + log.atInfo().log("Start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); + ((Rika2MqttPlugin) pluginWrapper.getPlugin()).preStart(pluginConfiguration); + pluginWrapper.getPlugin().start(); + pluginWrapper.setPluginState(PluginState.STARTED); + pluginWrapper.setFailedException(null); + startedPlugins.add(pluginWrapper); + } + private boolean isPluginConfigurationValid( @NonNull ConfigurablePlugin configurablePlugin, @NonNull PluginConfiguration pluginConfiguration) { diff --git a/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java b/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java index d0cc290c..c4c8b0d4 100644 --- a/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java +++ b/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java @@ -36,10 +36,6 @@ public class ExamplePluginUsingConfig extends Rika2MqttPlugin implements Configu static final String PLUGIN_NAME = "ExamplePlugin"; - // protected ExamplePluginUsingConfig(PluginContext pluginContext) { - // super(pluginContext); - // } - @Override public void start() { log.atInfo().log("%s >> STARTED", PLUGIN_NAME); diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java index a7b9d0a5..c7f2a365 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPlugin.java @@ -41,8 +41,6 @@ public class Rika2MqttInfluxMetricsPlugin extends Rika2MqttPlugin implements Con private static final String INFLUXDB_PROTOCOL = "INFLUXDB_PROTOCOL"; private static final String INFLUXDB_AUTHENTICATION_TOKEN = "INFLUXDB_AUTHENTICATION_TOKEN"; - // private StoveStatusHook hook; - @Override public void start() { Kamon.init(); diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index 34fc5b50..e441f4aa 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -152,13 +152,11 @@ private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { .getSensors() .getParametersDebug() .forEach( - parameterDebug -> { - Kamon.gauge("sensors.parameterDebug") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag - .update(parameterDebug.getValue()); - }); + parameterDebug -> Kamon.gauge("sensors.parameterDebug") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag + .update(parameterDebug.getValue())); } private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { From 7d4b07bf49b3b53496e5f94a620542fff5974da1 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 16:07:38 +0100 Subject: [PATCH 28/42] cleanup --- .../internal/v1/pf4j/Rika2MqttPluginManager.java | 2 +- .../plugins/influxdb/metrics/StoveStatusHook.java | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java index 8aebb454..5293d50e 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -76,7 +76,7 @@ public void startPlugins() { } } - private void startPlugin(PluginWrapper pluginWrapper, PluginConfiguration pluginConfiguration){ + private void startPlugin(PluginWrapper pluginWrapper, PluginConfiguration pluginConfiguration) { log.atInfo().log("Start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); ((Rika2MqttPlugin) pluginWrapper.getPlugin()).preStart(pluginConfiguration); pluginWrapper.getPlugin().start(); diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index e441f4aa..1c4cce53 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -152,11 +152,12 @@ private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { .getSensors() .getParametersDebug() .forEach( - parameterDebug -> Kamon.gauge("sensors.parameterDebug") - .withTag(STOVE_ID, stoveStatus.getStoveId()) - .withTag(STOVE_NAME, stoveStatus.getName()) - .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag - .update(parameterDebug.getValue())); + parameterDebug -> + Kamon.gauge("sensors.parameterDebug") + .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_NAME, stoveStatus.getName()) + .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag + .update(parameterDebug.getValue())); } private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { From c8499df48371cf5931be2fef24204d6eb15ae2bc Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 17:10:03 +0100 Subject: [PATCH 29/42] cleanup --- .code/docker-compose/docker-compose.yml | 1 + .docker/init.sh | 3 +- Dockerfile | 2 +- .../plugins/internal/v1/PluginDownloader.java | 144 ++++++++++++++++++ .../internal/v1/Rika2MqttPluginService.java | 4 + .../UnableToDownloadPluginException.java | 35 +++++ .../v1/Rika2MqttPluginServiceTest.java | 14 ++ 7 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/exceptions/UnableToDownloadPluginException.java diff --git a/.code/docker-compose/docker-compose.yml b/.code/docker-compose/docker-compose.yml index d549dbaa..b158e6d5 100644 --- a/.code/docker-compose/docker-compose.yml +++ b/.code/docker-compose/docker-compose.yml @@ -28,6 +28,7 @@ services: # - MQTT_PORT=1883 # - MQTT_COMMAND_TOPIC_NAME=cmnd/rika2mqtt # - MQTT_TELEMETRY_REPORT_TOPIC_NAME=tele/rika2mqtt +# - PLUGINS=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar influxdb: diff --git a/.docker/init.sh b/.docker/init.sh index b9471f54..8e68ce8e 100644 --- a/.docker/init.sh +++ b/.docker/init.sh @@ -23,9 +23,10 @@ # # Init script for rika2mqtt +export PLUGINS_DIR=/opt/rika2mqtt/plugins # Run it java --add-opens=java.base/java.net=ALL-UNNAMED \ --add-opens=java.base/java.io=ALL-UNNAMED \ --add-opens=java.base/java.nio=ALL-UNNAMED \ - -jar rika2mqtt.jar \ No newline at end of file + -jar rika2mqtt.jar diff --git a/Dockerfile b/Dockerfile index 89747cc3..3f6ac08a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ FROM eclipse-temurin:21.0.1_12-jre-jammy -RUN mkdir -p /opt/rika2mqtt +RUN mkdir -p /opt/rika2mqtt/plugins WORKDIR /opt/rika2mqtt COPY .docker/ . diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java new file mode 100644 index 00000000..7e82f0ba --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java @@ -0,0 +1,144 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1; + +import static java.net.HttpURLConnection.HTTP_OK; +import static java.util.stream.Collectors.toList; + +import dev.cookiecode.rika2mqtt.plugins.internal.v1.exceptions.UnableToDownloadPluginException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; +import lombok.NonNull; +import lombok.extern.flogger.Flogger; +import org.springframework.stereotype.Service; + +@Service +@Flogger +public class PluginDownloader { + + private static final String PLUGINS_ENV_VAR_NAME = "PLUGINS"; + private static final String PLUGINS_DIR_ENV_VAR_NAME = "PLUGINS_DIR"; + private static final String PLUGINS_SEPARATOR = ";"; + + /** + * sync plugins dir with PLUGINS environment variable. each plugin url has to be provided in + * PLUGINS=http://some.jar;http://another.jar + */ + public void synchronize() { + + // TODO: if md5 file is present (i.e maven central provide it then we should check integrity + // TODO: override files / delete before (should not delete files already present in plugins dir + // if they are not listed (useful for dev purpose) + var pluginsUrls = getDeclaredPlugins(); + var pluginsDir = getPluginsDir(); + for (var pluginUrl : pluginsUrls) { + try { + log.atInfo().log("Fetch plugin %s", pluginUrl); + downloadPlugin(pluginUrl, pluginsDir); + } catch (UnableToDownloadPluginException e) { + log.atSevere().withCause(e).log(e.getMessage()); + } + } + log.atInfo().log("Plugins synchronization: done."); + } + + private String getPluginsDir() { + return Optional.ofNullable(System.getenv(PLUGINS_DIR_ENV_VAR_NAME)).orElse("plugins"); + } + + private List getDeclaredPlugins() { + final var concatenatedString = + Optional.ofNullable(System.getenv(PLUGINS_ENV_VAR_NAME)).orElse(""); + return Arrays.stream(concatenatedString.split(PLUGINS_SEPARATOR)) + .map(String::trim) // Trim each URL string + .filter(urlStr -> !urlStr.isEmpty()) // Filter out empty or null strings + .map( + pluginUrlStr -> { + try { + return new URL(pluginUrlStr); + } catch (MalformedURLException e) { + log.atSevere().withCause(e).log("Ignore the following url: %s", pluginUrlStr); + return null; + } + }) + .filter(Objects::nonNull) // Remove the null URLs from the list + .collect(toList()); + } + + public void downloadPlugin(@NonNull URL jarUrl, @NonNull String pluginsDir) + throws UnableToDownloadPluginException { + try { + final var httpConn = (HttpURLConnection) jarUrl.openConnection(); + final var responseCode = httpConn.getResponseCode(); + + // Check for HTTP response code 200 (successful connection) + if (responseCode == HTTP_OK) { + // Get input stream from the connection + final var inputStream = httpConn.getInputStream(); + + // Create a FileOutputStream to write the downloaded file + var fileName = ""; + final var disposition = httpConn.getHeaderField("Content-Disposition"); + if (disposition != null) { + int index = disposition.indexOf("filename="); + if (index > 0) { + fileName = disposition.substring(index + 9); + } + } else { + fileName = jarUrl.toString().substring(jarUrl.toString().lastIndexOf("/") + 1); + } + final var saveFilePath = pluginsDir + File.separator + fileName; + + final var outputStream = new FileOutputStream(saveFilePath); + + // Read bytes from the input stream and write to the output stream + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + // Close streams + outputStream.close(); + inputStream.close(); + + log.atInfo().log("Plugin downloaded to: %s.", saveFilePath); + } else { + throw new UnableToDownloadPluginException( + String.format("No file to download. Server replied with HTTP code: %s", responseCode)); + } + httpConn.disconnect(); + } catch (MalformedURLException e) { + throw new UnableToDownloadPluginException( + String.format("Could not download the plugin, url is malformed: %s", jarUrl), e); + } catch (IOException e) { + throw new UnableToDownloadPluginException( + String.format("Could not download the plugin %s, io error.", jarUrl), e); + } + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java index b095c0ff..e2baf562 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java @@ -41,8 +41,12 @@ public class Rika2MqttPluginService { private final PluginManager pluginManager; + private final PluginDownloader pluginDownloader; public void start() { + log.atInfo().log("Fetch plugins ..."); + pluginDownloader.synchronize(); + log.atInfo().log("Plugin manager starting ..."); pluginManager.loadPlugins(); pluginManager.startPlugins(); diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/exceptions/UnableToDownloadPluginException.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/exceptions/UnableToDownloadPluginException.java new file mode 100644 index 00000000..2642f6da --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/exceptions/UnableToDownloadPluginException.java @@ -0,0 +1,35 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.exceptions; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions.PluginException; + +public class UnableToDownloadPluginException extends PluginException { + public UnableToDownloadPluginException(String message) { + super(message); + } + + public UnableToDownloadPluginException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java index 9bd7043a..2a02f171 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java @@ -42,6 +42,20 @@ class Rika2MqttPluginServiceTest { @InjectMocks private Rika2MqttPluginService rika2MqttPluginService; @Mock private PluginManager pluginManager; + @Mock private PluginDownloader pluginDownloader; + + @Test + void startShouldInvokeSynchronizePlugins() { + + // GIVEN + // nothing particular + + // WHEN + rika2MqttPluginService.start(); + + // THEN + verify(pluginDownloader, times(1)).synchronize(); + } @Test void startShouldInvokeLoadPlugins() { From fe8023704c11d853e0f9696d0279fff3ed9e8b49 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 18:21:55 +0100 Subject: [PATCH 30/42] cleanup --- .../plugins/internal/v1/PluginDownloader.java | 27 ++- .../internal/v1/PluginDownloaderTest.java | 179 ++++++++++++++++++ 2 files changed, 198 insertions(+), 8 deletions(-) create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java index 7e82f0ba..4e0e7d3b 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java @@ -25,6 +25,7 @@ import static java.net.HttpURLConnection.HTTP_OK; import static java.util.stream.Collectors.toList; +import com.google.common.annotations.VisibleForTesting; import dev.cookiecode.rika2mqtt.plugins.internal.v1.exceptions.UnableToDownloadPluginException; import java.io.File; import java.io.FileOutputStream; @@ -34,16 +35,22 @@ import java.net.URL; import java.util.*; import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.extern.flogger.Flogger; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @Service @Flogger +@RequiredArgsConstructor public class PluginDownloader { - private static final String PLUGINS_ENV_VAR_NAME = "PLUGINS"; - private static final String PLUGINS_DIR_ENV_VAR_NAME = "PLUGINS_DIR"; - private static final String PLUGINS_SEPARATOR = ";"; + static final String PLUGINS_ENV_VAR_NAME = "PLUGINS"; + static final String PLUGINS_DIR_ENV_VAR_NAME = "PLUGINS_DIR"; + static final String PLUGINS_SEPARATOR = ";"; + static final String DEFAULT_PLUGINS_DIR = "plugins"; + + private final Environment environment; /** * sync plugins dir with PLUGINS environment variable. each plugin url has to be provided in @@ -67,13 +74,16 @@ public void synchronize() { log.atInfo().log("Plugins synchronization: done."); } - private String getPluginsDir() { - return Optional.ofNullable(System.getenv(PLUGINS_DIR_ENV_VAR_NAME)).orElse("plugins"); + @VisibleForTesting + String getPluginsDir() { + return Optional.ofNullable(environment.getProperty(PLUGINS_DIR_ENV_VAR_NAME)) + .orElse(DEFAULT_PLUGINS_DIR); } - private List getDeclaredPlugins() { + @VisibleForTesting + List getDeclaredPlugins() { final var concatenatedString = - Optional.ofNullable(System.getenv(PLUGINS_ENV_VAR_NAME)).orElse(""); + Optional.ofNullable(environment.getProperty(PLUGINS_ENV_VAR_NAME)).orElse(""); return Arrays.stream(concatenatedString.split(PLUGINS_SEPARATOR)) .map(String::trim) // Trim each URL string .filter(urlStr -> !urlStr.isEmpty()) // Filter out empty or null strings @@ -90,7 +100,8 @@ private List getDeclaredPlugins() { .collect(toList()); } - public void downloadPlugin(@NonNull URL jarUrl, @NonNull String pluginsDir) + @VisibleForTesting + void downloadPlugin(@NonNull URL jarUrl, @NonNull String pluginsDir) throws UnableToDownloadPluginException { try { final var httpConn = (HttpURLConnection) jarUrl.openConnection(); diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java new file mode 100644 index 00000000..7b464607 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java @@ -0,0 +1,179 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1; + +import static dev.cookiecode.rika2mqtt.plugins.internal.v1.PluginDownloader.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +import java.net.URL; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.core.env.Environment; + +/** Test class */ +@ExtendWith(MockitoExtension.class) +class PluginDownloaderTest { + + @TempDir public Path pluginDir; + + @InjectMocks @Spy private PluginDownloader pluginDownloader; + + @Mock private Environment environment; + + @Test + void synchronize() {} + + @Test + void getDeclaredPluginsShouldReturnAnEmptyListGivenOnlyASpaceIsProvided() { + + // GIVEN + final var space = " "; + doReturn(space).when(environment).getProperty(PLUGINS_ENV_VAR_NAME); + + // WHEN + final var urls = pluginDownloader.getDeclaredPlugins(); + + // THEN + assertThat(urls).isEmpty(); + } + + @Test + void getDeclaredPluginsShouldReturnAnEmptyListGivenNothingIsProvided() { + + // GIVEN + final var empty = ""; + doReturn(empty).when(environment).getProperty(PLUGINS_ENV_VAR_NAME); + + // WHEN + final var urls = pluginDownloader.getDeclaredPlugins(); + + // THEN + assertThat(urls).isEmpty(); + } + + @Test + void getDeclaredPluginsShouldReturnAnEmptyListGivenEnvIsNotSet() { + + // GIVEN + doReturn(null).when(environment).getProperty(PLUGINS_ENV_VAR_NAME); + + // WHEN + final var urls = pluginDownloader.getDeclaredPlugins(); + + // THEN + assertThat(System.getenv(PLUGINS_ENV_VAR_NAME)).isNull(); + assertThat(urls).isEmpty(); + } + + @Test + void getDeclaredPluginsShouldReturnOnlyOneUrlGivenOneOfTheTwoIsMalformed() throws Exception { + + // GIVEN + final var malformedUrl = "http://plugin-a. jar"; + final var wellformedUrl = "http://plugin-b.jar"; + + final var pluginsUrls = String.format("%s;%s", malformedUrl, wellformedUrl); + doReturn(pluginsUrls).when(environment).getProperty(PLUGINS_ENV_VAR_NAME); + + // WHEN + final var urls = pluginDownloader.getDeclaredPlugins(); + + // THEN + assertThat(urls).isNotEmpty().hasSize(1).containsExactly(new URL(wellformedUrl)); + } + + @Test + void getDeclaredPluginsShouldReturnAUrlObjectWrapping2UrlsGivenTwoUrlsWereProvided() + throws Exception { + + // GIVEN + final var pluginAUrl = "http://plugin-a.jar"; + final var pluginBUrl = "http://plugin-b.jar"; + + final var pluginsUrls = String.format("%s%s%s", pluginAUrl, PLUGINS_SEPARATOR, pluginBUrl); + doReturn(pluginsUrls).when(environment).getProperty(PLUGINS_ENV_VAR_NAME); + + // WHEN + final var urls = pluginDownloader.getDeclaredPlugins(); + + // THEN + assertThat(urls) + .isNotEmpty() + .hasSize(2) + .containsExactly(new URL(pluginAUrl), new URL(pluginBUrl)); + } + + @Test + void getPluginsDirShouldReturnDefaultPluginsDirGivenEnvIsNotSet() { + // GIVEN + doReturn(null).when(environment).getProperty(PLUGINS_DIR_ENV_VAR_NAME); + + // WHEN + final var pluginsDir = pluginDownloader.getPluginsDir(); + + // THEN + assertThat(pluginsDir).isEqualTo(DEFAULT_PLUGINS_DIR); + } + + @Test + void getPluginsDirShouldReturnPluginsDirProvidedValueDirGivenEnvIsSet() { + // GIVEN + final var somedir = "somedir"; + doReturn(somedir).when(environment).getProperty(PLUGINS_DIR_ENV_VAR_NAME); + + // WHEN + final var pluginsDir = pluginDownloader.getPluginsDir(); + + // THEN + assertThat(pluginsDir).isEqualTo(somedir); + } + + @Test + void synchronizeShouldInvokeDownloadPlugin2TimesGivenThereAreTwoPluginsToDownload() + throws Exception { + // GIVEN + final var pluginAUrl = "http://plugin-a.jar"; + final var pluginBUrl = "http://plugin-b.jar"; + + final var pluginsUrls = String.format("%s%s%s", pluginAUrl, PLUGINS_SEPARATOR, pluginBUrl); + doReturn(pluginsUrls).when(environment).getProperty(PLUGINS_ENV_VAR_NAME); + + final var pluginDirPath = pluginDir.toAbsolutePath().toString(); + doReturn(pluginDirPath).when(environment).getProperty(PLUGINS_DIR_ENV_VAR_NAME); + + doNothing().when(pluginDownloader).downloadPlugin(any(), anyString()); + + // WHEN + pluginDownloader.synchronize(); + + // THEN + verify(pluginDownloader, times(2)).downloadPlugin(any(URL.class), anyString()); + } +} From e7262672874962b4f175ff705cb0df2d68942ee8 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 18:47:21 +0100 Subject: [PATCH 31/42] cleanup --- .../plugins/internal/v1/PluginDownloader.java | 6 +- .../v1/pf4j/Rika2MqttPluginManager.java | 72 ++++++++++--------- .../internal/v1/PluginDownloaderTest.java | 21 +++++- 3 files changed, 59 insertions(+), 40 deletions(-) diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java index 4e0e7d3b..254f514b 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java @@ -23,7 +23,7 @@ package dev.cookiecode.rika2mqtt.plugins.internal.v1; import static java.net.HttpURLConnection.HTTP_OK; -import static java.util.stream.Collectors.toList; +import static java.net.URI.create; import com.google.common.annotations.VisibleForTesting; import dev.cookiecode.rika2mqtt.plugins.internal.v1.exceptions.UnableToDownloadPluginException; @@ -90,14 +90,14 @@ List getDeclaredPlugins() { .map( pluginUrlStr -> { try { - return new URL(pluginUrlStr); + return create(pluginUrlStr).toURL(); } catch (MalformedURLException e) { log.atSevere().withCause(e).log("Ignore the following url: %s", pluginUrlStr); return null; } }) .filter(Objects::nonNull) // Remove the null URLs from the list - .collect(toList()); + .toList(); } @VisibleForTesting diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java index 5293d50e..0f437480 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -44,26 +44,22 @@ public void startPlugins() { final var pluginConfiguration = loadPluginConfiguration((Rika2MqttPlugin) pluginWrapper.getPlugin()); - - // configurable plugins - if (pluginWrapper.getPlugin() instanceof ConfigurablePlugin configurablePlugin) { - if (isPluginConfigurationValid(configurablePlugin, pluginConfiguration)) { - startPlugin(pluginWrapper, pluginConfiguration); - } else { - final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); - log.atSevere().log( - "Plugin '%s' configuration is invalid. Aborting load of the plugin", pluginName); - stopPlugin(pluginWrapper.getPluginId()); - pluginWrapper.setPluginState(PluginState.FAILED); - pluginWrapper.setFailedException( - new InvalidPluginConfigurationException( - String.format( - "Plugin '%s' configuration is invalid. Aborting load of the plugin", - pluginName))); - } - } else { + if (isPluginConfigurationValid( + (Rika2MqttPlugin) pluginWrapper.getPlugin(), pluginConfiguration)) { startPlugin(pluginWrapper, pluginConfiguration); + } else { + final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); + log.atSevere().log( + "Plugin '%s' configuration is invalid. Aborting load of the plugin", pluginName); + stopPlugin(pluginWrapper.getPluginId()); + pluginWrapper.setPluginState(PluginState.FAILED); + pluginWrapper.setFailedException( + new InvalidPluginConfigurationException( + String.format( + "Plugin '%s' configuration is invalid. Aborting load of the plugin", + pluginName))); } + } catch (Exception | LinkageError e) { pluginWrapper.setPluginState(PluginState.FAILED); pluginWrapper.setFailedException(e); @@ -86,29 +82,35 @@ private void startPlugin(PluginWrapper pluginWrapper, PluginConfiguration plugin } private boolean isPluginConfigurationValid( - @NonNull ConfigurablePlugin configurablePlugin, - @NonNull PluginConfiguration pluginConfiguration) { + @NonNull Rika2MqttPlugin plugin, @NonNull PluginConfiguration pluginConfiguration) { + + // configurable plugins + if (plugin instanceof ConfigurablePlugin configurablePlugin) { - final var parameters = configurablePlugin.declarePluginConfigurationParameters(); + final var parameters = configurablePlugin.declarePluginConfigurationParameters(); - final List errors = new ArrayList<>(); + final List errors = new ArrayList<>(); - for (final var param : parameters) { - // check required params are provided - if (param.isRequired() - && pluginConfiguration.getParameter(param.getParameterName()).isEmpty()) { - errors.add( - String.format( - "Parameter '%s' is required for this plugin to work properly. However unable to find any ENV named: 'PLUGIN_%s' declaring any value", - param.getParameterName(), param.getParameterName())); + for (final var param : parameters) { + // check required params are provided + if (param.isRequired() + && pluginConfiguration.getParameter(param.getParameterName()).isEmpty()) { + errors.add( + String.format( + "Parameter '%s' is required for this plugin to work properly. However unable to find any ENV named: 'PLUGIN_%s' declaring any value", + param.getParameterName(), param.getParameterName())); + } + } + + if (errors.isEmpty()) { + return true; + } else { + log.atSevere().log("%s", errors); + return false; } - } - if (errors.isEmpty()) { - return true; } else { - log.atSevere().log("%s", errors); - return false; + return true; } } diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java index 7b464607..04f96cb9 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java @@ -23,11 +23,14 @@ package dev.cookiecode.rika2mqtt.plugins.internal.v1; import static dev.cookiecode.rika2mqtt.plugins.internal.v1.PluginDownloader.*; +import static java.net.URI.create; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; @@ -106,7 +109,7 @@ void getDeclaredPluginsShouldReturnOnlyOneUrlGivenOneOfTheTwoIsMalformed() throw final var urls = pluginDownloader.getDeclaredPlugins(); // THEN - assertThat(urls).isNotEmpty().hasSize(1).containsExactly(new URL(wellformedUrl)); + assertThat(urls).isNotEmpty().hasSize(1).containsExactly(create(wellformedUrl).toURL()); } @Test @@ -127,7 +130,7 @@ void getDeclaredPluginsShouldReturnAUrlObjectWrapping2UrlsGivenTwoUrlsWereProvid assertThat(urls) .isNotEmpty() .hasSize(2) - .containsExactly(new URL(pluginAUrl), new URL(pluginBUrl)); + .containsExactly(create(pluginAUrl).toURL(), create(pluginBUrl).toURL()); } @Test @@ -176,4 +179,18 @@ void synchronizeShouldInvokeDownloadPlugin2TimesGivenThereAreTwoPluginsToDownloa // THEN verify(pluginDownloader, times(2)).downloadPlugin(any(URL.class), anyString()); } + + @Test + void downloadPluginShouldDownloadTheFileGivenItExists() throws Exception { + // GIVEN + final var pluginUrl = "https://repo.maven.apache.org/maven2/dev/cookiecode/maven-metadata.xml"; + + // WHEN + pluginDownloader.downloadPlugin( + create(pluginUrl).toURL(), pluginDir.toAbsolutePath().toString()); + + // THEN + final var expectedFile = Paths.get(pluginDir.toAbsolutePath().toString(), "maven-metadata.xml"); + assertThat(Files.exists(expectedFile)).as("File has been downloaded").isTrue(); + } } From 033e1552d3c718d1512d5339f5d5a2ec783532a1 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Thu, 21 Dec 2023 18:53:10 +0100 Subject: [PATCH 32/42] cleanup --- .../internal/v1/PluginDownloaderTest.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java index 04f96cb9..e833fbf4 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java @@ -50,9 +50,6 @@ class PluginDownloaderTest { @Mock private Environment environment; - @Test - void synchronize() {} - @Test void getDeclaredPluginsShouldReturnAnEmptyListGivenOnlyASpaceIsProvided() { @@ -95,23 +92,6 @@ void getDeclaredPluginsShouldReturnAnEmptyListGivenEnvIsNotSet() { assertThat(urls).isEmpty(); } - @Test - void getDeclaredPluginsShouldReturnOnlyOneUrlGivenOneOfTheTwoIsMalformed() throws Exception { - - // GIVEN - final var malformedUrl = "http://plugin-a. jar"; - final var wellformedUrl = "http://plugin-b.jar"; - - final var pluginsUrls = String.format("%s;%s", malformedUrl, wellformedUrl); - doReturn(pluginsUrls).when(environment).getProperty(PLUGINS_ENV_VAR_NAME); - - // WHEN - final var urls = pluginDownloader.getDeclaredPlugins(); - - // THEN - assertThat(urls).isNotEmpty().hasSize(1).containsExactly(create(wellformedUrl).toURL()); - } - @Test void getDeclaredPluginsShouldReturnAUrlObjectWrapping2UrlsGivenTwoUrlsWereProvided() throws Exception { From c02bada8aecb74c014499618ac1cb6c26879645f Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sat, 30 Dec 2023 12:01:00 +0100 Subject: [PATCH 33/42] cleanup --- .../plugins/api/v1/PluginConfiguration.java | 5 +- .../v1/pf4j/Rika2MqttPluginManager.java | 118 ++++--- .../v1/pf4j/DummyConfigurablePlugin.java | 52 +++ .../v1/pf4j/NotConfigurablePlugin.java | 30 ++ .../v1/pf4j/Rika2MqttPluginManagerTest.java | 308 ++++++++++++++++++ 5 files changed, 470 insertions(+), 43 deletions(-) create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/DummyConfigurablePlugin.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/NotConfigurablePlugin.java create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java index 136e217d..6bc7f38b 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java @@ -37,7 +37,10 @@ public class PluginConfiguration { private Map parameters; // paramName, value public String getParameter(@NonNull String parameter) { - return getOptionalParameter(parameter).orElseThrow(); + return getOptionalParameter(parameter) + .orElseThrow(); // TODO: if this happens it's at plugin side :/ the config should have been + // pre validated by rika2mqtt earlier should we keep this method or not ? + // provide more context to devs ? } public Optional getOptionalParameter(@NonNull String parameter) { diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java index 0f437480..94a42e4c 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -22,6 +22,10 @@ */ package dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; +import static org.pf4j.PluginState.DISABLED; +import static org.pf4j.PluginState.STARTED; + +import com.google.common.annotations.VisibleForTesting; import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; @@ -34,55 +38,83 @@ @Flogger public class Rika2MqttPluginManager extends DefaultPluginManager { + @VisibleForTesting + static final EnumSet PLUGIN_STATES_PREVENTING_START = EnumSet.of(DISABLED, STARTED); + + @VisibleForTesting static final String PLUGIN_ENV_NAME_PREFIX = "PLUGIN_"; + @Override public void startPlugins() { log.atInfo().log("Start plugins"); - for (PluginWrapper pluginWrapper : resolvedPlugins) { - PluginState pluginState = pluginWrapper.getPluginState(); - if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { - try { - - final var pluginConfiguration = - loadPluginConfiguration((Rika2MqttPlugin) pluginWrapper.getPlugin()); - if (isPluginConfigurationValid( - (Rika2MqttPlugin) pluginWrapper.getPlugin(), pluginConfiguration)) { - startPlugin(pluginWrapper, pluginConfiguration); - } else { - final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); - log.atSevere().log( - "Plugin '%s' configuration is invalid. Aborting load of the plugin", pluginName); - stopPlugin(pluginWrapper.getPluginId()); - pluginWrapper.setPluginState(PluginState.FAILED); - pluginWrapper.setFailedException( - new InvalidPluginConfigurationException( - String.format( - "Plugin '%s' configuration is invalid. Aborting load of the plugin", - pluginName))); - } - - } catch (Exception | LinkageError e) { - pluginWrapper.setPluginState(PluginState.FAILED); - pluginWrapper.setFailedException(e); - log.atSevere().withCause(e).log( - "Unable to start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); - } finally { - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } + getResolvedPlugins().stream() + .filter(pluginWrapper -> shouldStartPlugin(pluginWrapper.getPluginState())) + .forEach(this::handlePlugin); + } + + @VisibleForTesting + boolean shouldStartPlugin(@NonNull final PluginState pluginState) { + return !PLUGIN_STATES_PREVENTING_START.contains(pluginState); + } + + @VisibleForTesting + void handlePlugin(@NonNull final PluginWrapper pluginWrapper) { + try { + final var rika2MqttPlugin = (Rika2MqttPlugin) pluginWrapper.getPlugin(); + final var pluginConfiguration = loadPluginConfiguration(rika2MqttPlugin); + + if (isPluginConfigurationValid(rika2MqttPlugin, pluginConfiguration)) { + startPlugin(pluginWrapper, pluginConfiguration); + } else { + handleInvalidPluginConfiguration(pluginWrapper); } + } catch (Exception | LinkageError e) { + handlePluginStartFailure(pluginWrapper, e); + } finally { + firePluginStateEvent( + new PluginStateEvent(this, pluginWrapper, pluginWrapper.getPluginState())); } } - private void startPlugin(PluginWrapper pluginWrapper, PluginConfiguration pluginConfiguration) { + private void handleInvalidPluginConfiguration(@NonNull final PluginWrapper pluginWrapper) { + final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); + log.atSevere().log( + "Plugin '%s' configuration is invalid. Aborting load of the plugin", pluginName); + stopAndFailPlugin(pluginWrapper, pluginName); + } + + private void handlePluginStartFailure( + @NonNull final PluginWrapper pluginWrapper, @NonNull final Throwable exception) { + pluginWrapper.setPluginState(PluginState.FAILED); + pluginWrapper.setFailedException(exception); + log.atSevere().withCause(pluginWrapper.getFailedException()).log( + "Unable to start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); + } + + private void stopAndFailPlugin( + @NonNull final PluginWrapper pluginWrapper, @NonNull final String pluginName) { + stopPlugin(pluginWrapper.getPluginId()); + pluginWrapper.setPluginState(PluginState.FAILED); + pluginWrapper.setFailedException( + new InvalidPluginConfigurationException( + String.format( + "Plugin '%s' configuration is invalid. Aborting load of the plugin", pluginName))); + } + + @VisibleForTesting + void startPlugin( + @NonNull final PluginWrapper pluginWrapper, + @NonNull final PluginConfiguration pluginConfiguration) { log.atInfo().log("Start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); ((Rika2MqttPlugin) pluginWrapper.getPlugin()).preStart(pluginConfiguration); pluginWrapper.getPlugin().start(); - pluginWrapper.setPluginState(PluginState.STARTED); + pluginWrapper.setPluginState(STARTED); pluginWrapper.setFailedException(null); startedPlugins.add(pluginWrapper); } private boolean isPluginConfigurationValid( - @NonNull Rika2MqttPlugin plugin, @NonNull PluginConfiguration pluginConfiguration) { + @NonNull final Rika2MqttPlugin plugin, + @NonNull final PluginConfiguration pluginConfiguration) { // configurable plugins if (plugin instanceof ConfigurablePlugin configurablePlugin) { @@ -94,11 +126,11 @@ private boolean isPluginConfigurationValid( for (final var param : parameters) { // check required params are provided if (param.isRequired() - && pluginConfiguration.getParameter(param.getParameterName()).isEmpty()) { + && pluginConfiguration.getOptionalParameter(param.getParameterName()).isEmpty()) { errors.add( String.format( - "Parameter '%s' is required for this plugin to work properly. However unable to find any ENV named: 'PLUGIN_%s' declaring any value", - param.getParameterName(), param.getParameterName())); + "Parameter '%s' is required for this plugin to work properly. However unable to find any ENV named: '%s%s' declaring any value", + param.getParameterName(), PLUGIN_ENV_NAME_PREFIX, param.getParameterName())); } } @@ -114,15 +146,16 @@ private boolean isPluginConfigurationValid( } } - private PluginConfiguration loadPluginConfiguration(@NonNull Rika2MqttPlugin plugin) { + @VisibleForTesting + PluginConfiguration loadPluginConfiguration(@NonNull final Rika2MqttPlugin plugin) { - Map configuration = new HashMap<>(); + final Map configuration = new HashMap<>(); if (plugin instanceof ConfigurablePlugin configurablePlugin) { - for (var parameter : configurablePlugin.declarePluginConfigurationParameters()) { + for (final var parameter : configurablePlugin.declarePluginConfigurationParameters()) { var value = - getEnvironmentVariable("PLUGIN_" + parameter.getParameterName()) + getEnvironmentVariable(PLUGIN_ENV_NAME_PREFIX + parameter.getParameterName()) .orElseGet( () -> { if (parameter.isOptional() && parameter.getDefaultValue().isPresent()) { @@ -138,7 +171,8 @@ private PluginConfiguration loadPluginConfiguration(@NonNull Rika2MqttPlugin plu return PluginConfiguration.builder().parameters(configuration).build(); } - private Optional getEnvironmentVariable(@NonNull String environmentVariableName) { + @VisibleForTesting + Optional getEnvironmentVariable(@NonNull String environmentVariableName) { return Optional.ofNullable(System.getenv(environmentVariableName)); } } diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/DummyConfigurablePlugin.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/DummyConfigurablePlugin.java new file mode 100644 index 00000000..a6961820 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/DummyConfigurablePlugin.java @@ -0,0 +1,52 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.OptionalPluginConfigurationParameter; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.PluginConfigurationParameter; +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.RequiredPluginConfigurationParameter; +import java.util.List; + +/** + * Test class An example of plugin not implementing ConfigurablePlugin used for testing a behaviour + */ +public class DummyConfigurablePlugin extends Rika2MqttPlugin implements ConfigurablePlugin { + @Override + public List declarePluginConfigurationParameters() { + return List.of( + OptionalPluginConfigurationParameter.builder() + .withParameterName("language") + .withDescription("define the language to use") + .withValueType(String.class) + .withDefaultValue("en") + .withExample("en,fr,de") + .build(), + RequiredPluginConfigurationParameter.builder() + .withParameterName("password") + .withDescription("some password") + .withValueType(String.class) + .build()); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/NotConfigurablePlugin.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/NotConfigurablePlugin.java new file mode 100644 index 00000000..a4e1a973 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/NotConfigurablePlugin.java @@ -0,0 +1,30 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; + +/** + * Test class An example of plugin not implementing ConfigurablePlugin used for testing a behaviour + */ +public class NotConfigurablePlugin extends Rika2MqttPlugin {} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java new file mode 100644 index 00000000..608cf9f7 --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java @@ -0,0 +1,308 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; + +import static dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j.Rika2MqttPluginManager.PLUGIN_ENV_NAME_PREFIX; +import static dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j.Rika2MqttPluginManager.PLUGIN_STATES_PREVENTING_START; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; +import static org.pf4j.PluginState.*; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import lombok.NonNull; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.pf4j.PluginDescriptor; +import org.pf4j.PluginWrapper; + +/** + * Test class + * + * @author Sebastien Vermeille + */ +@ExtendWith(MockitoExtension.class) +class Rika2MqttPluginManagerTest { + + private final Map mockedAnswers = new HashMap<>(); + + @InjectMocks @Spy private Rika2MqttPluginManager rika2MqttPluginManager; + + @BeforeEach + void beforeEach() { + mockedAnswers.clear(); + } + + @Test + void startPluginsShouldInvokeHandlePluginsMethodOnlyToOnePluginGivenOnePluginIsDisabled() { + + // GIVEN + final var createdPlugin = mock(PluginWrapper.class); + when(createdPlugin.getPluginState()).thenReturn(CREATED); + final var disabledPlugin = mock(PluginWrapper.class); + when(disabledPlugin.getPluginState()).thenReturn(DISABLED); + + doReturn(List.of(createdPlugin, disabledPlugin)) + .when(rika2MqttPluginManager) + .getResolvedPlugins(); + doNothing().when(rika2MqttPluginManager).handlePlugin(createdPlugin); + + // WHEN + rika2MqttPluginManager.startPlugins(); + + // THEN + verify(rika2MqttPluginManager, times(1)).handlePlugin(createdPlugin); + verify(rika2MqttPluginManager, never()).handlePlugin(disabledPlugin); + } + + @Test + void shouldStartPluginShouldReturnFalseGivenPluginIsInDisabledState() { + + // GIVEN + final var state = DISABLED; + + // WHEN + final var shouldStart = rika2MqttPluginManager.shouldStartPlugin(state); + + // THEN + assertThat(shouldStart).isFalse(); + } + + @Test + void shouldStartPluginShouldReturnFalseGivenPluginIsInStartedState() { + + // GIVEN + final var state = STARTED; + + // WHEN + final var shouldStart = rika2MqttPluginManager.shouldStartPlugin(state); + + // THEN + assertThat(shouldStart).isFalse(); + } + + @Test + void shouldStartPluginShouldReturnTrueGivenPluginIsInStoppedState() { + + // GIVEN + final var state = STOPPED; + + // WHEN + final var shouldStart = rika2MqttPluginManager.shouldStartPlugin(state); + + // THEN + assertThat(shouldStart).isTrue(); + } + + @Test + void shouldStartPluginShouldReturnTrueGivenPluginIsInCreatedState() { + + // GIVEN + final var state = CREATED; + + // WHEN + final var shouldStart = rika2MqttPluginManager.shouldStartPlugin(state); + + // THEN + assertThat(shouldStart).isTrue(); + } + + @Test + void shouldStartPluginShouldReturnFalseForAllEnumeratedPluginStates() { + for (final var state : PLUGIN_STATES_PREVENTING_START) { + // GIVEN + // a state which is supposed to prevent the plugin to start + + // WHEN + final var shouldStart = rika2MqttPluginManager.shouldStartPlugin(state); + + // THEN + assertThat(shouldStart).isFalse(); + } + } + + @Test + void + loadPluginConfigurationShouldReturnAPluginConfigurationWithEmptyParametersGivenThePluginIsNotAConfigurablePlugin() { + // GIVEN + final var notConfigurablePlugin = new NotConfigurablePlugin(); + + // WHEN + final var pluginConfiguration = + rika2MqttPluginManager.loadPluginConfiguration(notConfigurablePlugin); + + // THEN + assertThat(pluginConfiguration.getParameters()).isEmpty(); + } + + @Test + void + loadPluginConfigurationShouldReturnAPluginConfigurationIncludingParametersGivenThePluginIsAConfigurablePlugin() { + // GIVEN + final var configurablePlugin = new DummyConfigurablePlugin(); + + // WHEN + final var pluginConfiguration = + rika2MqttPluginManager.loadPluginConfiguration(configurablePlugin); + + // THEN + assertThat(pluginConfiguration.getParameters()).isNotEmpty(); + } + + @Test + void + loadPluginConfigurationShouldReturnAPluginConfigurationIncludingParametersGivenThePluginDeclareAnOptionalParameterWithDefaultValueAndNoEnvIsGiven() { + // GIVEN + final var configurablePlugin = new DummyConfigurablePlugin(); + + // WHEN + final var pluginConfiguration = + rika2MqttPluginManager.loadPluginConfiguration(configurablePlugin); + + // THEN + assertThat(pluginConfiguration.getOptionalParameter("language")).isEqualTo(Optional.of("en")); + } + + @Test + void + loadPluginConfigurationShouldReturnAPluginConfigurationIncludingParametersGivenThePluginDeclareAnOptionalParameterWithDefaultValueAndEnvIsGiven() { + // GIVEN + final var configurablePlugin = new DummyConfigurablePlugin(); + final var language = "fr"; + mockPluginEnv("language", language); + + // WHEN + final var pluginConfiguration = + rika2MqttPluginManager.loadPluginConfiguration(configurablePlugin); + + // THEN + assertThat(pluginConfiguration.getOptionalParameter("language")) + .isEqualTo(Optional.of(language)); + } + + @Test + void + loadPluginConfigurationShouldReturnAPluginConfigurationIncludingParametersGivenThePluginDeclareARequiredParameterAndNoEnvIsProvided() { + // GIVEN + final var configurablePlugin = new DummyConfigurablePlugin(); + + // WHEN + final var pluginConfiguration = + rika2MqttPluginManager.loadPluginConfiguration(configurablePlugin); + + // THEN + assertThat(pluginConfiguration.getOptionalParameter("password")) + .as( + "The password parameter is not defined in ENV so it is set to null and then rika2mqtt will perform a validation of the plugin configuration") + .isEmpty(); + } + + @Test + void + loadPluginConfigurationShouldReturnAPluginConfigurationIncludingParametersGivenThePluginDeclareARequiredParameterAndEnvIsProvided() { + // GIVEN + final var configurablePlugin = new DummyConfigurablePlugin(); + final var password = "p4ssw0rd"; + mockPluginEnv("password", password); + + // WHEN + final var pluginConfiguration = + rika2MqttPluginManager.loadPluginConfiguration(configurablePlugin); + + // THEN + assertThat(pluginConfiguration.getParameter("password")).isEqualTo(password); + } + + @Test + void startPluginShouldInvokePluginPreStartMethod() { + + // GIVEN + final var pluginWrapper = mock(PluginWrapper.class); + when(pluginWrapper.getDescriptor()).thenReturn(mock(PluginDescriptor.class)); + final var rikaPlugin = spy(new DummyConfigurablePlugin()); + when(pluginWrapper.getPlugin()).thenReturn(rikaPlugin); + final var pluginConfiguration = mock(PluginConfiguration.class); + + // WHEN + rika2MqttPluginManager.startPlugin(pluginWrapper, pluginConfiguration); + + // THEN + verify(rikaPlugin, times(1)).preStart(pluginConfiguration); + } + + @Test + void startPluginShouldInvokePluginStartMethod() { + + // GIVEN + final var pluginWrapper = mock(PluginWrapper.class); + when(pluginWrapper.getDescriptor()).thenReturn(mock(PluginDescriptor.class)); + final var rikaPlugin = spy(new DummyConfigurablePlugin()); + when(pluginWrapper.getPlugin()).thenReturn(rikaPlugin); + final var pluginConfiguration = mock(PluginConfiguration.class); + + // WHEN + rika2MqttPluginManager.startPlugin(pluginWrapper, pluginConfiguration); + + // THEN + verify(rikaPlugin, times(1)).start(); + } + + @Test + void startPluginShouldUpdatePluginStateWhenInvoked() { + + // GIVEN + final var pluginWrapper = mock(PluginWrapper.class); + when(pluginWrapper.getDescriptor()).thenReturn(mock(PluginDescriptor.class)); + final var rikaPlugin = spy(new DummyConfigurablePlugin()); + when(pluginWrapper.getPlugin()).thenReturn(rikaPlugin); + final var pluginConfiguration = mock(PluginConfiguration.class); + + // WHEN + rika2MqttPluginManager.startPlugin(pluginWrapper, pluginConfiguration); + + // THEN + verify(pluginWrapper, times(1)).setPluginState(STARTED); + verify(pluginWrapper, times(1)).setFailedException(null); + } + + /** Helper method for testing plugin configurations */ + private void mockPluginEnv( + @NonNull final String pluginParameterName, @NonNull final String value) { + mockedAnswers.put(pluginParameterName, value); + doAnswer( + invocation -> { + final var arg = (String) invocation.getArgument(0); + final var keyName = arg.replaceFirst(PLUGIN_ENV_NAME_PREFIX, ""); + return Optional.ofNullable(mockedAnswers.get(keyName)); + }) + .when(rika2MqttPluginManager) + .getEnvironmentVariable(anyString()); + } +} From c37ea6cccb7c0c57dbc01566e4b92bb5d93c483c Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sat, 30 Dec 2023 13:20:54 +0100 Subject: [PATCH 34/42] cleanup --- .../rika2mqtt/plugins/api/v1/PluginConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java index 6bc7f38b..7d7ef831 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java @@ -39,8 +39,8 @@ public class PluginConfiguration { public String getParameter(@NonNull String parameter) { return getOptionalParameter(parameter) .orElseThrow(); // TODO: if this happens it's at plugin side :/ the config should have been - // pre validated by rika2mqtt earlier should we keep this method or not ? - // provide more context to devs ? + // pre validated by rika2mqtt earlier should we keep this method or not ? + // provide more context to devs ? } public Optional getOptionalParameter(@NonNull String parameter) { From 7205b53a729d06d04ee6bd62c52b6bc1bf386f6f Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sat, 30 Dec 2023 14:06:14 +0100 Subject: [PATCH 35/42] cleanup --- .../v1/pf4j/Rika2MqttPluginManager.java | 19 ++- .../v1/pf4j/Rika2MqttPluginManagerTest.java | 143 ++++++++++++++++++ 2 files changed, 154 insertions(+), 8 deletions(-) diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java index 94a42e4c..e22a6f86 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -22,8 +22,7 @@ */ package dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; -import static org.pf4j.PluginState.DISABLED; -import static org.pf4j.PluginState.STARTED; +import static org.pf4j.PluginState.*; import com.google.common.annotations.VisibleForTesting; import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; @@ -75,25 +74,28 @@ void handlePlugin(@NonNull final PluginWrapper pluginWrapper) { } } - private void handleInvalidPluginConfiguration(@NonNull final PluginWrapper pluginWrapper) { + @VisibleForTesting + void handleInvalidPluginConfiguration(@NonNull final PluginWrapper pluginWrapper) { final var pluginName = getPluginLabel(pluginWrapper.getDescriptor()); log.atSevere().log( "Plugin '%s' configuration is invalid. Aborting load of the plugin", pluginName); stopAndFailPlugin(pluginWrapper, pluginName); } - private void handlePluginStartFailure( + @VisibleForTesting + void handlePluginStartFailure( @NonNull final PluginWrapper pluginWrapper, @NonNull final Throwable exception) { - pluginWrapper.setPluginState(PluginState.FAILED); + pluginWrapper.setPluginState(FAILED); pluginWrapper.setFailedException(exception); log.atSevere().withCause(pluginWrapper.getFailedException()).log( "Unable to start plugin '%s'", getPluginLabel(pluginWrapper.getDescriptor())); } - private void stopAndFailPlugin( + @VisibleForTesting + void stopAndFailPlugin( @NonNull final PluginWrapper pluginWrapper, @NonNull final String pluginName) { stopPlugin(pluginWrapper.getPluginId()); - pluginWrapper.setPluginState(PluginState.FAILED); + pluginWrapper.setPluginState(FAILED); pluginWrapper.setFailedException( new InvalidPluginConfigurationException( String.format( @@ -112,7 +114,8 @@ void startPlugin( startedPlugins.add(pluginWrapper); } - private boolean isPluginConfigurationValid( + @VisibleForTesting + boolean isPluginConfigurationValid( @NonNull final Rika2MqttPlugin plugin, @NonNull final PluginConfiguration pluginConfiguration) { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java index 608cf9f7..c88a9cdf 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java @@ -29,6 +29,7 @@ import static org.pf4j.PluginState.*; import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; +import dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions.InvalidPluginConfigurationException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -292,6 +293,148 @@ void startPluginShouldUpdatePluginStateWhenInvoked() { verify(pluginWrapper, times(1)).setFailedException(null); } + @Test + void handleInvalidPluginConfigurationShouldInvokeStopAndFailPlugin() { + + // GIVEN + final var pluginWrapper = mock(PluginWrapper.class); + final var pluginDescriptor = mock(PluginDescriptor.class); + doReturn(pluginDescriptor).when(pluginWrapper).getDescriptor(); + doNothing().when(rika2MqttPluginManager).stopAndFailPlugin(any(), anyString()); + + // WHEN + rika2MqttPluginManager.handleInvalidPluginConfiguration(pluginWrapper); + + // THEN + verify(rika2MqttPluginManager, times(1)).stopAndFailPlugin(any(), anyString()); + } + + @Test + void handlePluginStartFailureShouldUpdatePluginState() { + + // GIVEN + final var pluginWrapper = mock(PluginWrapper.class); + final var pluginDescriptor = mock(PluginDescriptor.class); + doReturn(pluginDescriptor).when(pluginWrapper).getDescriptor(); + final var exception = mock(Exception.class); + + // WHEN + rika2MqttPluginManager.handlePluginStartFailure(pluginWrapper, exception); + + // THEN + verify(pluginWrapper, times(1)).setPluginState(FAILED); + verify(pluginWrapper, times(1)).setFailedException(exception); + } + + @Test + void stopAndFailPluginShouldUpdatePluginStateAccordingly() { + + // GIVEN + final var pluginWrapper = mock(PluginWrapper.class); + final var pluginId = "plugin-id"; + when(pluginWrapper.getPluginId()).thenReturn(pluginId); + doReturn(null).when(rika2MqttPluginManager).stopPlugin(anyString()); + final var pluginName = "plugin-a"; + + // WHEN + rika2MqttPluginManager.stopAndFailPlugin(pluginWrapper, pluginName); + + // THEN + verify(pluginWrapper, times(1)).setPluginState(FAILED); + verify(pluginWrapper, times(1)) + .setFailedException(any(InvalidPluginConfigurationException.class)); + } + + @Test + void handlePluginShouldInvokeStartPluginGivenTheConfigurationIsValid() { + + // GIVEN + final var pluginWrapper = mock(PluginWrapper.class); + final var rika2mqttPlugin = new DummyConfigurablePlugin(); + when(pluginWrapper.getPlugin()).thenReturn(rika2mqttPlugin); + final var pluginDescriptor = mock(PluginDescriptor.class); + doReturn(pluginDescriptor).when(pluginWrapper).getDescriptor(); + doReturn(true).when(rika2MqttPluginManager).isPluginConfigurationValid(any(), any()); + + // WHEN + rika2MqttPluginManager.handlePlugin(pluginWrapper); + + // THEN + verify(rika2MqttPluginManager, times(1)).startPlugin(any(), any()); + } + + @Test + void handlePluginShouldInvokeHandleInvalidPluginConfigurationGivenTheConfigurationIsInvalid() { + + // GIVEN + final var pluginWrapper = mock(PluginWrapper.class); + final var rika2mqttPlugin = new DummyConfigurablePlugin(); + when(pluginWrapper.getPlugin()).thenReturn(rika2mqttPlugin); + final var pluginDescriptor = mock(PluginDescriptor.class); + doReturn(pluginDescriptor).when(pluginWrapper).getDescriptor(); + doReturn(false).when(rika2MqttPluginManager).isPluginConfigurationValid(any(), any()); + + // WHEN + rika2MqttPluginManager.handlePlugin(pluginWrapper); + + // THEN + verify(rika2MqttPluginManager, times(1)).handleInvalidPluginConfiguration(any()); + } + + @Test + void + isPluginConfigurationValidShouldReturnTrueGivenThePluginIsNotImplementingConfigurablePlugin() { + + // GIVEN + final var plugin = new NotConfigurablePlugin(); + final var pluginConfiguration = mock(PluginConfiguration.class); + + // WHEN + final var valid = + rika2MqttPluginManager.isPluginConfigurationValid(plugin, pluginConfiguration); + + // THEN + assertThat(valid).isTrue(); + } + + @Test + void + isPluginConfigurationValidShouldReturnFalseGivenTheConfigurablePluginDeclaresARequiredParameterAndItIsNotProvided() { + + // GIVEN + // intentionally do not define a password ENV + + final var plugin = new DummyConfigurablePlugin(); + when(rika2MqttPluginManager.loadPluginConfiguration(plugin)).thenCallRealMethod(); + final var pluginConfiguration = rika2MqttPluginManager.loadPluginConfiguration(plugin); + + // WHEN + final var valid = + rika2MqttPluginManager.isPluginConfigurationValid(plugin, pluginConfiguration); + + // THEN + assertThat(valid).isFalse(); + } + + @Test + void + isPluginConfigurationValidShouldReturnTrueGivenTheConfigurablePluginDeclaresARequiredParameterAndIsProvided() { + + // GIVEN + mockPluginEnv("password", "p4ssw0rd"); + + final var plugin = new DummyConfigurablePlugin(); + when(rika2MqttPluginManager.loadPluginConfiguration(plugin)).thenCallRealMethod(); + final var pluginConfiguration = rika2MqttPluginManager.loadPluginConfiguration(plugin); + + // WHEN + final var valid = + rika2MqttPluginManager.isPluginConfigurationValid(plugin, pluginConfiguration); + + // THEN + assertThat(valid).isTrue(); + } + /** Helper method for testing plugin configurations */ private void mockPluginEnv( @NonNull final String pluginParameterName, @NonNull final String value) { From 5447dc4695f90b13c70d89269d529a515c68497e Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sat, 30 Dec 2023 14:12:07 +0100 Subject: [PATCH 36/42] cleanup --- .../v1/pf4j/Rika2MqttPluginFactory.java | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java deleted file mode 100644 index 495b0079..00000000 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginFactory.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * The MIT License - * Copyright © 2022 Sebastien Vermeille - * - * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; - -import dev.cookiecode.rika2mqtt.plugins.api.PluginContext; -import lombok.extern.flogger.Flogger; -import org.pf4j.DefaultPluginFactory; -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; - -@Flogger -public class Rika2MqttPluginFactory extends DefaultPluginFactory { - - @Override - protected Plugin createInstance(Class pluginClass, PluginWrapper pluginWrapper) { - - final var pluginContext = new PluginContext(); - try { - final var constructor = pluginClass.getConstructor(PluginContext.class); - return (Plugin) constructor.newInstance(pluginContext); - } catch (Exception e) { - log.atSevere().withCause(e).log(e.getMessage()); - return null; - } - } -} From 24b33174e0494530bc82b7c3a3c00d0e44221c00 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sat, 30 Dec 2023 14:22:18 +0100 Subject: [PATCH 37/42] cleanup --- DEV NOTES.md | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 DEV NOTES.md diff --git a/DEV NOTES.md b/DEV NOTES.md deleted file mode 100644 index 032e6a32..00000000 --- a/DEV NOTES.md +++ /dev/null @@ -1,18 +0,0 @@ -TODO: -* open a PR to PF4J there is an issue: - - - dev.cookiecode.rika2mqtt.plugins.influxdb.metrics.Rika2MqttInfluxMetricsPlugin - influx-metrics-plugin - 0.0.1 - 2.0.0 - Export RIKA stoves values to InfluxDB. - Sebastien Vermeille - MIT - # Why am I forced to define this... nothing should mean = empty by default it suppose it is x,y,z open a PR to fix it - - -If I do not declare any plugin dependencies entry, when the plugin is loaded it fails saying there is not plugin id. - -Declaring it empty might be a wish but then the error is wrong -IMHO should not be forced to declare dependencies if there are none. (See with authors) From 14bd6bde50365b13a9bf0a741677064984e07c8e Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sun, 31 Dec 2023 09:59:30 +0100 Subject: [PATCH 38/42] cleanup --- .../plugins/api/v1/Rika2MqttPlugin.java | 2 + .../InvalidPluginConfigurationException.java | 19 ++-- .../api/v1/exceptions/PluginException.java | 17 ++-- .../plugins/api/v1/model/StoveStatus.java | 6 +- .../plugins/api/v1/DummyPlugin.java} | 18 ++-- .../api/v1/PluginConfigurationTest.java | 88 +++++++++++++++++++ .../plugins/api/v1/Rika2MqttPluginTest.java | 70 +++++++++++++++ .../plugins/api/v1/model/StoveIdTest.java | 47 ++++++++++ .../UnableToDownloadPluginException.java | 17 ++-- .../internal/v1/mapper/StoveIdMapper.java | 41 +++++++++ .../internal/v1/mapper/StoveStatusMapper.java | 6 +- .../internal/v1/mapper/TimeRangeMapper.java | 4 +- .../v1/mapper/StoveStatusMapperTest.java | 3 +- ...NotAuthenticateToRikaFirenetException.java | 10 +-- .../exception/InvalidStoveIdException.java | 10 +-- .../exception/OutdatedRevisionException.java | 10 +-- .../UnableToControlRikaFirenetException.java | 14 +-- ...bleToRetrieveRikaFirenetDataException.java | 10 +-- .../influxdb/metrics/StoveStatusHook.java | 32 +++---- .../Rika2MqttInfluxMetricsPluginTest.java | 5 -- 20 files changed, 327 insertions(+), 102 deletions(-) rename plugins-api/src/{main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java => test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/DummyPlugin.java} (77%) create mode 100644 plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfigurationTest.java create mode 100644 plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPluginTest.java create mode 100644 plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveIdTest.java create mode 100644 plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapper.java diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java index b2f7a706..3f81225e 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java @@ -23,11 +23,13 @@ package dev.cookiecode.rika2mqtt.plugins.api.v1; import dev.cookiecode.rika2mqtt.plugins.api.Beta; +import lombok.Getter; import lombok.NonNull; import org.pf4j.Plugin; /** Base class for Rika2Mqtt plugins */ @Beta +@Getter public abstract class Rika2MqttPlugin extends Plugin { private PluginConfiguration pluginConfiguration; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java index 294fbfb0..67817874 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/InvalidPluginConfigurationException.java @@ -22,12 +22,15 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions; -public class InvalidPluginConfigurationException extends PluginException { - public InvalidPluginConfigurationException(String message) { - super(message); - } +import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; +import lombok.experimental.StandardException; - public InvalidPluginConfigurationException(String message, Throwable cause) { - super(message, cause); - } -} +/** + * Exception thrown by Rika2Mqtt when a plugin specify some configuration via {@link + * ConfigurablePlugin#declarePluginConfigurationParameters()} method and the provided configuration + * is not satisfying it. + * + * @author Sebastien Vermeille + */ +@StandardException +public class InvalidPluginConfigurationException extends PluginException {} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java index 11185518..64e54e07 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/exceptions/PluginException.java @@ -22,13 +22,12 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions; -public class PluginException extends Exception { +import lombok.experimental.StandardException; - public PluginException(String message) { - super(message); - } - - public PluginException(String message, Throwable cause) { - super(message, cause); - } -} +/** + * Base exception regarding to Rika2Mqtt plugins + * + * @author Sebastien Vermeille + */ +@StandardException +public class PluginException extends Exception {} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java index 98e7fe17..ff6a6c4b 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveStatus.java @@ -33,11 +33,7 @@ public class StoveStatus { private String name; - - // @SerializedName( - // value = "stoveId", - // alternate = {"stoveID"}) // for coherence (the rest of the api is using camelCase) - private Long stoveId; + private StoveId stoveId; private Long lastSeenMinutes; private Long lastConfirmedRevision; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/DummyPlugin.java similarity index 77% rename from plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java rename to plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/DummyPlugin.java index 1b636d13..a9490cef 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/PluginContext.java +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/DummyPlugin.java @@ -20,18 +20,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package dev.cookiecode.rika2mqtt.plugins.api; - -import lombok.Getter; -import lombok.NoArgsConstructor; +package dev.cookiecode.rika2mqtt.plugins.api.v1; /** - * An instance of this class is provided to plugins in their constructor. This class facilitates - * communication with Rika2Mqtt and plugin manager. + * Test class + * + * @author Sebastien Vermeille + *

This is a dummy implementation of abstract Rika2MqttPlugin which ease testing */ -@Getter -@NoArgsConstructor -public class PluginContext { - - private String name = "test"; -} +public class DummyPlugin extends Rika2MqttPlugin {} diff --git a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfigurationTest.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfigurationTest.java new file mode 100644 index 00000000..60a12d7e --- /dev/null +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfigurationTest.java @@ -0,0 +1,88 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +/** + * Test class + * + * @author Sebastien Vermeille + */ +@ExtendWith(MockitoExtension.class) +class PluginConfigurationTest { + + @InjectMocks @Spy private PluginConfiguration pluginConfiguration; + + @Test + void getParameterShouldInvokeGetOptionalParameterGivenThatThisIsMoreErrorSafe() { + // GIVEN + final var parameterName = "something"; + doReturn(Optional.of("some value")).when(pluginConfiguration).getOptionalParameter(anyString()); + + // WHEN + pluginConfiguration.getParameter(parameterName); + + // THEN + verify(pluginConfiguration, times(1)).getOptionalParameter(parameterName); + } + + @Test + void getOptionalParameterShouldReturnAnEmptyOptionalGivenTheRequestedParameterIsNotProvided() { + // GIVEN + final var parameterName = "somethingThatDoNotExists"; + final var emptyPluginConfiguration = + PluginConfiguration.builder().parameters(new HashMap<>()).build(); + + // WHEN + final var result = emptyPluginConfiguration.getOptionalParameter(parameterName); + + // THEN + assertThat(result).isEmpty(); + } + + @Test + void getOptionalParameterShouldReturnAValueGivenTheRequestedParameterIsProvidedWithinTheConfig() { + // GIVEN + final var parameterName = "username"; + final var parameterValue = "root"; + final var filledPluginConfiguration = + PluginConfiguration.builder().parameters(Map.of(parameterName, parameterValue)).build(); + + // WHEN + final var result = filledPluginConfiguration.getOptionalParameter(parameterName); + + // THEN + assertThat(result).isPresent().isEqualTo(Optional.of(parameterValue)); + } +} diff --git a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPluginTest.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPluginTest.java new file mode 100644 index 00000000..92d2d9ca --- /dev/null +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPluginTest.java @@ -0,0 +1,70 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +/** + * Test class + * + * @author Sebastien Vermeille + */ +@ExtendWith(MockitoExtension.class) +class Rika2MqttPluginTest { + + @Test + void preStartShouldInitPluginConfigurationAsItIsTheWholePurposeOfIt() { + // GIVEN + final var plugin = new DummyPlugin(); + final var pluginConfiguration = mock(PluginConfiguration.class); + + // WHEN + plugin.preStart(pluginConfiguration); + + // THEN + assertThat(plugin.getPluginConfiguration()).isEqualTo(pluginConfiguration); + } + + @Test + void + getPluginConfigurationParameterShouldInvokePluginConfigurationGetParameterMethodGivenItsAConvenienceWrapper() { + // GIVEN + final var plugin = new DummyPlugin(); + final var pluginConfiguration = mock(PluginConfiguration.class); + + plugin.preStart(pluginConfiguration); + + final var parameterName = "something"; + + // WHEN + plugin.getPluginConfigurationParameter(parameterName); + + // THEN + verify(pluginConfiguration, times(1)).getParameter(parameterName); + } +} diff --git a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveIdTest.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveIdTest.java new file mode 100644 index 00000000..a4656d47 --- /dev/null +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/StoveIdTest.java @@ -0,0 +1,47 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** + * Test class + * + * @author Sebastien Vermeille + */ +class StoveIdTest { + + @Test + void ofShouldBuildAStoveIdAsExpected() { + // GIVEN + final var stoveIdValue = 12321321L; + + // WHEN + final var stoveId = StoveId.of(stoveIdValue); + + // THEN + assertThat(stoveId.id()).isEqualTo(stoveIdValue); + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/exceptions/UnableToDownloadPluginException.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/exceptions/UnableToDownloadPluginException.java index 2642f6da..c5b0fd21 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/exceptions/UnableToDownloadPluginException.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/exceptions/UnableToDownloadPluginException.java @@ -23,13 +23,12 @@ package dev.cookiecode.rika2mqtt.plugins.internal.v1.exceptions; import dev.cookiecode.rika2mqtt.plugins.api.v1.exceptions.PluginException; +import lombok.experimental.StandardException; -public class UnableToDownloadPluginException extends PluginException { - public UnableToDownloadPluginException(String message) { - super(message); - } - - public UnableToDownloadPluginException(String message, Throwable cause) { - super(message, cause); - } -} +/** + * Exception thrown by Rika2Mqtt when a plugin can not be downloaded for some reasons. + * + * @author Sebastien Vermeille + */ +@StandardException +public class UnableToDownloadPluginException extends PluginException {} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapper.java new file mode 100644 index 00000000..204302b9 --- /dev/null +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapper.java @@ -0,0 +1,41 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import static org.mapstruct.ReportingPolicy.ERROR; + +import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveId; +import lombok.NonNull; +import org.mapstruct.Mapper; + +/** + * Mapper for StoveId + * + * @author Sebastien Vermeille + */ +@Mapper(unmappedTargetPolicy = ERROR) // ignore as we are using a map +public interface StoveIdMapper { + default StoveId map(@NonNull Long stoveId) { + return StoveId.of(stoveId); + } +} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java index 27b24347..f1a6558b 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java @@ -29,7 +29,11 @@ @Mapper( unmappedTargetPolicy = ReportingPolicy.IGNORE, - uses = {SensorsMapper.class, ControlsMapper.class}) // ignore as we are using a map + uses = { + SensorsMapper.class, + ControlsMapper.class, + StoveIdMapper.class + }) // ignore as we are using a map public interface StoveStatusMapper { StoveStatus toApiStoveStatus( diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java index 9ca4b054..244e9bba 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java @@ -23,14 +23,14 @@ package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static java.lang.Integer.parseInt; -import static org.mapstruct.ReportingPolicy.IGNORE; +import static org.mapstruct.ReportingPolicy.ERROR; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeDefinition; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeRange; import lombok.NonNull; import org.mapstruct.Mapper; -@Mapper(unmappedTargetPolicy = IGNORE) // ignore as we are using a map +@Mapper(unmappedTargetPolicy = ERROR) public interface TimeRangeMapper { /** * @param rikaTimeRange i.e: `13302200` diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java index e20d8c27..e82ebfff 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java @@ -38,7 +38,8 @@ classes = { StoveStatusMapperImpl.class, SensorsMapperEmptyImpl.class, - ControlsMapperEmptyImpl.class + ControlsMapperEmptyImpl.class, + StoveIdMapperImpl.class }) class StoveStatusMapperTest { diff --git a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/CouldNotAuthenticateToRikaFirenetException.java b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/CouldNotAuthenticateToRikaFirenetException.java index ad6b204f..da2c7a48 100644 --- a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/CouldNotAuthenticateToRikaFirenetException.java +++ b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/CouldNotAuthenticateToRikaFirenetException.java @@ -22,12 +22,10 @@ */ package dev.cookiecode.rika2mqtt.rika.firenet.exception; +import lombok.experimental.StandardException; + /** * @author Sebastien Vermeille */ -public class CouldNotAuthenticateToRikaFirenetException extends RikaFirenetException { - - public CouldNotAuthenticateToRikaFirenetException(final String message) { - super(message); - } -} +@StandardException +public class CouldNotAuthenticateToRikaFirenetException extends RikaFirenetException {} diff --git a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/InvalidStoveIdException.java b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/InvalidStoveIdException.java index 65063570..778b4fdf 100644 --- a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/InvalidStoveIdException.java +++ b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/InvalidStoveIdException.java @@ -22,12 +22,10 @@ */ package dev.cookiecode.rika2mqtt.rika.firenet.exception; +import lombok.experimental.StandardException; + /** * @author Sebastien Vermeille */ -public class InvalidStoveIdException extends RikaFirenetException { - - public InvalidStoveIdException(final String message) { - super(message); - } -} +@StandardException +public class InvalidStoveIdException extends RikaFirenetException {} diff --git a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/OutdatedRevisionException.java b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/OutdatedRevisionException.java index 1653973b..169af9f3 100644 --- a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/OutdatedRevisionException.java +++ b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/OutdatedRevisionException.java @@ -22,12 +22,10 @@ */ package dev.cookiecode.rika2mqtt.rika.firenet.exception; +import lombok.experimental.StandardException; + /** * @author Sebastien Vermeille */ -public class OutdatedRevisionException extends RikaFirenetException { - - public OutdatedRevisionException(final String message) { - super(message); - } -} +@StandardException +public class OutdatedRevisionException extends RikaFirenetException {} diff --git a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/UnableToControlRikaFirenetException.java b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/UnableToControlRikaFirenetException.java index 7bd18226..daedfc54 100644 --- a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/UnableToControlRikaFirenetException.java +++ b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/UnableToControlRikaFirenetException.java @@ -22,16 +22,10 @@ */ package dev.cookiecode.rika2mqtt.rika.firenet.exception; +import lombok.experimental.StandardException; + /** * @author Sebastien Vermeille */ -public class UnableToControlRikaFirenetException extends RikaFirenetException { - - public UnableToControlRikaFirenetException(final String message) { - super(message); - } - - public UnableToControlRikaFirenetException(final String message, final Throwable cause) { - super(message, cause); - } -} +@StandardException +public class UnableToControlRikaFirenetException extends RikaFirenetException {} diff --git a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/UnableToRetrieveRikaFirenetDataException.java b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/UnableToRetrieveRikaFirenetDataException.java index 3b1b5d48..3c26b3ca 100644 --- a/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/UnableToRetrieveRikaFirenetDataException.java +++ b/rika-firenet/src/main/java/dev/cookiecode/rika2mqtt/rika/firenet/exception/UnableToRetrieveRikaFirenetDataException.java @@ -22,12 +22,10 @@ */ package dev.cookiecode.rika2mqtt.rika.firenet.exception; +import lombok.experimental.StandardException; + /** * @author Sebastien Vermeille */ -public class UnableToRetrieveRikaFirenetDataException extends RikaFirenetException { - - public UnableToRetrieveRikaFirenetDataException(final String message, final Throwable cause) { - super(message, cause); - } -} +@StandardException +public class UnableToRetrieveRikaFirenetDataException extends RikaFirenetException {} diff --git a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java index 1c4cce53..cfd7a64c 100644 --- a/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java +++ b/rika2mqtt-flux-metrics-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/StoveStatusHook.java @@ -143,7 +143,7 @@ private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { .forEach( parameterErrorCount -> Kamon.gauge("sensors.parameterErrorCount") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(ERROR_NUMBER, parameterErrorCount.getNumber()) // extra tag .update(parameterErrorCount.getValue())); @@ -154,7 +154,7 @@ private void exportSensorsMetrics(@NonNull final StoveStatus stoveStatus) { .forEach( parameterDebug -> Kamon.gauge("sensors.parameterDebug") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(DEBUG_NUMBER, parameterDebug.getNumber()) // extra tag .update(parameterDebug.getValue())); @@ -182,19 +182,19 @@ private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { .forEach( convectionFan -> { Kamon.gauge("controls.convectionFan.area") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(FAN_ID, convectionFan.getIdentifier()) // extra tag .update(convectionFan.getArea()); Kamon.gauge("controls.convectionFan.level") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(FAN_ID, convectionFan.getIdentifier()) // extra tag .update(convectionFan.getLevel()); Kamon.gauge("controls.convectionFan.active") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(FAN_ID, convectionFan.getIdentifier()) // extra tag .update(convectionFan.isActive() ? 1 : 0); @@ -211,19 +211,19 @@ private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { final var timeRange = timeRanges.get(index); // from Kamon.gauge("controls.heatingTime.from.hours") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag .withTag(TIME_RANGE_INDEX, index) .update(timeRange.getFrom().getHours()); Kamon.gauge("controls.heatingTime.from.minutes") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag .withTag(TIME_RANGE_INDEX, index) .update(timeRange.getFrom().getMinutes()); Kamon.gauge("controls.heatingTime.from.decimal") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag .withTag(TIME_RANGE_INDEX, index) @@ -231,20 +231,20 @@ private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { // to Kamon.gauge("controls.heatingTime.to.hours") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag .withTag(TIME_RANGE_INDEX, index) .update(timeRange.getTo().getHours()); Kamon.gauge("controls.heatingTime.to.minutes") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag .withTag(TIME_RANGE_INDEX, index) .update(timeRange.getTo().getMinutes()); Kamon.gauge("controls.heatingTime.to.decimal") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(DAY_OF_WEEK, dayOfWeek.name()) // extra tag .withTag(TIME_RANGE_INDEX, index) @@ -257,7 +257,7 @@ private void exportControlsMetrics(@NonNull StoveStatus stoveStatus) { index -> { final var debugValue = controlsDebugs.get(index); Kamon.gauge("controls.debug") - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .withTag(DEBUG_NUMBER, index) // extra tag .update(debugValue); @@ -271,7 +271,7 @@ private void exportProperty( .ifPresentOrElse( value -> Kamon.gauge(propertyName) - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .update(value), () -> @@ -282,7 +282,7 @@ private void exportProperty( .ifPresentOrElse( value -> Kamon.gauge(propertyName) - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .update(value), () -> @@ -293,7 +293,7 @@ private void exportProperty( .ifPresentOrElse( value -> Kamon.gauge(propertyName) - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .update(value), () -> @@ -305,7 +305,7 @@ private void exportProperty( .map( value -> Kamon.gauge(propertyName) - .withTag(STOVE_ID, stoveStatus.getStoveId()) + .withTag(STOVE_ID, stoveStatus.getStoveId().id()) .withTag(STOVE_NAME, stoveStatus.getName()) .update(value)); } else { diff --git a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPluginTest.java b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPluginTest.java index 41abe4e6..ec60a102 100644 --- a/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPluginTest.java +++ b/rika2mqtt-flux-metrics-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/influxdb/metrics/Rika2MqttInfluxMetricsPluginTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.*; import com.typesafe.config.Config; -import dev.cookiecode.rika2mqtt.plugins.api.PluginContext; import kamon.Kamon; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,12 +38,8 @@ class Rika2MqttInfluxMetricsPluginTest { private Rika2MqttInfluxMetricsPlugin plugin; - private PluginContext pluginContext; - @BeforeEach public void beforeEach() { - // pluginContext = mock(PluginContext.class); - // plugin = spy(new Rika2MqttInfluxMetricsPlugin(pluginContext)); plugin = spy(new Rika2MqttInfluxMetricsPlugin()); } From d1282e74dc6650f1b73a38ef196a0dd2d9852aa2 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sun, 31 Dec 2023 10:28:14 +0100 Subject: [PATCH 39/42] cleanup --- .../api/v1/model/UpdatableControls.java | 78 ------------------- .../OptionalPluginConfigurationParameter.java | 5 ++ .../plugins/PluginConfigurationParameter.java | 5 ++ .../PluginConfigurationParameterBuilder.java | 5 ++ .../RequiredPluginConfigurationParameter.java | 5 ++ .../api/v1/PluginConfigurationTest.java | 37 +++++++++ 6 files changed, 57 insertions(+), 78 deletions(-) delete mode 100644 plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java deleted file mode 100644 index 2fda1841..00000000 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/UpdatableControls.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * The MIT License - * Copyright © 2022 Sebastien Vermeille - * - * 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 dev.cookiecode.rika2mqtt.plugins.api.v1.model; - -import static lombok.AccessLevel.NONE; - -import dev.cookiecode.rika2mqtt.plugins.api.Beta; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Beta -// TODO: check if this is the way we want (no usage yet) -public class UpdatableControls { - - private Long revision; - - /** Fields below (some, or all) can be sent using MQTT command to pilot RIKA. */ - private Integer operatingMode; - - private Integer heatingPower; - private Integer targetTemperature; - private Integer bakeTemperature; - private Boolean heatingTimesActiveForComfort; - private Integer setBackTemperature; - private Boolean convectionFan1Active; - private Integer convectionFan1Level; - private Integer convectionFan1Area; - private Boolean convectionFan2Active; - private Integer convectionFan2Level; - private Integer convectionFan2Area; - private Boolean frostProtectionActive; - private Integer frostProtectionTemperature; - private Boolean onOff; - - // @FieldNameConstants lombok annotation would generate this. Unfortunately at the moment it - // generates issues to generate - // javadoc: error: cannot find symbol - @NoArgsConstructor(access = NONE) - public static final class Fields { - public static final String REVISION = "revision"; - public static final String OPERATING_MODE = "operatingMode"; - public static final String HEATING_POWER = "heatingPower"; - public static final String TARGET_TEMPERATURE = "targetTemperature"; - public static final String BAKE_TEMPERATURE = "bakeTemperature"; - public static final String HEATING_TIMES_ACTIVE_FOR_COMFORT = "heatingTimesActiveForComfort"; - public static final String SET_BACK_TEMPERATURE = "setBackTemperature"; - public static final String CONVECTION_FAN1_ACTIVE = "convectionFan1Active"; - public static final String CONVECTION_FAN1_LEVEL = "convectionFan1Level"; - public static final String CONVECTION_FAN1_AREA = "convectionFan1Area"; - public static final String CONVECTION_FAN2_ACTIVE = "convectionFan2Active"; - public static final String CONVECTION_FAN2_LEVEL = "convectionFan2Level"; - public static final String CONVECTION_FAN2_AREA = "convectionFan2Area"; - public static final String FROST_PROTECTION_ACTIVE = "frostProtectionActive"; - public static final String FROST_PROTECTION_TEMPERATURE = "frostProtectionTemperature"; - public static final String ON_OFF = "onOff"; - } -} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java index 74ab7768..d2f3882d 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java @@ -23,8 +23,13 @@ package dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins; import java.util.Optional; +import javax.annotation.processing.Generated; import lombok.Getter; +@Generated( + value = "Step Builder generator Idea Plugin", + date = "2023-12-31T09:40:55+0100", + comments = "parsely adapted by hands to handle optional props") @Getter public class OptionalPluginConfigurationParameter { private final String parameterName; diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java index 67d6d370..2d6d5fb6 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java @@ -24,11 +24,16 @@ import dev.cookiecode.rika2mqtt.plugins.api.Beta; import java.util.Optional; +import javax.annotation.processing.Generated; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; import lombok.ToString; +@Generated( + value = "Step Builder generator Idea Plugin", + date = "2023-12-31T09:40:55+0100", + comments = "parsely adapted by hands to handle optional props") @Getter @ToString @EqualsAndHashCode diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java index 67c87530..a27d77de 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java @@ -23,10 +23,15 @@ package dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins; import dev.cookiecode.rika2mqtt.plugins.api.Beta; +import javax.annotation.processing.Generated; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; +@Generated( + value = "Step Builder generator Idea Plugin", + date = "2023-12-31T09:40:55+0100", + comments = "parsely adapted by hands to handle optional props") @Getter @ToString @EqualsAndHashCode diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java index 16b194f6..1308948a 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java @@ -23,8 +23,13 @@ package dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins; import java.util.Optional; +import javax.annotation.processing.Generated; import lombok.Getter; +@Generated( + value = "Step Builder generator Idea Plugin", + date = "2023-12-31T09:40:55+0100", + comments = "parsely adapted by hands to handle optional props") @Getter public class RequiredPluginConfigurationParameter { private final String parameterName; diff --git a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfigurationTest.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfigurationTest.java index 60a12d7e..f6f37cb9 100644 --- a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfigurationTest.java +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfigurationTest.java @@ -23,10 +23,12 @@ package dev.cookiecode.rika2mqtt.plugins.api.v1; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.*; import java.util.HashMap; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -57,6 +59,21 @@ void getParameterShouldInvokeGetOptionalParameterGivenThatThisIsMoreErrorSafe() verify(pluginConfiguration, times(1)).getOptionalParameter(parameterName); } + @Test + void getParameterShouldThrowAnExceptionGivenTheOptionalIsEmpty() { + // GIVEN + final var parameterName = "something"; + doReturn(Optional.empty()).when(pluginConfiguration).getOptionalParameter(anyString()); + + // THEN + assertThrows( + NoSuchElementException.class, + () -> { + // WHEN + pluginConfiguration.getParameter(parameterName); + }); + } + @Test void getOptionalParameterShouldReturnAnEmptyOptionalGivenTheRequestedParameterIsNotProvided() { // GIVEN @@ -85,4 +102,24 @@ void getOptionalParameterShouldReturnAValueGivenTheRequestedParameterIsProvidedW // THEN assertThat(result).isPresent().isEqualTo(Optional.of(parameterValue)); } + + @Test + void getOptionalParameterShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + pluginConfiguration.getOptionalParameter(null); + }); + } + + @Test + void getParameterShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + pluginConfiguration.getParameter(null); + }); + } } From abeec8beae2bcc8cd6a822db7dac9e8bef71c5bf Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sun, 31 Dec 2023 10:35:29 +0100 Subject: [PATCH 40/42] cleanup --- .../v1/mapper/ConvectionFanMapperTest.java | 11 ++++ .../internal/v1/mapper/StoveIdMapperTest.java | 63 +++++++++++++++++++ .../v1/mapper/TimeRangeMapperTest.java | 11 ++++ 3 files changed, 85 insertions(+) create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapperTest.java diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java index 44c3c436..137bbcaa 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java @@ -23,6 +23,7 @@ package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -34,6 +35,16 @@ class ConvectionFanMapperTest { @Autowired private ConvectionFanMapper mapper; + @Test + void mapShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + mapper.map(null, null, null, null); + }); + } + @Test void mapShouldConvertSuccessfullyGivenFanProperties() { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapperTest.java new file mode 100644 index 00000000..8e632eeb --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapperTest.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * Test class + * + * @author Sebastien Vermeille + */ +@SpringBootTest(classes = {StoveIdMapperImpl.class}) +class StoveIdMapperTest { + + @Autowired private StoveIdMapper mapper; + + @Test + void mapShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + mapper.map(null); + }); + } + + @Test + void mapShouldConvertSuccessfullyGivenAStoveIdLongValueIsProvided() { + // GIVEN + final var stoveIdLongValue = 12321312L; + + // WHEN + final var result = mapper.map(stoveIdLongValue); + + // THEN + assertThat(result.id()).isEqualTo(stoveIdLongValue); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java index 67309234..a37e72d4 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java @@ -23,6 +23,7 @@ package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -34,6 +35,16 @@ class TimeRangeMapperTest { @Autowired private TimeRangeMapper mapper; + @Test + void mapShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + mapper.map(null); + }); + } + @Test void mapShouldConvertSuccessfullyGivenAValidRikaTimeRangeInputGivenDoubleDigitTimes() { From 21e61b0fc95807e951b3ed562dbee31d868caa69 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sun, 31 Dec 2023 10:45:40 +0100 Subject: [PATCH 41/42] cleanup --- .../v1/pf4j/Pf4jPluginManagerConfigTest.java | 46 +++++++++++ .../v1/pf4j/Rika2MqttPluginManagerTest.java | 81 +++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfigTest.java diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfigTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfigTest.java new file mode 100644 index 00000000..ee49f6ae --- /dev/null +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfigTest.java @@ -0,0 +1,46 @@ +/* + * The MIT License + * Copyright © 2022 Sebastien Vermeille + * + * 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 dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.pf4j.PluginManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * Test class + * + * @author Sebastien Vermeille + */ +@SpringBootTest(classes = {Pf4jPluginManagerConfig.class}) +class Pf4jPluginManagerConfigTest { + + @Autowired private PluginManager pluginManager; + + @Test + void pluginManagerHasTheCorrectInstance() { + assertThat(pluginManager).isInstanceOf(Rika2MqttPluginManager.class); + } +} diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java index c88a9cdf..0ba186e5 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManagerTest.java @@ -25,6 +25,7 @@ import static dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j.Rika2MqttPluginManager.PLUGIN_ENV_NAME_PREFIX; import static dev.cookiecode.rika2mqtt.plugins.internal.v1.pf4j.Rika2MqttPluginManager.PLUGIN_STATES_PREVENTING_START; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.*; import static org.pf4j.PluginState.*; @@ -448,4 +449,84 @@ private void mockPluginEnv( .when(rika2MqttPluginManager) .getEnvironmentVariable(anyString()); } + + @Test + void shouldStartPluginShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + rika2MqttPluginManager.shouldStartPlugin(null); + }); + } + + @Test + void handlePluginShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + rika2MqttPluginManager.handlePlugin(null); + }); + } + + @Test + void handleInvalidPluginConfigurationShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + rika2MqttPluginManager.handleInvalidPluginConfiguration(null); + }); + } + + @Test + void handlePluginStartFailureShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + rika2MqttPluginManager.handlePluginStartFailure(null, null); + }); + } + + @Test + void stopAndFailPluginShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + rika2MqttPluginManager.stopAndFailPlugin(null, null); + }); + } + + @Test + void isPluginConfigurationValidShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + rika2MqttPluginManager.isPluginConfigurationValid(null, null); + }); + } + + @Test + void loadPluginConfigurationShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + rika2MqttPluginManager.loadPluginConfiguration(null); + }); + } + + @Test + void getEnvironmentVariableShouldThrowAnNullPointerExceptionGivenNoParameterIsPassed() { + assertThrows( + NullPointerException.class, + () -> { + // WHEN + rika2MqttPluginManager.getEnvironmentVariable(null); + }); + } } From adc66f465683c97f276dcf19907c2b1e80e06a39 Mon Sep 17 00:00:00 2001 From: Sebastien Vermeille Date: Sun, 31 Dec 2023 11:53:29 +0100 Subject: [PATCH 42/42] cleanup --- .../java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java | 2 ++ .../rika2mqtt/plugins/api/v1/PluginConfiguration.java | 3 +++ .../rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java | 6 +++++- .../rika2mqtt/plugins/api/v1/StoveStatusExtension.java | 3 +++ .../plugins/api/v1/annotations/ConfigurablePlugin.java | 7 +++++++ .../rika2mqtt/plugins/api/v1/annotations/Nullable.java | 6 +++++- .../rika2mqtt/plugins/api/v1/model/ConvectionFan.java | 3 +++ .../rika2mqtt/plugins/api/v1/model/ParameterDebug.java | 3 +++ .../plugins/api/v1/model/ParameterErrorCount.java | 3 +++ .../rika2mqtt/plugins/api/v1/model/TimeDefinition.java | 6 +++++- .../rika2mqtt/plugins/api/v1/model/TimeRange.java | 3 +++ .../plugins/OptionalPluginConfigurationParameter.java | 3 +++ .../v1/model/plugins/PluginConfigurationParameter.java | 3 +++ .../plugins/PluginConfigurationParameterBuilder.java | 3 +++ .../plugins/RequiredPluginConfigurationParameter.java | 3 +++ .../plugins/api/v1/model/TimeDefinitionTest.java | 6 +++++- .../rika2mqtt/plugins/api/v1/model/TimeRangeTest.java | 6 +++++- .../rika2mqtt/plugins/internal/v1/PluginDownloader.java | 7 +++++++ .../plugins/internal/v1/Rika2MqttPluginService.java | 2 +- .../plugins/internal/v1/event/PolledStoveStatusEvent.java | 5 +++++ .../plugins/internal/v1/event/Rika2MqttPluginEvent.java | 6 ++++++ .../plugins/internal/v1/mapper/ControlsMapper.java | 5 +++++ .../plugins/internal/v1/mapper/ConvectionFanMapper.java | 5 +++++ .../plugins/internal/v1/mapper/SensorsMapper.java | 5 +++++ .../plugins/internal/v1/mapper/StoveIdMapper.java | 2 ++ .../plugins/internal/v1/mapper/StoveStatusMapper.java | 5 +++++ .../plugins/internal/v1/mapper/TimeRangeMapper.java | 5 +++++ .../plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java | 3 +++ .../plugins/internal/v1/pf4j/Rika2MqttPluginManager.java | 5 +++++ .../plugins/internal/v1/PluginDownloaderTest.java | 6 +++++- .../plugins/internal/v1/Rika2MqttPluginServiceTest.java | 6 +++++- .../plugins/internal/v1/mapper/ControlsMapperTest.java | 6 +++++- .../internal/v1/mapper/ConvectionFanMapperTest.java | 6 +++++- .../plugins/internal/v1/mapper/SensorsMapperTest.java | 5 +++++ .../plugins/internal/v1/mapper/StoveStatusMapperTest.java | 6 +++++- .../plugins/internal/v1/mapper/TimeRangeMapperTest.java | 6 +++++- .../v1/mapper/helper/ControlsMapperEmptyImpl.java | 6 ++++-- .../internal/v1/mapper/helper/SensorsMapperEmptyImpl.java | 6 ++++-- .../plugins/internal/v1/pf4j/DummyConfigurablePlugin.java | 2 ++ .../plugins/internal/v1/pf4j/NotConfigurablePlugin.java | 2 ++ .../plugins/example/ExamplePluginUsingConfig.java | 8 ++++++++ .../src/test/java/DummyTest.java | 5 +++++ .../cookiecode/rika2mqtt/plugins/example/ExampleHook.java | 5 +++++ .../rika2mqtt/plugins/example/ExamplePlugin.java | 5 +++++ .../rika2mqtt/plugins/example/ExampleHookTest.java | 5 +++++ 45 files changed, 193 insertions(+), 16 deletions(-) diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java index 3b732a32..3ed540b3 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/Beta.java @@ -25,5 +25,7 @@ /** * Document beta features that might be removed/updated with breaking changes use it carefully being * aware of this + * + * @author Sebastien Vermeille */ public @interface Beta {} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java index 7d7ef831..fd706afc 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/PluginConfiguration.java @@ -28,6 +28,9 @@ import java.util.Optional; import lombok.*; +/** + * @author Sebastien Vermeille + */ @Builder @Getter @ToString diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java index 3f81225e..23502c38 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/Rika2MqttPlugin.java @@ -27,7 +27,11 @@ import lombok.NonNull; import org.pf4j.Plugin; -/** Base class for Rika2Mqtt plugins */ +/** + * Base class for Rika2Mqtt plugins + * + * @author Sebastien Vermeille + */ @Beta @Getter public abstract class Rika2MqttPlugin extends Plugin { diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java index 06e1b882..5d389f2a 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/StoveStatusExtension.java @@ -26,6 +26,9 @@ import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; import org.pf4j.ExtensionPoint; +/** + * @author Sebastien Vermeille + */ @Beta public interface StoveStatusExtension extends ExtensionPoint { diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java index f758e045..7b0c3860 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/ConfigurablePlugin.java @@ -27,6 +27,13 @@ import dev.cookiecode.rika2mqtt.plugins.api.v1.model.plugins.RequiredPluginConfigurationParameter; import java.util.List; +/** + * Plugins that require external configuration should implement this interface. This way, they + * benefit from Rika2Mqtt configuration check at startup time. The bridge will verify that the + * required parameters are provided and then only start the plugin. + * + * @author Sebastien Vermeille + */ public interface ConfigurablePlugin { /** diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/Nullable.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/Nullable.java index d2f811b4..f2481c60 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/Nullable.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/annotations/Nullable.java @@ -22,5 +22,9 @@ */ package dev.cookiecode.rika2mqtt.plugins.api.v1.annotations; -/** Explicitly document that a parameter can be null */ +/** + * Explicitly document that a parameter can be null + * + * @author Sebastien Vermeille + */ public @interface Nullable {} diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java index d4e087e4..197e9db1 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ConvectionFan.java @@ -25,6 +25,9 @@ import dev.cookiecode.rika2mqtt.plugins.api.Beta; import lombok.*; +/** + * @author Sebastien Vermeille + */ @Data @Builder @Beta diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java index c6efe851..05cc7017 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterDebug.java @@ -26,6 +26,9 @@ import lombok.Builder; import lombok.Data; +/** + * @author Sebastien Vermeille + */ @Data @Builder @Beta diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java index a42f7e37..4d8d812d 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/ParameterErrorCount.java @@ -26,6 +26,9 @@ import lombok.Builder; import lombok.Data; +/** + * @author Sebastien Vermeille + */ @Data @Builder @Beta diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java index bc677d5e..e3b6e575 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinition.java @@ -27,7 +27,11 @@ import lombok.EqualsAndHashCode; import lombok.Getter; -/** Class to represent time schedules such as : 10:00 - 12:30 */ +/** + * Class to represent time schedules such as : 10:00 - 12:30 + * + * @author Sebastien Vermeille + */ @Builder @Getter @EqualsAndHashCode diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java index 04cfb889..eb5f46b8 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRange.java @@ -27,6 +27,9 @@ import lombok.EqualsAndHashCode; import lombok.Getter; +/** + * @author Sebastien Vermeille + */ @Builder @Getter @EqualsAndHashCode diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java index d2f3882d..90cd063d 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/OptionalPluginConfigurationParameter.java @@ -26,6 +26,9 @@ import javax.annotation.processing.Generated; import lombok.Getter; +/** + * @author Sebastien Vermeille + */ @Generated( value = "Step Builder generator Idea Plugin", date = "2023-12-31T09:40:55+0100", diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java index 2d6d5fb6..8a4d3b04 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameter.java @@ -30,6 +30,9 @@ import lombok.NonNull; import lombok.ToString; +/** + * @author Sebastien Vermeille + */ @Generated( value = "Step Builder generator Idea Plugin", date = "2023-12-31T09:40:55+0100", diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java index a27d77de..fed5b5b6 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/PluginConfigurationParameterBuilder.java @@ -28,6 +28,9 @@ import lombok.Getter; import lombok.ToString; +/** + * @author Sebastien Vermeille + */ @Generated( value = "Step Builder generator Idea Plugin", date = "2023-12-31T09:40:55+0100", diff --git a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java index 1308948a..7af8a4b7 100644 --- a/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java +++ b/plugins-api/src/main/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/plugins/RequiredPluginConfigurationParameter.java @@ -26,6 +26,9 @@ import javax.annotation.processing.Generated; import lombok.Getter; +/** + * @author Sebastien Vermeille + */ @Generated( value = "Step Builder generator Idea Plugin", date = "2023-12-31T09:40:55+0100", diff --git a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinitionTest.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinitionTest.java index 237c7a15..0a0b43e2 100644 --- a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinitionTest.java +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeDefinitionTest.java @@ -26,7 +26,11 @@ import org.junit.jupiter.api.Test; -/** Test class */ +/** + * Test class + * + * @author Sebastien Vermeille + */ class TimeDefinitionTest { @Test diff --git a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRangeTest.java b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRangeTest.java index 0831fccf..5546e0ab 100644 --- a/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRangeTest.java +++ b/plugins-api/src/test/java/dev/cookiecode/rika2mqtt/plugins/api/v1/model/TimeRangeTest.java @@ -26,7 +26,11 @@ import org.junit.jupiter.api.Test; -/** Test class */ +/** + * Test class + * + * @author Sebastien Vermeille + */ class TimeRangeTest { @Test diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java index 254f514b..18dc6067 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloader.java @@ -26,6 +26,7 @@ import static java.net.URI.create; import com.google.common.annotations.VisibleForTesting; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.internal.v1.exceptions.UnableToDownloadPluginException; import java.io.File; import java.io.FileOutputStream; @@ -40,6 +41,12 @@ import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; +/** + * Service class responsible to download rika2mqtt plugins and synchronize them at startup. + * + * @author Sebastien Vermeille + */ +@Beta @Service @Flogger @RequiredArgsConstructor diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java index e2baf562..ee1ac83d 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginService.java @@ -31,7 +31,7 @@ import org.springframework.stereotype.Service; /** - * Manage plugin loading, start etc. + * Service responsible to orchestrate the whole plugins lifecycle: loading, start etc. * * @author Sebastien Vermeille */ diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java index eeb59010..97784df3 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/PolledStoveStatusEvent.java @@ -22,14 +22,19 @@ */ package dev.cookiecode.rika2mqtt.plugins.internal.v1.event; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; import lombok.*; +/** + * @author Sebastien Vermeille + */ @RequiredArgsConstructor @Getter @ToString @EqualsAndHashCode @Builder +@Beta public class PolledStoveStatusEvent implements Rika2MqttPluginEvent { private final StoveStatus stoveStatus; } diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java index ab415429..267b23bf 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/event/Rika2MqttPluginEvent.java @@ -22,4 +22,10 @@ */ package dev.cookiecode.rika2mqtt.plugins.internal.v1.event; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; + +/** + * @author Sebastien Vermeille + */ +@Beta public interface Rika2MqttPluginEvent {} diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java index 48d8d2a1..8c2289cf 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapper.java @@ -27,6 +27,7 @@ import static org.mapstruct.ReportingPolicy.IGNORE; import com.google.common.collect.ImmutableMap; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Controls; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeRange; import java.time.DayOfWeek; @@ -37,6 +38,10 @@ import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; +/** + * @author Sebastien Vermeille + */ +@Beta @Mapper( unmappedTargetPolicy = IGNORE, uses = {TimeRangeMapper.class, ConvectionFanMapper.class}) // ignore as we are using a map diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java index f9b7f882..a9c8f98c 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapper.java @@ -24,10 +24,15 @@ import static org.mapstruct.ReportingPolicy.IGNORE; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.ConvectionFan; import lombok.NonNull; import org.mapstruct.Mapper; +/** + * @author Sebastien Vermeille + */ +@Beta @Mapper(unmappedTargetPolicy = IGNORE) // ignore as we are using a map public interface ConvectionFanMapper { diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java index 881ed024..8c6371f6 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapper.java @@ -24,6 +24,7 @@ import static org.mapstruct.ReportingPolicy.IGNORE; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.ParameterDebug; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.ParameterErrorCount; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.Sensors; @@ -34,6 +35,10 @@ import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; +/** + * @author Sebastien Vermeille + */ +@Beta @Mapper(unmappedTargetPolicy = IGNORE) // ignore as we are using a map public interface SensorsMapper { diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapper.java index 204302b9..d0a549c2 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveIdMapper.java @@ -24,6 +24,7 @@ import static org.mapstruct.ReportingPolicy.ERROR; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveId; import lombok.NonNull; import org.mapstruct.Mapper; @@ -33,6 +34,7 @@ * * @author Sebastien Vermeille */ +@Beta @Mapper(unmappedTargetPolicy = ERROR) // ignore as we are using a map public interface StoveIdMapper { default StoveId map(@NonNull Long stoveId) { diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java index f1a6558b..8a9732da 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapper.java @@ -22,11 +22,16 @@ */ package dev.cookiecode.rika2mqtt.plugins.internal.v1.mapper; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.StoveStatus; import lombok.NonNull; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; +/** + * @author Sebastien Vermeille + */ +@Beta @Mapper( unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = { diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java index 244e9bba..eb98a1e7 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapper.java @@ -25,11 +25,16 @@ import static java.lang.Integer.parseInt; import static org.mapstruct.ReportingPolicy.ERROR; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeDefinition; import dev.cookiecode.rika2mqtt.plugins.api.v1.model.TimeRange; import lombok.NonNull; import org.mapstruct.Mapper; +/** + * @author Sebastien Vermeille + */ +@Beta @Mapper(unmappedTargetPolicy = ERROR) public interface TimeRangeMapper { /** diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java index 424d5aaf..49e91fb8 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Pf4jPluginManagerConfig.java @@ -26,6 +26,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * @author Sebastien Vermeille + */ @Configuration public class Pf4jPluginManagerConfig { diff --git a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java index e22a6f86..79be6ee4 100644 --- a/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java +++ b/plugins-internal/src/main/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/Rika2MqttPluginManager.java @@ -25,6 +25,7 @@ import static org.pf4j.PluginState.*; import com.google.common.annotations.VisibleForTesting; +import dev.cookiecode.rika2mqtt.plugins.api.Beta; import dev.cookiecode.rika2mqtt.plugins.api.v1.PluginConfiguration; import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; import dev.cookiecode.rika2mqtt.plugins.api.v1.annotations.ConfigurablePlugin; @@ -34,6 +35,10 @@ import lombok.extern.flogger.Flogger; import org.pf4j.*; +/** + * @author Sebastien Vermeille + */ +@Beta @Flogger public class Rika2MqttPluginManager extends DefaultPluginManager { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java index e833fbf4..53dd9e27 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/PluginDownloaderTest.java @@ -40,7 +40,11 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.env.Environment; -/** Test class */ +/** + * Test class + * + * @author Sebastien Vermeille + */ @ExtendWith(MockitoExtension.class) class PluginDownloaderTest { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java index 2a02f171..01ce7bef 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/Rika2MqttPluginServiceTest.java @@ -35,7 +35,11 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.pf4j.PluginManager; -/** Test class */ +/** + * Test class + * + * @author Sebastien Vermeille + */ @ExtendWith(MockitoExtension.class) class Rika2MqttPluginServiceTest { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java index a00a147b..8671922f 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ControlsMapperTest.java @@ -29,7 +29,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -/** Test class */ +/** + * Test class + * + * @author Sebastien Vermeille + */ @SpringBootTest( classes = {ControlsMapperImpl.class, TimeRangeMapperImpl.class, ConvectionFanMapperImpl.class}) class ControlsMapperTest { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java index 137bbcaa..50181a16 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/ConvectionFanMapperTest.java @@ -29,7 +29,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -/** Test class */ +/** + * Test class + * + * @author Sebastien Vermeille + */ @SpringBootTest(classes = {ConvectionFanMapperImpl.class}) class ConvectionFanMapperTest { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java index 5dcc84a4..477cb9ca 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/SensorsMapperTest.java @@ -29,6 +29,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +/** + * Test class + * + * @author Sebastien Vermeille + */ @SpringBootTest(classes = {SensorsMapperImpl.class}) class SensorsMapperTest { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java index e82ebfff..1a00e4c4 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/StoveStatusMapperTest.java @@ -33,7 +33,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -/** Test class */ +/** + * Test class + * + * @author Sebastien Vermeille + */ @SpringBootTest( classes = { StoveStatusMapperImpl.class, diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java index a37e72d4..4917eba0 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/TimeRangeMapperTest.java @@ -29,7 +29,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -/** Test class */ +/** + * Test class + * + * @author Sebastien Vermeille + */ @SpringBootTest(classes = {TimeRangeMapperImpl.class}) class TimeRangeMapperTest { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java index edc19394..1c95a701 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/ControlsMapperEmptyImpl.java @@ -28,8 +28,10 @@ import org.springframework.stereotype.Component; /** - * This class is intentionally very empty (It allows to keep tests valuable and not repeating - * themselves) + * Test class This class is intentionally very empty (It allows to keep tests valuable and not + * repeating themselves) + * + * @author Sebastien Vermeille */ @Component public class ControlsMapperEmptyImpl implements ControlsMapper { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java index 172a9a30..dc1ec26b 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/mapper/helper/SensorsMapperEmptyImpl.java @@ -28,8 +28,10 @@ import org.springframework.stereotype.Component; /** - * This class is intentionally very empty (It allows to keep tests valuable and not repeating - * themselves) + * Test class This class is intentionally very empty (It allows to keep tests valuable and not + * repeating themselves) + * + * @author Sebastien Vermeille */ @Component public class SensorsMapperEmptyImpl implements SensorsMapper { diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/DummyConfigurablePlugin.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/DummyConfigurablePlugin.java index a6961820..5c363475 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/DummyConfigurablePlugin.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/DummyConfigurablePlugin.java @@ -31,6 +31,8 @@ /** * Test class An example of plugin not implementing ConfigurablePlugin used for testing a behaviour + * + * @author Sebastien Vermeille */ public class DummyConfigurablePlugin extends Rika2MqttPlugin implements ConfigurablePlugin { @Override diff --git a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/NotConfigurablePlugin.java b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/NotConfigurablePlugin.java index a4e1a973..323268f1 100644 --- a/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/NotConfigurablePlugin.java +++ b/plugins-internal/src/test/java/dev/cookiecode/rika2mqtt/plugins/internal/v1/pf4j/NotConfigurablePlugin.java @@ -26,5 +26,7 @@ /** * Test class An example of plugin not implementing ConfigurablePlugin used for testing a behaviour + * + * @author Sebastien Vermeille */ public class NotConfigurablePlugin extends Rika2MqttPlugin {} diff --git a/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java b/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java index c4c8b0d4..350b2b30 100644 --- a/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java +++ b/rika2mqtt-example-plugin-using-config/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePluginUsingConfig.java @@ -31,6 +31,11 @@ import java.util.List; import lombok.extern.flogger.Flogger; +/** + * Example of a plugin class that declares requiring some configuration + * + * @author Sebastien Vermeille + */ @Flogger public class ExamplePluginUsingConfig extends Rika2MqttPlugin implements ConfigurablePlugin { @@ -48,6 +53,9 @@ public void stop() { @Override public List declarePluginConfigurationParameters() { + + // Here the plugin declares what it needs access to + return List.of( // Optional Parameters diff --git a/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java b/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java index 839489b6..f4cccffc 100644 --- a/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java +++ b/rika2mqtt-example-plugin-using-config/src/test/java/DummyTest.java @@ -25,6 +25,11 @@ import org.junit.jupiter.api.Test; +/** + * Test class + * + * @author Sebastien Vermeille + */ class DummyTest { @Test diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java index 2d2732fc..1d9a79af 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHook.java @@ -29,6 +29,11 @@ import lombok.extern.flogger.Flogger; import org.pf4j.Extension; +/** + * A sample hook (or listener) class + * + * @author Sebastien Vermeille + */ @Extension @Flogger public class ExampleHook implements StoveStatusExtension { diff --git a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java index ba4d0892..091e1b1b 100644 --- a/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java +++ b/rika2mqtt-example-plugin/src/main/java/dev/cookiecode/rika2mqtt/plugins/example/ExamplePlugin.java @@ -25,6 +25,11 @@ import dev.cookiecode.rika2mqtt.plugins.api.v1.Rika2MqttPlugin; import lombok.extern.flogger.Flogger; +/** + * A sample plugin class + * + * @author Sebastien Vermeille + */ @Flogger public class ExamplePlugin extends Rika2MqttPlugin { diff --git a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java index 334cd1bf..92543368 100644 --- a/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java +++ b/rika2mqtt-example-plugin/src/test/java/dev/cookiecode/rika2mqtt/plugins/example/ExampleHookTest.java @@ -26,6 +26,11 @@ import org.junit.jupiter.api.Test; +/** + * Test class + * + * @author Sebastien Vermeille + */ class ExampleHookTest { @Test