Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/plugin api support (beta) #146

Merged
merged 42 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
833135f
First iteration
sebastienvermeille Oct 8, 2023
f09cd7c
Stable status before review
sebastienvermeille Dec 18, 2023
f293f4f
Stable status before review
sebastienvermeille Dec 18, 2023
f2af323
Fix format
sebastienvermeille Dec 18, 2023
22785f1
Add missing header
sebastienvermeille Dec 18, 2023
31ac619
Document useful command lines
sebastienvermeille Dec 18, 2023
51b2c44
exclude disabled.txt
sebastienvermeille Dec 18, 2023
06cdd5f
Add unit tests for API module
sebastienvermeille Dec 18, 2023
930db51
Cleanup example plugin code
sebastienvermeille Dec 18, 2023
f2bc82c
Refactore/add some tests
sebastienvermeille Dec 18, 2023
3de59a8
Refactore/add some tests
sebastienvermeille Dec 18, 2023
07fa0b7
Refactore/add some tests
sebastienvermeille Dec 18, 2023
8463522
Refactore/add some tests
sebastienvermeille Dec 18, 2023
d8e27ec
Refactore/add some tests
sebastienvermeille Dec 18, 2023
69516b9
Adapt for sonar analysis
sebastienvermeille Dec 18, 2023
3196db7
Adapt for sonar analysis
sebastienvermeille Dec 18, 2023
b28b6b2
Fix sonar issues
sebastienvermeille Dec 18, 2023
0d70b41
Fix sonar issues
sebastienvermeille Dec 18, 2023
6461ccf
Fix sonar issues
sebastienvermeille Dec 18, 2023
6eaaba3
Fix sonar issues
sebastienvermeille Dec 18, 2023
428cf5c
Introduce @Beta annotation to mark features that should not be used f…
sebastienvermeille Dec 20, 2023
a9c4eeb
Introduce configuration plugin (wip)
sebastienvermeille Dec 21, 2023
bef7963
cleanup
sebastienvermeille Dec 21, 2023
4cd999a
cleanup
sebastienvermeille Dec 21, 2023
7092289
cleanup
sebastienvermeille Dec 21, 2023
5a60ea3
cleanup
sebastienvermeille Dec 21, 2023
dfe333c
cleanup
sebastienvermeille Dec 21, 2023
7d4b07b
cleanup
sebastienvermeille Dec 21, 2023
c8499df
cleanup
sebastienvermeille Dec 21, 2023
fe80237
cleanup
sebastienvermeille Dec 21, 2023
e726267
cleanup
sebastienvermeille Dec 21, 2023
033e155
cleanup
sebastienvermeille Dec 21, 2023
c02bada
cleanup
sebastienvermeille Dec 30, 2023
c37ea6c
cleanup
sebastienvermeille Dec 30, 2023
7205b53
cleanup
sebastienvermeille Dec 30, 2023
5447dc4
cleanup
sebastienvermeille Dec 30, 2023
24b3317
cleanup
sebastienvermeille Dec 30, 2023
14bd6bd
cleanup
sebastienvermeille Dec 31, 2023
d1282e7
cleanup
sebastienvermeille Dec 31, 2023
abeec8b
cleanup
sebastienvermeille Dec 31, 2023
21e61b0
cleanup
sebastienvermeille Dec 31, 2023
adc66f4
cleanup
sebastienvermeille Dec 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .code/docker-compose/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,26 @@ 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:
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"
3 changes: 2 additions & 1 deletion .docker/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
-jar rika2mqtt.jar
5 changes: 5 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,10 @@ jobs:
bridge/target
mqtt/target
rika-firenet/target
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
7 changes: 6 additions & 1 deletion DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -45,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`
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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/ .
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
@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)
6 changes: 6 additions & 0 deletions bridge/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@
<scope>compile</scope>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>dev.cookiecode</groupId>
<artifactId>plugins-internal</artifactId>
<version>1.1.0</version>
<scope>compile</scope>
</dependency>
<!-- 3rd part dependencies-->
<dependency>
<groupId>org.glassfish.expressly</groupId>
Expand Down
18 changes: 17 additions & 1 deletion bridge/src/main/java/dev/cookiecode/rika2mqtt/bridge/Bridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.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;
import dev.cookiecode.rika2mqtt.rika.firenet.exception.CouldNotAuthenticateToRikaFirenetException;
import dev.cookiecode.rika2mqtt.rika.firenet.exception.InvalidStoveIdException;
Expand All @@ -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;
Expand Down Expand Up @@ -76,6 +80,11 @@ public class Bridge {

private final EmailObfuscator emailObfuscator;
private final Gson gson;
private final StoveStatusMapper stoveStatusMapper;

private final Rika2MqttPluginService pluginManager;

private final ApplicationEventPublisher applicationEventPublisher;

private final List<StoveId> stoveIds = new ArrayList<>();

Expand All @@ -85,6 +94,7 @@ void init() {

initStoves(rikaFirenetService.getStoves());
printStartupMessages();
pluginManager.start();
publishToMqtt();
}

Expand Down Expand Up @@ -123,7 +133,13 @@ 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))
.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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@

import com.google.gson.Gson;
import dev.cookiecode.rika2mqtt.bridge.misc.EmailObfuscator;
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;
import dev.cookiecode.rika2mqtt.rika.mqtt.MqttService;
Expand All @@ -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
Expand All @@ -66,6 +69,11 @@ class BridgeTest {
@Mock MqttService mqttService;
@Mock EmailObfuscator emailObfuscator;
@Mock Gson gson;
@Mock StoveStatusMapper stoveStatusMapper;

@Mock Rika2MqttPluginService pluginManager;

@Mock ApplicationEventPublisher applicationEventPublisher;
@InjectMocks @Spy Bridge bridge;

@BeforeEach
Expand Down
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
44 changes: 44 additions & 0 deletions docs/write-a-plugin.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 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.


## Make a plugin configurable by the end user
Make your plugin main class implement `ConfigurablePlugin`

### Define plugin parameters
Implement: `List<PluginConfigurationParameter> 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!
22 changes: 22 additions & 0 deletions plugins-api/README.md
Original file line number Diff line number Diff line change
@@ -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/

75 changes: 75 additions & 0 deletions plugins-api/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

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.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.cookiecode</groupId>
<artifactId>rika2mqtt-parent</artifactId>
<version>1.1.0</version>
</parent>

<artifactId>plugins-api</artifactId>

<properties>
<rika2mqtt.root>${basedir}/..</rika2mqtt.root>
<maven.compiler.source>${java.sdk.version}</maven.compiler.source>
<maven.compiler.target>${java.sdk.version}</maven.compiler.target>
<project.build.sourceEncoding>${source.encoding}</project.build.sourceEncoding>
<!-- sonar analysis -->
<sonar.projectKey>${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId}</sonar.projectKey>
</properties>

<dependencies>
<dependency>
<groupId>org.pf4j</groupId>
<artifactId>pf4j</artifactId>
<version>${pf4j.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.sdk.version}</source>
<target>${java.sdk.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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
*
* @author Sebastien Vermeille
*/
public @interface Beta {}
Loading
Loading