[fpp] Initial contribution (#16298)

* working on FPP

Signed-off-by: Scott Hanson <scooter_seh@yahoo.com>
This commit is contained in:
Scott Hanson 2024-09-20 14:59:10 -04:00 committed by GitHub
parent ab2daded4c
commit 163f517f6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1162 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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
}
```

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.mqtt.fpp</artifactId>
<name>openHAB Add-ons :: Bundles :: MQTT FPP</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.mqtt.fpp-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-mqtt-fpp" description="MQTT Binding FPP" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-mqtt</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version}</bundle>
<bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.fpp/${project.version}</bundle>
</feature>
</features>

View File

@ -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 = "";
}

View File

@ -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<ThingTypeUID> 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";
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:mqtt:player">
<parameter name="playerAddress" type="text" required="true">
<context>network-address</context>
<label>Player Address</label>
<description>Player IP Address or Host Name</description>
</parameter>
<parameter name="playerMQTTTopic" type="text" required="true">
<context>name</context>
<label>MQTT Player Topic</label>
<description>MQTT Player Status Topic</description>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -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

View File

@ -0,0 +1,169 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mqtt"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="player">
<supported-bridge-type-refs>
<bridge-type-ref id="broker"/>
</supported-bridge-type-refs>
<label>FPP Player</label>
<description>FPP Player</description>
<channels>
<channel id="player" typeId="player"/>
<channel id="status" typeId="status"/>
<channel id="volume" typeId="volume"/>
<channel id="version" typeId="version"/>
<channel id="mode" typeId="mode"/>
<channel id="current-sequence" typeId="current-sequence"/>
<channel id="current-song" typeId="current-song"/>
<channel id="current-playlist" typeId="current-playlist"/>
<channel id="seconds-played" typeId="seconds-played"/>
<channel id="seconds-remaining" typeId="seconds-remaining"/>
<channel id="uptime" typeId="uptime"/>
<channel id="uuid" typeId="uuid"/>
<channel id="bridging-enabled" typeId="bridging-enabled"/>
<channel id="multisync-enabled" typeId="multisync-enabled"/>
<channel id="testing-enabled" typeId="testing-enabled"/>
<channel id="last-playlist" typeId="last-playlist"/>
<channel id="scheduler-status" typeId="scheduler-status"/>
<channel id="scheduler-current-playlist" typeId="scheduler-current-playlist"/>
<channel id="scheduler-current-playlist-start" typeId="scheduler-current-playlist-start"/>
<channel id="scheduler-current-playlist-end" typeId="scheduler-current-playlist-end"/>
<channel id="scheduler-current-playlist-stop-type" typeId="scheduler-current-playlist-stop-type"/>
<channel id="scheduler-next-playlist" typeId="scheduler-next-playlist"/>
<channel id="scheduler-next-playlist-start" typeId="scheduler-next-playlist-start"/>
</channels>
<properties>
<property name="Software Version">unknown</property>
<property name="uuid">unknown</property>
</properties>
<config-description-ref uri="thing-type:mqtt:player"/>
</thing-type>
<channel-type id="status">
<item-type>String</item-type>
<label>Status</label>
<description>FPP Player Status</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="mode" advanced="true">
<item-type>String</item-type>
<label>Mode</label>
<description>FPP Player Mode</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="current-sequence">
<item-type>String</item-type>
<label>Current Sequence</label>
<description>FPP Current Sequence</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="current-song">
<item-type>String</item-type>
<label>Current Song</label>
<description>FPP Current Song</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="current-playlist">
<item-type>String</item-type>
<label>Current Playlist</label>
<description>FPP Current Playlist</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="seconds-played" advanced="true">
<item-type>Number:Time</item-type>
<label>Seconds Played</label>
<description>FPP Seconds Played</description>
<state readOnly="true" pattern="%.1f sec"/>
</channel-type>
<channel-type id="seconds-remaining" advanced="true">
<item-type>Number:Time</item-type>
<label>Seconds Remaining</label>
<description>FPP Seconds Remaining</description>
<state readOnly="true" pattern="%.1f sec"/>
</channel-type>
<channel-type id="uptime" advanced="true">
<item-type>Number:Time</item-type>
<label>Uptime</label>
<description>FPP System Uptime (time after start)</description>
<state readOnly="true" pattern="%.1f sec"/>
</channel-type>
<channel-type id="player">
<item-type>Player</item-type>
<label>Player</label>
<description>FPP Player Control</description>
</channel-type>
<channel-type id="volume">
<item-type>Dimmer</item-type>
<label>Volume</label>
<description>FPP Volume of the Output</description>
</channel-type>
<channel-type id="bridging-enabled" advanced="true">
<item-type>Switch</item-type>
<label>Bridging</label>
<description>FPP Recieving Bridge Data</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="multisync-enabled" advanced="true">
<item-type>Switch</item-type>
<label>Multisync</label>
<description>FPP Multisync Mode Enabled</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="testing-enabled">
<item-type>Switch</item-type>
<label>Testing</label>
<description>FPP Is In Test Mode</description>
</channel-type>
<channel-type id="last-playlist" advanced="true">
<item-type>String</item-type>
<label>Last Run Playlist</label>
<description>FPP Last Run Playlist</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="scheduler-status" advanced="true">
<item-type>String</item-type>
<label>Scheduler Status</label>
<description>FPP Scheduler Status</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="scheduler-current-playlist">
<item-type>String</item-type>
<label>Scheduler Current Playlist</label>
<description>FPP Scheduler Current Playlist</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="scheduler-current-playlist-start" advanced="true">
<item-type>String</item-type>
<label>Scheduler Current Playlist Start</label>
<description>FPP Scheduler Current Playlist Start Time</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="scheduler-current-playlist-end" advanced="true">
<item-type>String</item-type>
<label>Scheduler Current Playlist End</label>
<description>FPP Scheduler Current Playlist End Time</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="scheduler-current-playlist-stop-type" advanced="true">
<item-type>String</item-type>
<label>Scheduler Current Playlist Stop Type</label>
<description>FPP Scheduler Current Playlist Stop Type</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="scheduler-next-playlist" advanced="true">
<item-type>String</item-type>
<label>Scheduler Next Playlist</label>
<description>FPP Scheduler Next Playlist</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="scheduler-next-playlist-start" advanced="true">
<item-type>String</item-type>
<label>Scheduler Next Playlist Start</label>
<description>FPP Scheduler Next Playlist Start Time</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -270,6 +270,7 @@
<module>org.openhab.binding.mpd</module>
<module>org.openhab.binding.mqtt</module>
<module>org.openhab.binding.mqtt.espmilighthub</module>
<module>org.openhab.binding.mqtt.fpp</module>
<module>org.openhab.binding.mqtt.generic</module>
<module>org.openhab.binding.mqtt.homeassistant</module>
<module>org.openhab.binding.mqtt.homie</module>

View File

@ -28,6 +28,7 @@
<bundle dependency="true">mvn:ch.obermuhlner/big-math/2.3.2</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt/${project.version}</bundle>
<bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.espmilighthub/${project.version}</bundle>
<bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.fpp/${project.version}</bundle>
<bundle start-level="81">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.generic/${project.version}</bundle>
<bundle start-level="82">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homeassistant/${project.version}</bundle>
<bundle start-level="82">mvn:org.openhab.addons.bundles/org.openhab.binding.mqtt.homie/${project.version}</bundle>