Skip to content

Commit e6628cf

Browse files
authored
[jellyfin] initial contribution (openhab#11939)
* [jellyfin] initial contribution Signed-off-by: Miguel Álvarez Díez <[email protected]> * [jellyfin] update parent version Signed-off-by: Miguel Álvarez Díez <[email protected]> * update license header year Signed-off-by: Miguel Álvarez Díez <[email protected]> * add example to readme Signed-off-by: Miguel Álvarez Díez <[email protected]> * apply pr review Signed-off-by: Miguel Álvarez Díez <[email protected]> * apply pr review Signed-off-by: Miguel Álvarez Díez <[email protected]> * apply pr review Signed-off-by: Miguel Álvarez Díez <[email protected]> * add third-party info Signed-off-by: Miguel Álvarez Díez <[email protected]> * upgrade sdk to release 1.2.0 Signed-off-by: Miguel Álvarez Díez <[email protected]>
1 parent ae20f93 commit e6628cf

20 files changed

+2160
-0
lines changed

CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
/bundles/org.openhab.binding.ism8/ @hans-reiner
147147
/bundles/org.openhab.binding.jablotron/ @octa22
148148
/bundles/org.openhab.binding.jeelink/ @vbier
149+
/bundles/org.openhab.binding.jellyfin/ @GiviMAD
149150
/bundles/org.openhab.binding.kaleidescape/ @mlobstein
150151
/bundles/org.openhab.binding.keba/ @kgoderis
151152
/bundles/org.openhab.binding.km200/ @Markinus

bom/openhab-addons/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,11 @@
721721
<artifactId>org.openhab.binding.jeelink</artifactId>
722722
<version>${project.version}</version>
723723
</dependency>
724+
<dependency>
725+
<groupId>org.openhab.addons.bundles</groupId>
726+
<artifactId>org.openhab.binding.jellyfin</artifactId>
727+
<version>${project.version}</version>
728+
</dependency>
724729
<dependency>
725730
<groupId>org.openhab.addons.bundles</groupId>
726731
<artifactId>org.openhab.binding.kaleidescape</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/openhab-addons
14+
15+
== Third-party Content
16+
17+
org.jellyfin.sdk.jellyfin-core-jvm
18+
* License: MIT License
19+
* Project: https://github.com/jellyfin/jellyfin-sdk-kotlin
20+
* Source: https://github.com/jellyfin/jellyfin-sdk-kotlin/jellyfin-core-jvm
21+
22+
org.jellyfin.sdk.jellyfin-api-jvm
23+
* License: MIT License
24+
* Project: https://github.com/jellyfin/jellyfin-sdk-kotlin
25+
* Source: https://github.com/jellyfin/jellyfin-sdk-kotlin/jellyfin-api-jvm
26+
27+
org.jellyfin.sdk.jellyfin-model-jvm
28+
* License: MIT License
29+
* Project: https://github.com/jellyfin/jellyfin-sdk-kotlin
30+
* Source: https://github.com/jellyfin/jellyfin-sdk-kotlin/jellyfin-model-jvm
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Jellyfin Binding
2+
3+
This is the binding for [Jellyfin](https://jellyfin.org) the volunteer-built media solution that puts you in control of your media.
4+
Stream to any device from your own server, with no strings attached.
5+
Your media, your server, your way.
6+
This binding allows connect to Jellyfin clients that supports remote control, it's build on top of the official Jellyfin kotlin sdk.
7+
8+
## Supported Things
9+
10+
This binding was tested against the android tv, and web clients.
11+
The only problem that I found is that the channels play-next-by-terms and play-last-by-terms don't work currently on the android tv client.
12+
13+
Before open an issue please test you are able to correctly control your device from the Jellyfin web ui to identify whetter is an issue on the client itself.
14+
15+
## Discovery
16+
17+
Before you are able to discover clients you should have a bridge to the server so until one is online the discovery will only look for servers on your local network. Once one is online the discovery will detect controllable clients connected to that server.
18+
19+
## Thing Types
20+
21+
| ThingTypeID | description |
22+
|----------|------------------------------|
23+
| server (bridge) | Jellyfin server instance |
24+
| client | Jellyfin controllable client instance |
25+
26+
## Authentication
27+
28+
To allow the server thing to go online you should provide valid credentials for the user that the biding will use to interact with the server api (userId and token configuration properties).
29+
Please note that the user should be allowed on the Jellyfin server to remote control devices.
30+
31+
In order to assist you with this process the binding expose a simple login form you can access on \<local openHAB server url\>/jellyfin/\<server thing id\> for example http://127.0.0.1:8080/jellyfin/2846b8fb60ad444f9ebd085335e3f6bf.
32+
33+
## Server Thing Configuration
34+
35+
| Config | Type | description |
36+
|----------|----------|------------------------------|
37+
| hostname | text | Hostname or IP address of the server (required) |
38+
| port | integer | Port of the server (required) |
39+
| ssl | boolean | Connect through https (required) |
40+
| refreshSeconds | integer | Interval to pull devices state from the server |
41+
| clientActiveWithInSeconds | integer | Amount off seconds allowed since the last client activity to assert it's online (0 disabled) |
42+
| userId | text | The user id |
43+
| token | text | The user access token |
44+
45+
## Channels
46+
47+
| channel | type | description |
48+
|----------|--------|------------------------------|
49+
| send-notification | String | Display message in client |
50+
| media-control | Player | Control media playback |
51+
| playing-item-name | String | Name of the item currently playing (readonly) |
52+
| playing-item-series-name | String | Name of the item's series currently playing, only have value when item is an episode (readonly) |
53+
| playing-item-season-name | String | Name of the item's season currently playing, only have value when item is an episode (readonly) |
54+
| playing-item-season | Number | Number of the item's season currently playing, only have value when item is an episode (readonly) |
55+
| playing-item-episode | Number | Number of the episode item currently playing, only have value when item is an episode (readonly) |
56+
| playing-item-genders | String | Coma separate list genders of the item currently playing (readonly) |
57+
| playing-item-type | String | Type of the item currently playing (readonly) |
58+
| playing-item-percentage | Dimmer | Played percentage for the item currently playing, allow seek |
59+
| playing-item-second | Number | Current second for the item currently playing, allow seek |
60+
| playing-item-total-seconds | Number | Total seconds for the item currently playing (readonly) |
61+
| play-by-terms | String | Play media by terms, works for series, episodes and movies; terms search is explained bellow |
62+
| play-next-by-terms | String | Add to playback queue as next by terms, works for series, episodes and movies; terms search is explained bellow |
63+
| play-last-by-terms | String | Add to playback queue as last by terms, works for series, episodes and movies; terms search is explained bellow |
64+
| browse-by-terms | String | Browse media by terms, works for series, episodes and movies; terms search is explained bellow |
65+
66+
### Terms search:
67+
68+
The terms search has a default behavior that can be modified sending some predefined prefixes.
69+
70+
The default behavior will look for movies, series, or episodes whose name start with the provided text, if it found results the prevalence go as said before.
71+
If the result is a series the binding will try to resume some episode, if not it will look for the next episode to watch and finally will fall back to the first episode.
72+
73+
You can prefix your search with '\<type:movie\>', '\<type:episode\>', '\<type:series\>' to restrict your search to a given type.
74+
75+
Also, you can target a specific series episode by season and episode numbers prefixing your search with '\<season:1\>\<episode:1\>' with the desired values. So '\<season:3\>\<episode:10\>Something' will try to play the episode 10 for the season 3 of the series named 'Something'.
76+
77+
## Full Example
78+
79+
### Example Server (Bridge) - jellyfin.bridge.things
80+
81+
```
82+
Bridge jellyfin:server:exampleServerId "Jellyfin Server" [
83+
clientActiveWithInSeconds=0,
84+
hostname="192.168.1.177",
85+
port=8096,
86+
refreshSeconds=30,
87+
ssl="false"
88+
token=XXXXX # Optional, read bellow
89+
userId=XXXXX # Optional, read bellow
90+
]
91+
```
92+
93+
* token and userId could be retrieved using the login form at http://YOUROPENHABIP:PORT/jellyfin/exampleServerId
94+
95+
### Example Client - jellyfin.clients.things
96+
97+
```
98+
Thing jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID> "Jellyfin Web client" (jellyfin:server:exampleServerId)
99+
Thing jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID> "Jellyfin Android client" (jellyfin:server:exampleServerId)
100+
```
101+
102+
* I recommend creating the clients using the discovery. For getting the device ids manually I recommend to use the Jellyfin web interface with the web inspector and look for the request that is launched when you click the cast button (<jellyfin url>/Sessions?ControllableByUserId=XXXXXXXXXXXX).
103+
104+
### Example Items - jellyfin.items
105+
106+
```
107+
String strJellyfinAndroidSendNotification { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:send-notification " }
108+
Player plJellyfinAndroidMediaControl { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:media-control" }
109+
String strJellyfinAndroidPlayingItemName { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-name" }
110+
String strJellyfinAndroidPlayingItemSeriesName { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-series-name" }
111+
String strJellyfinAndroidPlayingItemSeasonName { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-season-name" }
112+
Number nJellyfinAndroidPlayingItemSeason { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-season" }
113+
Number nJellyfinAndroidPlpayingItemEpisode { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-episode" }
114+
String strJellyfinAndroidPlayingItemGenders { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-genders" }
115+
String strJellyfinAndroidPlayingItemType { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-type" }
116+
Dimmer dJellyfinAndroidPlayingItemPercentage { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-percentage" }
117+
Number nJellyfinAndroidPlayingItemSecond { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-second" }
118+
Number nJellyfinAndroidPlayingItemTotalSeconds { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:playing-item-total-seconds" }
119+
String strJellyfinAndroidPlayByTerms { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:play-by-terms" }
120+
String strJellyfinAndroidPlayByNextTerms { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:play-next-by-terms" }
121+
String strJellyfinAndroidPlayByLastTerms { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:play-last-by-terms" }
122+
String strJellyfinAndroidBrowseByTerms { channel="jellyfin:client:exampleServerId:<JELLYFIN_DEVICE_ID>:browse-by-terms" }
123+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.openhab.addons.bundles</groupId>
9+
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
10+
<version>3.3.0-SNAPSHOT</version>
11+
</parent>
12+
<properties>
13+
<bnd.importpackage>
14+
!android.*,!com.android.*,!kotlin.internal.jdk7,!kotlin.internal.jdk8,!kotlin.reflect.*,!okhttp3.*,!okio.*
15+
</bnd.importpackage>
16+
</properties>
17+
<artifactId>org.openhab.binding.jellyfin</artifactId>
18+
19+
<name>openHAB Add-ons :: Bundles :: Jellyfin Binding</name>
20+
<dependencies>
21+
<dependency>
22+
<groupId>org.jellyfin.sdk</groupId>
23+
<artifactId>jellyfin-core-jvm</artifactId>
24+
<version>1.2.0</version>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.jellyfin.sdk</groupId>
28+
<artifactId>jellyfin-api-jvm</artifactId>
29+
<version>1.2.0</version>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.jellyfin.sdk</groupId>
33+
<artifactId>jellyfin-model-jvm</artifactId>
34+
<version>1.2.0</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>io.ktor</groupId>
38+
<artifactId>ktor-client-core-jvm</artifactId>
39+
<version>1.6.7</version>
40+
<scope>compile</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>io.ktor</groupId>
44+
<artifactId>ktor-client-cio-jvm</artifactId>
45+
<version>1.6.7</version>
46+
<scope>compile</scope>
47+
</dependency>
48+
<dependency>
49+
<groupId>io.ktor</groupId>
50+
<artifactId>ktor-http-jvm</artifactId>
51+
<version>1.6.7</version>
52+
<scope>compile</scope>
53+
</dependency>
54+
<dependency>
55+
<groupId>io.ktor</groupId>
56+
<artifactId>ktor-http-cio-jvm</artifactId>
57+
<version>1.6.7</version>
58+
<scope>compile</scope>
59+
</dependency>
60+
<dependency>
61+
<groupId>io.ktor</groupId>
62+
<artifactId>ktor-network-jvm</artifactId>
63+
<version>1.6.7</version>
64+
<scope>compile</scope>
65+
</dependency>
66+
<dependency>
67+
<groupId>io.ktor</groupId>
68+
<artifactId>ktor-utils-jvm</artifactId>
69+
<version>1.6.7</version>
70+
<scope>compile</scope>
71+
</dependency>
72+
<dependency>
73+
<groupId>io.ktor</groupId>
74+
<artifactId>ktor-io-jvm</artifactId>
75+
<version>1.6.7</version>
76+
<scope>compile</scope>
77+
</dependency>
78+
<dependency>
79+
<groupId>io.ktor</groupId>
80+
<artifactId>ktor-network-tls-jvm</artifactId>
81+
<version>1.6.7</version>
82+
<scope>compile</scope>
83+
</dependency>
84+
<dependency>
85+
<groupId>org.jetbrains.kotlin</groupId>
86+
<artifactId>kotlin-stdlib</artifactId>
87+
<version>1.6.10</version>
88+
<scope>compile</scope>
89+
</dependency>
90+
<dependency>
91+
<groupId>org.jetbrains.kotlinx</groupId>
92+
<artifactId>kotlinx-coroutines-core-jvm</artifactId>
93+
<version>1.5.2</version>
94+
<scope>compile</scope>
95+
</dependency>
96+
<dependency>
97+
<groupId>org.jetbrains.kotlinx</groupId>
98+
<artifactId>kotlinx-coroutines-jdk8</artifactId>
99+
<version>1.5.2</version>
100+
</dependency>
101+
<dependency>
102+
<groupId>org.jetbrains.kotlinx</groupId>
103+
<artifactId>kotlinx-serialization-core-jvm</artifactId>
104+
<version>1.3.1</version>
105+
</dependency>
106+
<dependency>
107+
<groupId>org.jetbrains.kotlinx</groupId>
108+
<artifactId>kotlinx-serialization-json-jvm</artifactId>
109+
<version>1.3.1</version>
110+
</dependency>
111+
<dependency>
112+
<groupId>io.github.microutils</groupId>
113+
<artifactId>kotlin-logging-jvm</artifactId>
114+
<version>2.1.16</version>
115+
</dependency>
116+
</dependencies>
117+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<features name="org.openhab.binding.jellyfin-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
3+
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
4+
5+
<feature name="openhab-binding-jellyfin" description="Jellyfin Binding" version="${project.version}">
6+
<feature>openhab-runtime-base</feature>
7+
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.jellyfin/${project.version}</bundle>
8+
</feature>
9+
</features>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Copyright (c) 2010-2022 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.jellyfin.internal;
14+
15+
import java.util.Set;
16+
17+
import org.eclipse.jdt.annotation.NonNullByDefault;
18+
import org.openhab.core.thing.ThingTypeUID;
19+
20+
/**
21+
* The {@link JellyfinBindingConstants} class defines common constants, which are
22+
* used across the whole binding.
23+
*
24+
* @author Miguel Álvarez - Initial contribution
25+
*/
26+
@NonNullByDefault
27+
public class JellyfinBindingConstants {
28+
29+
static final String BINDING_ID = "jellyfin";
30+
31+
// List of all Thing Type UIDs
32+
public static final ThingTypeUID THING_TYPE_SERVER = new ThingTypeUID(BINDING_ID, "server");
33+
public static final ThingTypeUID THING_TYPE_CLIENT = new ThingTypeUID(BINDING_ID, "client");
34+
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_SERVER, THING_TYPE_CLIENT);
35+
36+
// List of all Channel ids
37+
public static final String SEND_NOTIFICATION_CHANNEL = "send-notification";
38+
public static final String MEDIA_CONTROL_CHANNEL = "media-control";
39+
public static final String PLAYING_ITEM_PERCENTAGE_CHANNEL = "playing-item-percentage";
40+
public static final String PLAYING_ITEM_NAME_CHANNEL = "playing-item-name";
41+
public static final String PLAYING_ITEM_SERIES_NAME_CHANNEL = "playing-item-series-name";
42+
public static final String PLAYING_ITEM_SEASON_NAME_CHANNEL = "playing-item-season-name";
43+
public static final String PLAYING_ITEM_SEASON_CHANNEL = "playing-item-season";
44+
public static final String PLAYING_ITEM_EPISODE_CHANNEL = "playing-item-episode";
45+
public static final String PLAYING_ITEM_GENRES_CHANNEL = "playing-item-genders";
46+
public static final String PLAYING_ITEM_TYPE_CHANNEL = "playing-item-type";
47+
public static final String PLAYING_ITEM_SECOND_CHANNEL = "playing-item-second";
48+
public static final String PLAYING_ITEM_TOTAL_SECOND_CHANNEL = "playing-item-total-seconds";
49+
public static final String PLAY_BY_TERMS_CHANNEL = "play-by-terms";
50+
public static final String PLAY_NEXT_BY_TERMS_CHANNEL = "play-next-by-terms";
51+
public static final String PLAY_LAST_BY_TERMS_CHANNEL = "play-last-by-terms";
52+
public static final String BROWSE_ITEM_BY_TERMS_CHANNEL = "browse-by-terms";
53+
54+
// Discovery
55+
public static final int DISCOVERY_RESULT_TTL_SEC = 600;
56+
}

0 commit comments

Comments
 (0)