From 163f517f6f198b26b0731a386657f7be55bc983b Mon Sep 17 00:00:00 2001 From: Scott Hanson Date: Fri, 20 Sep 2024 14:59:10 -0400 Subject: [PATCH] [fpp] Initial contribution (#16298) * working on FPP Signed-off-by: Scott Hanson --- CODEOWNERS | 1 + bundles/org.openhab.binding.mqtt.fpp/NOTICE | 13 + .../org.openhab.binding.mqtt.fpp/README.md | 92 ++++++ bundles/org.openhab.binding.mqtt.fpp/pom.xml | 24 ++ .../src/main/feature/feature.xml | 12 + .../mqtt/fpp/internal/ConfigOptions.java | 26 ++ .../fpp/internal/FPPBindingConstants.java | 73 +++++ .../mqtt/fpp/internal/FPPHandlerFactory.java | 59 ++++ .../binding/mqtt/fpp/internal/FPPStatus.java | 156 ++++++++++ .../fpp/internal/dto/FPPCurrentPlaylist.java | 40 +++ .../fpp/internal/dto/FPPNextPlaylist.java | 45 +++ .../mqtt/fpp/internal/dto/FPPPlaylist.java | 53 ++++ .../mqtt/fpp/internal/dto/FPPScheduler.java | 40 +++ .../internal/handler/FPPPlayerHandler.java | 282 ++++++++++++++++++ .../main/resources/OH-INF/config/config.xml | 19 ++ .../resources/OH-INF/i18n/mqtt.properties | 56 ++++ .../resources/OH-INF/thing/thing-types.xml | 169 +++++++++++ bundles/pom.xml | 1 + .../src/main/resources/footer.xml | 1 + 19 files changed, 1162 insertions(+) create mode 100644 bundles/org.openhab.binding.mqtt.fpp/NOTICE create mode 100644 bundles/org.openhab.binding.mqtt.fpp/README.md create mode 100644 bundles/org.openhab.binding.mqtt.fpp/pom.xml create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/ConfigOptions.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPBindingConstants.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPHandlerFactory.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPStatus.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPCurrentPlaylist.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPNextPlaylist.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPPlaylist.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPScheduler.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/handler/FPPPlayerHandler.java create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/config/config.xml create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/i18n/mqtt.properties create mode 100644 bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/thing/thing-types.xml diff --git a/CODEOWNERS b/CODEOWNERS index 486a1c2e5d6d8..28ec2b43f63d9 100755 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -235,6 +235,7 @@ /bundles/org.openhab.binding.mpd/ @stefanroellin /bundles/org.openhab.binding.mqtt/ @ccutrer /bundles/org.openhab.binding.mqtt.espmilighthub/ @Skinah +/bundles/org.openhab.binding.mqtt.fpp/ @computergeek1507 /bundles/org.openhab.binding.mqtt.generic/ @ccutrer /bundles/org.openhab.binding.mqtt.homeassistant/ @antroids @ccutrer /bundles/org.openhab.binding.mqtt.homie/ @ccutrer diff --git a/bundles/org.openhab.binding.mqtt.fpp/NOTICE b/bundles/org.openhab.binding.mqtt.fpp/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.mqtt.fpp/README.md b/bundles/org.openhab.binding.mqtt.fpp/README.md new file mode 100644 index 0000000000000..19d46cac76a69 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/README.md @@ -0,0 +1,92 @@ +# FPP Binding + +Binding to control Falcon Player (FPP) Devices using MQTT and HTTP. Status messages are recieved over MQTT and Commands are HTTP Commands. + +## Discovery + +Autodiscovering is not supported. We have to define the things manually. + +## Supported Things + +The binding supports one Thing `player` that represents the Falcon Player. +## Thing Configuration + +| Parameter | Description | Required | Default | +|--------------|-----------------------------------------|----------|---------| +| `playerIP` | IP Address or Host Name of FPP Devive | Y | | +| `playerMQTT` | MQTT Topic of FPP Devive Status Updates | Y | | + +## Channels + +| Channel | Type | Description | +|----------------------------------------|--------------------|-------------------------------------------| +| `player` | Player | Play/Stop Current Playlist. | +| `volume` | Dimmer | Playback Audio Volume. | +| `status` | String | Playback Status. | +| `mode` | String | Playback Mode. | +| `uptime` | Number:Time | Device Uptime. | +| `testing-enabled` | Switch | Enabled/Disable Sending Testing Data. | +| `current-sequence` | String (read only) | Currently Playing Sequence File. | +| `current-song` | String (read only) | Currently Playing Audio/Media File. | +| `current-playlist` | String (read only) | Currently Playing Playlist. | +| `seconds-played` | Number:Time | Sequence Playback time in secs. | +| `seconds-remaining` | Number:Time | Sequence Playback time remaining in secs. | +| `last-playlist` | String | Lasted Played Playlist. | +| `bridging-enabled` | Switch | Is Recieving Bridge Data. | +| `multisync-enabled` | Switch | Multisync Mode Enabled. | +| `scheduler-current-playlist` | String (read only) | Scheduler Current Playlist. | +| `scheduler-current-playlist-start` | String (read only) | Scheduler Current Playlist Start Time. | +| `scheduler-current-playlist-end` | String (read only) | Scheduler Current Playlist End Time. | +| `scheduler-current-playlist-stop-type` | String (read only) | Scheduler Current Playlist End Type. | +| `scheduler-next-playlist` | String (read only) | Next Scheduled Playlist. | +| `scheduler-next-playlist-start` | String (read only) | Next Scheduled Start Time. | + + +## Full Example + +To use these examples for textual configuration, you must already have a configured MQTT `broker` thing, and know its unique ID. +This UID will be used in the things file and will replace the text `myBroker`. +The first line in the things file will create a `broker` thing and this can be removed if you have already setup a broker in another file or via the UI already. + +### fpp.things + +```java +Bridge mqtt:broker:myBroker [ host="localhost", secure=false, password="*******", qos=1, username="user"] +Thing mqtt:player:myBroker:mainPlayer "Main Player" (mqtt:broker:myBroker) @ "MQTT" +``` + +### fpp.items + +```java +Player FPP_Player "FPP Player" {channel="mqtt:player:myBroker:mainPlayer:player"} +Dimmer Audio_Volume "Audio Volume" {channel="mqtt:player:myBroker:mainPlayer:volume"} +String Current_Sequence "Current Sequence" {channel="mqtt:player:myBroker:mainPlayer:current-sequence"} +String Current_Song "Current Song" {channel="mqtt:player:myBroker:mainPlayer:current-song"} +String Current_Playlist "Current Playlist" {channel="mqtt:player:myBroker:mainPlayer:current-playlist"} +String Status "FPP Status" {channel="mqtt:player:myBroker:mainPlayer:status"} +String Mode "FPP Mode" {channel="mqtt:player:myBroker:mainPlayer:mode"} +String Last_Playlist "Last Playlist" {channel="mqtt:player:myBroker:mainPlayer:last-playlist"} +Number:Time Seconds_Played "Seconds Played [%d %unit%]" {channel="mqtt:player:myBroker:mainPlayer:seconds-played"} +Number:Time Seconds_Remaining "Seconds Remaining [%d %unit%]" {channel="mqtt:player:myBroker:mainPlayer:seconds-remaining"} +Switch Testing "Testing Mode" {channel="mqtt:player:myBroker:mainPlayer:testing-enabled"} +Switch Multisync "Multisync" {channel="mqtt:player:myBroker:mainPlayer:multisync-enabled"} +``` + +### fpp.sitemap + +```perl +Text label="Main Player" +{ + Player item=FPP_Player + Switch item=Testing + Slider item=Audio_Volume + Text item=Current_Sequence + Text item=Current_Song + Text item=Current_Playlist + Text item=Status + Text item=Mode + Selection item=Last_Playlist + Switch item=Testing + Switch item=Multisync +} +``` diff --git a/bundles/org.openhab.binding.mqtt.fpp/pom.xml b/bundles/org.openhab.binding.mqtt.fpp/pom.xml new file mode 100644 index 0000000000000..b6c67c8aadeb9 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/pom.xml @@ -0,0 +1,24 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 4.3.0-SNAPSHOT + + + org.openhab.binding.mqtt.fpp + openHAB Add-ons :: Bundles :: MQTT FPP + + + + org.openhab.addons.bundles + org.openhab.binding.mqtt + ${project.version} + provided + + + diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/feature/feature.xml b/bundles/org.openhab.binding.mqtt.fpp/src/main/feature/feature.xml new file mode 100644 index 0000000000000..ca8ff9fdadfd0 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/feature/feature.xml @@ -0,0 +1,12 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + openhab-transport-mqtt + mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version} + mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.fpp/${project.version} + + + diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/ConfigOptions.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/ConfigOptions.java new file mode 100644 index 0000000000000..9ffcada0992e3 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/ConfigOptions.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ConfigOptions} Holds the config for the settings. + * + * @author Scott Hanson - Initial contribution + */ +@NonNullByDefault +public class ConfigOptions { + public String playerAddress = ""; + public String playerMQTTTopic = ""; +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPBindingConstants.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPBindingConstants.java new file mode 100644 index 0000000000000..9c27ea30ec233 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPBindingConstants.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal; + +import static org.openhab.binding.mqtt.MqttBindingConstants.BINDING_ID; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link FPPBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Scott Hanson - Initial contribution + */ +@NonNullByDefault +public class FPPBindingConstants { + // falcon/player/FPP/fppd_status + public static final String STATUS_TOPIC = "fppd_status"; + public static final String VERSION_TOPIC = "version"; + public static final String PLAYLIST_TOPIC = "playlist"; + public static final String MQTT_PREFIX = "falcon/player/"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_PLAYER = new ThingTypeUID(BINDING_ID, "player"); + + public static final Set SUPPORTED_THING_TYPES = Set.of(THING_TYPE_PLAYER); + + // Channels + public static final String CHANNEL_PLAYER = "player"; + public static final String CHANNEL_STATUS = "status"; + public static final String CHANNEL_VOLUME = "volume"; + public static final String CHANNEL_MODE = "mode"; + public static final String CHANNEL_CURRENT_SEQUENCE = "current-sequence"; + public static final String CHANNEL_CURRENT_SONG = "current-song"; + public static final String CHANNEL_CURRENT_PLAYLIST = "current-playlist"; + public static final String CHANNEL_SEC_PLAYED = "seconds-played"; + public static final String CHANNEL_SEC_REMAINING = "seconds-remaining"; + public static final String CHANNEL_UPTIME = "uptime"; + public static final String CHANNEL_BRIDGING = "bridging-enabled"; + public static final String CHANNEL_MULTISYNC = "multisync-enabled"; + public static final String CHANNEL_TESTING = "testing-enabled"; + public static final String CHANNEL_LAST_PLAYLIST = "last-playlist"; + + public static final String CHANNEL_SCHEDULER_STATUS = "scheduler-status"; + public static final String CHANNEL_SCHEDULER_CURRENT_PLAYLIST = "scheduler-current-playlist"; + public static final String CHANNEL_SCHEDULER_CURRENT_PLAYLIST_START = "scheduler-current-playlist-start"; + public static final String CHANNEL_SCHEDULER_CURRENT_PLAYLIST_END = "scheduler-current-playlist-end"; + public static final String CHANNEL_SCHEDULER_CURRENT_PLAYLIST_STOP_TYPE = "scheduler-current-playlist-stop-type"; + public static final String CHANNEL_SCHEDULER_NEXT_PLAYLIST = "scheduler-next-playlist"; + public static final String CHANNEL_SCHEDULER_NEXT_PLAYLIST_START = "scheduler-next-playlist-start"; + + public static final String PROPERTY_UUID = "uuid"; + public static final String PROPERTY_SOFTWARE_VERSION = "Software Version"; + + // Status + public static final String CONNECTED = "connected"; + public static final String CHANNEL_STATUS_NAME = "status-name"; + + public static final String TESTING = "testing"; +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPHandlerFactory.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPHandlerFactory.java new file mode 100644 index 0000000000000..96d132ff765c1 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPHandlerFactory.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal; + +import static org.openhab.binding.mqtt.fpp.internal.FPPBindingConstants.SUPPORTED_THING_TYPES; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mqtt.fpp.internal.handler.FPPPlayerHandler; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingRegistry; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link FPPHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Scott Hanson - Initial contribution + */ +@Component(service = ThingHandlerFactory.class) +@NonNullByDefault +public class FPPHandlerFactory extends BaseThingHandlerFactory { + private final ThingRegistry thingRegistry; + + @Activate + public FPPHandlerFactory(final @Reference ThingRegistry thingRegistry) { + this.thingRegistry = thingRegistry; + } + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + if (SUPPORTED_THING_TYPES.contains(thingTypeUID)) { + return new FPPPlayerHandler(thing, thingRegistry); + } + return null; + } +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPStatus.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPStatus.java new file mode 100644 index 0000000000000..25adf6dd80559 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/FPPStatus.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * The {@link FPPStstus} is responsible for storing + * the FPP JSON Status data. + * + * @author Scott Hanson - Initial contribution + */ +public class FPPStatus { + /* + * { + * "MQTT" : + * { + * "configured" : true, + * "connected" : true + * }, + * "bridging" : false, + * "current_playlist" : + * { + * "count" : "0", + * "description" : "", + * "index" : "0", + * "playlist" : "", + * "type" : "" + * }, + * "current_sequence" : "", + * "current_song" : "", + * "dateStr" : "Sun Jan 7", + * "fppd" : "running", + * "mode" : 2, + * "mode_name" : "player", + * "multisync" : true, + * "next_playlist" : + * { + * "playlist" : "No playlist scheduled.", + * "start_time" : "" + * }, + * "repeat_mode" : "0", + * "scheduler" : + * { + * "enabled" : 1, + * "nextPlaylist" : + * { + * "playlistName" : "No playlist scheduled.", + * "scheduledStartTime" : 0, + * "scheduledStartTimeStr" : "" + * }, + * "status" : "idle" + * }, + * "seconds_played" : "0", + * "seconds_remaining" : "0", + * "sensors" : + * [ + * { + * "formatted" : "55.0", + * "label" : "CPU: ", + * "postfix" : "", + * "prefix" : "", + * "value" : 55.017000000000003, + * "valueType" : "Temperature" + * } + * ], + * "status" : 0, + * "status_name" : "idle", + * "time" : "Sun Jan 07 19:15:25 EST 2024", + * "timeStr" : "07:15 PM", + * "timeStrFull" : "07:15:25 PM", + * "time_elapsed" : "00:00", + * "time_remaining" : "00:00", + * "uptime" : "2 days, 01:30:04", + * "uptimeDays" : 2.0625462962962962, + * "uptimeHours" : 1.5011111111111111, + * "uptimeMinutes" : 30.066666666666666, + * "uptimeSeconds" : 4, + * "uptimeStr" : "2 days, 1 hours, 30 minutes, 4 seconds", + * "uptimeTotalSeconds" : 178204, + * "uuid" : "M1-10000000fd93cfe5", + * "volume" : 48 + * } + * + */ + @SerializedName("status") + @Expose + public int status; + + @SerializedName("status_name") + @Expose + public String status_name; + + @SerializedName("mode_name") + @Expose + public String mode_name; + + @SerializedName("current_sequence") + @Expose + public String current_sequence; + + @SerializedName("current_song") + @Expose + public String current_song; + + @SerializedName("time_elapsed") + @Expose + public String time_elapsed; + + @SerializedName("uptimeTotalSeconds") + @Expose + public long uptimeTotalSeconds; + + @SerializedName("seconds_played") + @Expose + public int seconds_played; + + @SerializedName("seconds_remaining") + @Expose + public int seconds_remaining; + + @SerializedName("volume") + @Expose + public int volume; + + @SerializedName("uuid") + @Expose + public String uuid; + + @SerializedName("multisync") + @Expose + public boolean multisync; + + @SerializedName("current_playlist") + @Expose + public FPPPlaylist current_playlist; + + @SerializedName("bridging") + @Expose + public boolean bridging; + + @SerializedName("scheduler") + @Expose + public FPPScheduler scheduler; +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPCurrentPlaylist.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPCurrentPlaylist.java new file mode 100644 index 0000000000000..3b4a4a3d918ad --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPCurrentPlaylist.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * The {@link FPPCurrentPlaylist} is responsible for storing + * the FPP JSON Current Playlist data. + * + * @author Scott Hanson - Initial contribution + */ +public class FPPCurrentPlaylist { + @SerializedName("playlistName") + @Expose + public String playlistName; + + @SerializedName("scheduledStartTimeStr") + @Expose + public String scheduledStartTimeStr; + + @SerializedName("scheduledEndTimeStr") + @Expose + public String scheduledEndTimeStr; + + @SerializedName("stopTypeStr") + @Expose + public String stopTypeStr; +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPNextPlaylist.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPNextPlaylist.java new file mode 100644 index 0000000000000..34c8f8c123bd7 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPNextPlaylist.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * The {@link FPPNextPlaylist} is responsible for storing + * the FPP JSON Next Playlist data. + * + * @author Scott Hanson - Initial contribution + */ +public class FPPNextPlaylist { + /* + * "nextPlaylist" : + * { + * "playlistName" : "No playlist scheduled.", + * "scheduledStartTime" : 0, + * "scheduledStartTimeStr" : "" + * }, + */ + + @SerializedName("playlistName") + @Expose + public String playlistName; + + @SerializedName("scheduledStartTimeStr") + @Expose + public String scheduledStartTimeStr; + + @SerializedName("scheduledStartTime") + @Expose + public int scheduledStartTime; +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPPlaylist.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPPlaylist.java new file mode 100644 index 0000000000000..0a2495bdf0fb3 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPPlaylist.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * The {@link FPPPlaylist} is responsible for storing + * the FPP JSON Status data. + * + * @author Scott Hanson - Initial contribution + */ +public class FPPPlaylist { + /* + * + * "current_playlist" : + * { + * "count" : "0", + * "description" : "", + * "index" : "0", + * "playlist" : "", + * "type" : "" + * } + * + */ + + @SerializedName("playlist") + @Expose + public String playlist; + + @SerializedName("description") + @Expose + public String description; + + @SerializedName("count") + @Expose + public int count; + + @SerializedName("index") + @Expose + public int index; +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPScheduler.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPScheduler.java new file mode 100644 index 0000000000000..783ec83a5e630 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/dto/FPPScheduler.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * The {@link FPPPlaylist} is responsible for storing + * the FPP JSON FPPScheduler data. + * + * @author Scott Hanson - Initial contribution + */ +public class FPPScheduler { + @SerializedName("currentPlaylist") + @Expose + public FPPCurrentPlaylist currentPlaylist; + + @SerializedName("nextPlaylist") + @Expose + public FPPNextPlaylist nextPlaylist; + + @SerializedName("enabled") + @Expose + public int enabled; + + @SerializedName("status") + @Expose + public String status; +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/handler/FPPPlayerHandler.java b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/handler/FPPPlayerHandler.java new file mode 100644 index 0000000000000..bb89b7562a3c7 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/java/org/openhab/binding/mqtt/fpp/internal/handler/FPPPlayerHandler.java @@ -0,0 +1,282 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.fpp.internal.handler; + +import static org.openhab.binding.mqtt.fpp.internal.FPPBindingConstants.*; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mqtt.fpp.internal.ConfigOptions; +import org.openhab.binding.mqtt.fpp.internal.FPPStatus; +import org.openhab.binding.mqtt.handler.AbstractBrokerHandler; +import org.openhab.core.io.net.http.HttpUtil; +import org.openhab.core.io.transport.mqtt.MqttBrokerConnection; +import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber; +import org.openhab.core.library.types.IncreaseDecreaseType; +import org.openhab.core.library.types.NextPreviousType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.PlayPauseType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingRegistry; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingStatusInfo; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; + +/** + * The {@link FPPPlayerHandler} is responsible for handling commands of the globes, which are then + * sent to one of the bridges to be sent out by MQTT. + * + * @author Scott Hanson - Initial contribution + */ +@NonNullByDefault +public class FPPPlayerHandler extends BaseThingHandler implements MqttMessageSubscriber { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private @Nullable MqttBrokerConnection connection; + + private String fullStatusTopic = ""; + private String fullVersionTopic = ""; + private int lastKnownVolume = 50; + private String lastPlaylist = ""; + private ConfigOptions config = new ConfigOptions(); + + private final Gson gson = new Gson(); + + public FPPPlayerHandler(Thing thing, ThingRegistry thingRegistry) { + super(thing); + } + + private void processIncomingState(String messageJSON) { + FPPStatus data = gson.fromJson(messageJSON, FPPStatus.class); + updateState(CHANNEL_STATUS, new StringType(data.status_name)); + updateState(CHANNEL_PLAYER, data.status == 1 ? PlayPauseType.PLAY : PlayPauseType.PAUSE); + lastKnownVolume = data.volume; + updateState(CHANNEL_VOLUME, new PercentType(data.volume)); + updateState(CHANNEL_MODE, new StringType(data.mode_name)); + updateState(CHANNEL_CURRENT_SEQUENCE, new StringType(data.current_sequence)); + updateState(CHANNEL_CURRENT_SONG, new StringType(data.current_song)); + updateState(CHANNEL_CURRENT_PLAYLIST, new StringType(data.current_playlist.playlist)); + updateState(CHANNEL_SEC_PLAYED, new QuantityType<>(new BigDecimal(data.seconds_played), Units.SECOND)); + updateState(CHANNEL_SEC_REMAINING, new QuantityType<>(new BigDecimal(data.seconds_remaining), Units.SECOND)); + updateState(CHANNEL_UPTIME, new QuantityType<>(new BigDecimal(data.uptimeTotalSeconds), Units.SECOND)); + updateState(CHANNEL_BRIDGING, OnOffType.from(data.bridging)); + updateState(CHANNEL_MULTISYNC, OnOffType.from(data.multisync)); + updateState(CHANNEL_TESTING, data.status_name.equals(TESTING) ? OnOffType.ON : OnOffType.OFF); + + updateState(CHANNEL_SCHEDULER_STATUS, new StringType(data.scheduler.status)); + updateState(CHANNEL_SCHEDULER_NEXT_PLAYLIST, new StringType(data.scheduler.nextPlaylist.playlistName)); + updateState(CHANNEL_SCHEDULER_NEXT_PLAYLIST_START, + new StringType(data.scheduler.nextPlaylist.scheduledStartTimeStr)); + + if (data.scheduler.currentPlaylist != null) { + updateState(CHANNEL_SCHEDULER_CURRENT_PLAYLIST, + new StringType(data.scheduler.currentPlaylist.playlistName)); + updateState(CHANNEL_SCHEDULER_CURRENT_PLAYLIST_START, + new StringType(data.scheduler.currentPlaylist.playlistName)); + updateState(CHANNEL_SCHEDULER_CURRENT_PLAYLIST_END, + new StringType(data.scheduler.currentPlaylist.playlistName)); + updateState(CHANNEL_SCHEDULER_CURRENT_PLAYLIST_STOP_TYPE, + new StringType(data.scheduler.currentPlaylist.playlistName)); + } + + if (!data.current_playlist.playlist.isEmpty()) { + lastPlaylist = data.current_playlist.playlist; + updateState(CHANNEL_LAST_PLAYLIST, new StringType(lastPlaylist)); + } + + thing.setProperty(PROPERTY_UUID, data.uuid); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + return; + } + String channelId = channelUID.getId(); + if (channelId.equals(CHANNEL_PLAYER)) { + if (command == PlayPauseType.PAUSE || command == OnOffType.OFF) { + executeGet("/api/playlists/stop"); + } else if (command == PlayPauseType.PLAY || command == OnOffType.ON) { + if (!lastPlaylist.isEmpty()) { + executeGet("/api/playlist/" + lastPlaylist + "/start"); + } + } else if (command == NextPreviousType.NEXT) { + executeGet("/api/command/Next Playlist Item"); + } else if (command == NextPreviousType.PREVIOUS) { + executeGet("/api/command/Prev Playlist Item"); + } + } + if (channelId.equals(CHANNEL_VOLUME)) { + Integer volume = null; + if (command instanceof PercentType percentCommand) { + volume = percentCommand.intValue(); + } else if (command == OnOffType.OFF) { + volume = 0; + } else if (command == OnOffType.ON) { + volume = lastKnownVolume; + } else if (command == IncreaseDecreaseType.INCREASE) { + if (lastKnownVolume < 100) { + lastKnownVolume++; + volume = lastKnownVolume; + } + } else if (command == IncreaseDecreaseType.DECREASE) { + if (lastKnownVolume > 0) { + lastKnownVolume--; + volume = lastKnownVolume; + } + } + if (volume != null) { + lastKnownVolume = volume; + executePost("/api/system/volume", "{\"volume\":" + lastKnownVolume + "}"); + updateState(CHANNEL_VOLUME, new PercentType(lastKnownVolume)); + } + } + if (channelId.equals(CHANNEL_TESTING)) { + if (command == OnOffType.OFF) { + executePost("/api/testmode", GetTestMode(0)); + } else if (command == OnOffType.ON) { + executePost("/api/testmode", GetTestMode(1)); + } + } + } + + private String GetTestMode(int enable) { + return "{\"mode\":\"RGBChase\",\"subMode\":\"RGBChase-RGB\",\"cycleMS\":1000,\"colorPattern\":\"FF000000FF000000FF\",\"enabled\":" + + enable + ",\"channelSet\": \"1-1048576\",\"channelSetType\": \"channelRange\"}"; + } + + @Override + public void initialize() { + updateStatus(ThingStatus.UNKNOWN); + config = getConfigAs(ConfigOptions.class); + if (!config.playerMQTTTopic.isEmpty()) { + fullStatusTopic = config.playerMQTTTopic + "/" + STATUS_TOPIC; + fullVersionTopic = config.playerMQTTTopic + "/" + VERSION_TOPIC; + bridgeStatusChanged(getBridgeStatus()); + updateStatus(ThingStatus.ONLINE); + } + } + + @Override + public void processMessage(String topic, byte[] payload) { + String state = new String(payload, StandardCharsets.UTF_8); + logger.trace("Received the following new FPP state:{}:{}", topic, state); + + if (topic.endsWith(STATUS_TOPIC)) { + processIncomingState(state); + } else if (topic.endsWith(VERSION_TOPIC)) { + thing.setProperty(PROPERTY_SOFTWARE_VERSION, state); + updateStatus(ThingStatus.ONLINE); + } + } + + public ThingStatusInfo getBridgeStatus() { + Bridge b = getBridge(); + if (b != null) { + return b.getStatusInfo(); + } else { + return new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, null); + } + } + + @Override + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + connection = null; + return; + } + + Bridge localBridge = this.getBridge(); + if (localBridge == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, + "Bridge is missing or offline, you need to setup a working MQTT broker first."); + return; + } + ThingHandler handler = localBridge.getHandler(); + if (handler instanceof AbstractBrokerHandler abh) { + final MqttBrokerConnection connection; + try { + connection = abh.getConnectionAsync().get(5000, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException ignored) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, + "Bridge handler has no valid broker connection!"); + return; + } + this.connection = connection; + updateStatus(ThingStatus.UNKNOWN); + if (!fullStatusTopic.isEmpty()) { + connection.subscribe(fullStatusTopic, this); + connection.subscribe(fullVersionTopic, this); + } + } + return; + } + + private @Nullable String executeGet(String url) { + String response = null; + try { + response = HttpUtil.executeUrl("GET", "http://" + config.playerAddress + url, 5000); + + } catch (IOException e) { + logger.warn("Failed HTTP Post", e); + } + return response; + } + + private boolean executePost(String url, String json) { + try { + Properties header = new Properties(); + header.put("Accept", "application/json"); + header.put("Content-Type", "application/json"); + String response = HttpUtil.executeUrl("POST", "http://" + config.playerAddress + url, header, + new ByteArrayInputStream(json.getBytes()), "application/json", 5000); + + return !response.isEmpty(); + } catch (IOException e) { + logger.warn("Failed HTTP Post", e); + } + return false; + } + + @Override + public void dispose() { + MqttBrokerConnection localConnection = connection; + if (localConnection != null) { + localConnection.unsubscribe(fullStatusTopic, this); + localConnection.unsubscribe(fullVersionTopic, this); + } + } +} diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/config/config.xml new file mode 100644 index 0000000000000..82c725d815a0a --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/config/config.xml @@ -0,0 +1,19 @@ + + + + + + network-address + + Player IP Address or Host Name + + + name + + MQTT Player Status Topic + + + diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/i18n/mqtt.properties b/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/i18n/mqtt.properties new file mode 100644 index 0000000000000..542c765a31955 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/i18n/mqtt.properties @@ -0,0 +1,56 @@ +# thing types + +thing.label.player=FPP Player +thing.description.player=FPP Player + +# thing types config + +thing-type.config.mqtt.player.playerAddress.label = Player Address +thing-type.config.mqtt.player.playerAddress.description = Player IP Address or Host Name +thing-type.config.mqtt.player.playerMQTTTopic.label = MQTT Player Topic +thing-type.config.mqtt.player.playerMQTTTopic.description = MQTT Player Status Topic + +# channel types + +channel-type.mqtt.player.status.label = Status +channel-type.mqtt.player.status.description = FPP Player Status +channel-type.mqtt.player.mode.label = Mode +channel-type.mqtt.player.mode.description = FPP Player Mode +channel-type.mqtt.player.current-sequence.label = Current Sequence +channel-type.mqtt.player.current-sequence.description = FPP Current Sequence +channel-type.mqtt.player.current-song.label = Current Song +channel-type.mqtt.player.current-song.description = FPP Current Song +channel-type.mqtt.player.current-playlist.label = Current Playlist +channel-type.mqtt.player.current-playlist.description = FPP Current Playlist +channel-type.mqtt.player.seconds-played.label = Seconds Played +channel-type.mqtt.player.seconds-played.description = FPP Seconds Played +channel-type.mqtt.player.seconds-remaining.label = Seconds Remaining +channel-type.mqtt.player.seconds-remaining.description = FPP Seconds Remaining +channel-type.mqtt.player.uptime.label = Uptime +channel-type.mqtt.player.uptime.description = FPP System Uptime (time after start) +channel-type.mqtt.player.player.label = Player +channel-type.mqtt.player.player.description = FPP Player Control +channel-type.mqtt.player.volume.label = Volume +channel-type.mqtt.player.volume.description = FPP Volume of the Output +channel-type.mqtt.player.bridging-enabled.label = Bridging +channel-type.mqtt.player.bridging-enabled.description = FPP Recieving Bridge Data +channel-type.mqtt.player.multisync-enabled.label = Multisync +channel-type.mqtt.player.multisync-enabled.description = FPP Multisync Mode Enabled +channel-type.mqtt.player.testing-enabled.label = Testing +channel-type.mqtt.player.testing-enabled.description = FPP Is In Test Mode +channel-type.mqtt.player.last-playlist.label = Last Run Playlist +channel-type.mqtt.player.last-playlist.description = FPP Last Run Playlist +channel-type.mqtt.player.scheduler-status.label = Scheduler Status +channel-type.mqtt.player.scheduler-status.description = FPP Scheduler Status +channel-type.mqtt.player.scheduler-current-playlist.label = Scheduler Current Playlist +channel-type.mqtt.player.scheduler-current-playlist.description = FPP Scheduler Current Playlist +channel-type.mqtt.player.scheduler-current-playlist-start.label = Scheduler Current Playlist Start +channel-type.mqtt.player.scheduler-current-playlist-start.description = FPP Scheduler Current Playlist Start Time +channel-type.mqtt.player.scheduler-current-playlist-end.label = Scheduler Current Playlist End +channel-type.mqtt.player.scheduler-current-playlist-end.description = FPP Scheduler Current Playlist End Time +channel-type.mqtt.player.scheduler-current-playlist-stop-type.label = Scheduler Current Playlist Stop Type +channel-type.mqtt.player.scheduler-current-playlist-stop-type.description = FPP Scheduler Current Playlist Stop Type +channel-type.mqtt.player.scheduler-next-playlist.label = Scheduler Next Playlist +channel-type.mqtt.player.scheduler-next-playlist.description = FPP Scheduler Next Playlist +channel-type.mqtt.player.scheduler-next-playlist-start.label = Scheduler Next Playlist Start +channel-type.mqtt.player.scheduler-next-playlist-start.description = FPP Scheduler Next Playlist Start Time diff --git a/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..99076559f5d8f --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.fpp/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,169 @@ + + + + + + + + + FPP Player + + + + + + + + + + + + + + + + + + + + + + + + + + + + unknown + unknown + + + + + + String + + FPP Player Status + + + + String + + FPP Player Mode + + + + String + + FPP Current Sequence + + + + String + + FPP Current Song + + + + String + + FPP Current Playlist + + + + Number:Time + + FPP Seconds Played + + + + Number:Time + + FPP Seconds Remaining + + + + Number:Time + + FPP System Uptime (time after start) + + + + Player + + FPP Player Control + + + Dimmer + + FPP Volume of the Output + + + Switch + + FPP Recieving Bridge Data + + + + Switch + + FPP Multisync Mode Enabled + + + + Switch + + FPP Is In Test Mode + + + String + + FPP Last Run Playlist + + + + String + + FPP Scheduler Status + + + + String + + FPP Scheduler Current Playlist + + + + String + + FPP Scheduler Current Playlist Start Time + + + + String + + FPP Scheduler Current Playlist End Time + + + + String + + FPP Scheduler Current Playlist Stop Type + + + + String + + FPP Scheduler Next Playlist + + + + String + + FPP Scheduler Next Playlist Start Time + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index b3a3286878373..94b766445216a 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -270,6 +270,7 @@ org.openhab.binding.mpd org.openhab.binding.mqtt org.openhab.binding.mqtt.espmilighthub + org.openhab.binding.mqtt.fpp org.openhab.binding.mqtt.generic org.openhab.binding.mqtt.homeassistant org.openhab.binding.mqtt.homie diff --git a/features/openhab-addons/src/main/resources/footer.xml b/features/openhab-addons/src/main/resources/footer.xml index 383959548d486..9b241a74a7056 100644 --- a/features/openhab-addons/src/main/resources/footer.xml +++ b/features/openhab-addons/src/main/resources/footer.xml @@ -28,6 +28,7 @@ mvn:ch.obermuhlner/big-math/2.3.2 mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.espmilighthub/${project.version} + mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.fpp/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.generic/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homeassistant/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homie/${project.version}