added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
38
bundles/org.openhab.binding.kodi/.classpath
Normal file
38
bundles/org.openhab.binding.kodi/.classpath
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.kodi/.project
Normal file
23
bundles/org.openhab.binding.kodi/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.kodi</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.kodi/NOTICE
Normal file
13
bundles/org.openhab.binding.kodi/NOTICE
Normal 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
|
||||
238
bundles/org.openhab.binding.kodi/README.md
Normal file
238
bundles/org.openhab.binding.kodi/README.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Kodi Binding
|
||||
|
||||
[Kodi](https://kodi.tv) (formerly known as XBMC) is an free and open source (GPL) software media center for playing videos, music, pictures, games, and more.
|
||||
Kodi runs on Linux, OS X, BSD, Windows, iOS, and Android.
|
||||
It allows users to play and view most videos, music, podcasts, and other digital media files from local and network storage media and the internet.
|
||||
|
||||
The Kodi Binding integrates Kodi media center support with openHAB, allowing both controlling the player as well as retrieving player status data like the currently played movie title.
|
||||
|
||||
The Kodi binding is the successor to the openHAB 1.x xbmc binding.
|
||||
|
||||
## Preparation
|
||||
|
||||
In order to allow Kodi to be controlled through this binding, you need to enable the Kodi application remote control feature.
|
||||
Please enable "Allow remote control from applications on this/other systems" in the Kodi settings menu under:
|
||||
|
||||
* Settings ➔ Services ➔ Control ➔
|
||||
* Allow remote control from applications on **this** systems
|
||||
* Allow remote control from applications on **other** systems
|
||||
|
||||
To make use of the auto-discovery feature, you additionally need to enable "Allow control of Kodi via UPnP" in the Kodi settings menu.
|
||||
|
||||
* Settings ➔ Services ➔ UPnP / DLNA ➔ Allow remote control via UPnP
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding provides only one thing type: The Kodi media center.
|
||||
Create one Kodi thing per Kodi instance available in your home automation system.
|
||||
|
||||
All Kodi devices are registered as an audio sink in openHAB.
|
||||
|
||||
## Discovery
|
||||
|
||||
The binding supports auto-discovery for available and prepared (see above) instances of the Kodi media center on your local network.
|
||||
Auto-discovery is enabled by default.
|
||||
To disable it, you can add the following line to `<openHAB-conf>/services/runtime.cfg`:
|
||||
|
||||
```
|
||||
discovery.kodi:background=false
|
||||
```
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
The following configuration options are available for the Kodi binding:
|
||||
|
||||
| Parameter | Name | Description | Required |
|
||||
|---------------|--------------|----------------------------------------------------------------------------|----------|
|
||||
| `callbackUrl` | Callback URL | URL to use for playing notification sounds, e.g. `http://192.168.0.2:8080` | no |
|
||||
|
||||
### Thing Configuration
|
||||
|
||||
The Kodi thing requires the IP address of the device hosting your Kodi media center instance, the TCP port to access it (default: `9090`) and the HTTP port to build URLs to the Kodi webinterface for downloading thumbnail and fanart images (default: `8080`).
|
||||
You optionally can define a `httpUser` and a `httpPassword` parameter if the access to your Kodi webinterface is protected.
|
||||
The IP address will be found by the auto-discovery feature.
|
||||
You can use the `notificationVolume` property for setting a default volume (in %) as well as a `notificationTimeout` (in s) to be used to play notifications.
|
||||
|
||||
A manual setup through a `things/kodi.things` file could look like this:
|
||||
|
||||
```
|
||||
Thing kodi:kodi:myKodi "Kodi" @ "Living Room" [ipAddress="192.168.1.100", port=9090, httpPort=8080]
|
||||
```
|
||||
|
||||
## Channels
|
||||
|
||||
The Kodi thing supports the following channels:
|
||||
|
||||
| Channel Type ID | Item Type | Description |
|
||||
|-----------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| mute | Switch | Mute/unmute your playback |
|
||||
| volume | Dimmer | Read or control the volume of your playback |
|
||||
| control | Player | Control the Kodi player, e.g. `PLAY`, `PAUSE`, `NEXT`, `PREVIOUS`, `FASTFORWARD`, `REWIND` |
|
||||
| stop | Switch | Write `ON` to this channel: Stops the Kodi player. If this channel is `ON`, the player is stopped, otherwise Kodi is in another state (see control channel) |
|
||||
| title | String | Title of the currently played song/movie/tv episode |
|
||||
| originaltitle | String | Title of the currently played song/movie/tv episode in local language |
|
||||
| showtitle | String | Title of the currently played tv-show; empty for other types |
|
||||
| album | String | Album name of the currently played song |
|
||||
| artist | String | Artist name of the currently played song or director of the currently played movie |
|
||||
| playuri | String | Plays the file with the provided URI. The URI can be amended by `#timestamp=<s>` where `s` is position in seconds at which the video should be started. |
|
||||
| playfavorite | String | Plays or opens the favorite with the provided title (channel's state options contains available favorites) |
|
||||
| pvr-open-tv | String | Opens the PVR TV channel with the provided name (channel's state options contains available PVR TV channels) |
|
||||
| pvr-open-radio | String | Opens the PVR Radio channel with the provided name (channel's state options contains available PVR Radio channels) |
|
||||
| pvr-channel | String | Title of the currently played PVR channel |
|
||||
| shownotification | String | Shows the provided notification message on the screen |
|
||||
| input | String | Sends a key stroke to Kodi to navigate in the UI. Valid commands are: `Back`, `ContextMenu`, `Down`, `Home`, `Info`, `Left`, `Right`, `Select`, `ShowCodec`, `ShowOSD`, `ShowPlayerProcessInfo` and `Up`. `ExecuteAction` and `SendText` should be used with the dedicated channels `inputaction` and `inputtext`. |
|
||||
| inputtext | String | Sends a generic input (unicode) text to Kodi. |
|
||||
| inputaction | String | Sends a predefined action to Kodi to control the UI and/or perform other tasks. Valid commands are: `left`, `right`, `up`, `down`, `pageup`, `pagedown`, `select`, `highlight`, `parentdir`, `parentfolder`, `back`, `menu`, `previousmenu`, `info`, `pause`, `stop`, `skipnext`, `skipprevious`, `fullscreen`, `aspectratio`, `stepforward`, `stepback`, `bigstepforward`, `bigstepback`, `chapterorbigstepforward`, `chapterorbigstepback`, `osd`, `showsubtitles`, `nextsubtitle`, `cyclesubtitle`, `playerdebug`, `codecinfo`, `playerprocessinfo`, `nextpicture`, `previouspicture`, `zoomout`, `zoomin`, `playlist`, `queue`, `zoomnormal`, `zoomlevel1`, `zoomlevel2`, `zoomlevel3`, `zoomlevel4`, `zoomlevel5`, `zoomlevel6`, `zoomlevel7`, `zoomlevel8`, `zoomlevel9`, `nextcalibration`, `resetcalibration`, `analogmove`, `analogmovex`, `analogmovey`, `rotate`, `rotateccw`, `close`, `subtitledelayminus`, `subtitledelay`, `subtitledelayplus`, `audiodelayminus`, `audiodelay`, `audiodelayplus`, `subtitleshiftup`, `subtitleshiftdown`, `subtitlealign`, `audionextlanguage`, `verticalshiftup`, `verticalshiftdown`, `nextresolution`, `audiotoggledigital`, `number0`, `number1`, `number2`, `number3`, `number4`, `number5`, `number6`, `number7`, `number8`, `number9`, `smallstepback`, `fastforward`, `rewind`, `play`, `playpause`, `switchplayer`, `delete`, `copy`, `move`, `screenshot`, `rename`, `togglewatched`, `scanitem`, `reloadkeymaps`, `volumeup`, `volumedown`, `mute`, `backspace`, `scrollup`, `scrolldown`, `analogfastforward`, `analogrewind`, `moveitemup`, `moveitemdown`, `contextmenu`, `shift`, `symbols`, `cursorleft`, `cursorright`, `showtime`, `analogseekforward`, `analogseekback`, `showpreset`, `nextpreset`, `previouspreset`, `lockpreset`, `randompreset`, `increasevisrating`, `decreasevisrating`, `showvideomenu`, `enter`, `increaserating`, `decreaserating`, `setrating`, `togglefullscreen`, `nextscene`, `previousscene`, `nextletter`, `prevletter`, `jumpsms2`, `jumpsms3`, `jumpsms4`, `jumpsms5`, `jumpsms6`, `jumpsms7`, `jumpsms8`, `jumpsms9`, `filter`, `filterclear`, `filtersms2`, `filtersms3`, `filtersms4`, `filtersms5`, `filtersms6`, `filtersms7`, `filtersms8`, `filtersms9`, `firstpage`, `lastpage`, `guiprofile`, `red`, `green`, `yellow`, `blue`, `increasepar`, `decreasepar`, `volampup`, `volampdown`, `volumeamplification`, `createbookmark`, `createepisodebookmark`, `settingsreset`, `settingslevelchange`, `stereomode`, `nextstereomode`, `previousstereomode`, `togglestereomode`, `stereomodetomono`, `channelup`, `channeldown`, `previouschannelgroup`, `nextchannelgroup`, `playpvr`, `playpvrtv`, `playpvrradio`, `record`, `togglecommskip`, `showtimerrule`, `leftclick`, `rightclick`, `middleclick`, `doubleclick`, `longclick`, `wheelup`, `wheeldown`, `mousedrag`, `mousemove`, `tap`, `longpress`, `pangesture`, `zoomgesture`, `rotategesture`, `swipeleft`, `swiperight`, `swipeup`, `swipedown`, `error`, `noop`. |
|
||||
| systemcommand | String | This channel allows to send system commands to `Shutdown`, `Suspend`, `Hibernate`, `Reboot` or `Quit` Kodi (channel's state options contains available system commands) |
|
||||
| mediatype | String | The media type of the current file. Valid return values are: `unknown`, `channel`, `episode`, `movie`, `musicvideo`, `picture`, `radio`, `song`, `video` |
|
||||
| mediaid | Number | The media_id in database of Kodi |
|
||||
| mediafile | String | Full path and file name of the current file |
|
||||
| uniqueid-imdb | String | IMDB link ID of currently playing media **(Advanced)** |
|
||||
| uniqueid-tmdb | String | TheMovieDB link ID of currently playing media **(Advanced)** |
|
||||
| uniqueid-douban | String | Douban link ID of currently playing media **(Advanced)** |
|
||||
| uniqueid-imdbtvshow | String | IMDB link ID of the season of currently playing tv episode **(Advanced)** |
|
||||
| uniqueid-tmdbtvshow | String | IMDB link ID of the season of currently playing tv episode **(Advanced)** |
|
||||
| uniqueid-tmdbepisode | String | IMDB link ID of single episode of currently playing tv episode **(Advanced)** |
|
||||
| season | Number | Season number if currently playing tv episode |
|
||||
| episode | Number | Episode number if currently playing tv episode |
|
||||
| genreList | String | A comma-separated list of genres of the current file |
|
||||
| thumbnail | Image | The URL to the thumbnail of the current file |
|
||||
| fanart | Image | The URL to the fanart of the current file |
|
||||
| playnotification | String | Plays the notification sound by a given URI |
|
||||
| subtitle-enabled | Switch | Display/hidden subtitle |
|
||||
| subtitle-index | Number | Set or get subtitle index of currently playing media |
|
||||
| subtitle-language | String | Display subtitle language of currently playing media |
|
||||
| subtitle-name | String | Display subtitle title of currently playing media |
|
||||
| audio-index | Number | Audio stream index of currently playing media |
|
||||
| audio-codec | String | Audio codec of currently playing media **(Advanced)** |
|
||||
| audio-language | String | Display language of currently playing audio stream **(Advanced)** |
|
||||
| audio-name | String | Display title of currently playing audio stream **(Advanced)** |
|
||||
| audio-channels | Number | Display channels of currently playing audio stream **(Advanced)** |
|
||||
| video-codec | String | Video codec of currently playing media **(Advanced)** |
|
||||
| video-index | Number | Index of currently playing multi stream video **(Advanced)** |
|
||||
| video-height | Number | Height of currently playing video **(Advanced)** |
|
||||
| video-weight | Number | Width of currently playing video **(Advanced)** |
|
||||
| currenttime | Number:Time | Current time of currently playing media |
|
||||
| currenttimepercentage | Number:Dimensionless | Current time of currently playing media |
|
||||
| duration | Number:Time | Length of currently playing media |
|
||||
| rating | Number | rating of currently playing media **(Advanced)** |
|
||||
| mpaa | String | MPAA rating of currently playing media **(Advanced)** |
|
||||
| userrating | Number | personal rating of currently playing media **(Advanced)** |
|
||||
| profile | String | Current profile **(Advanced)** |
|
||||
|
||||
Kodi things are extensible by channels of type `shownotification`, so that different notification pop-ups can be configured for different use cases.
|
||||
|
||||
|
||||
### Channel Configuration
|
||||
|
||||
**group** The PVR channels can be put into user-defined PVR channel groups.
|
||||
There are two default PVR channel groups.
|
||||
One for PVR TV channels and one for PVR radio channels.
|
||||
The default labels are "All channels" (in German systems "Alle Kanäle").
|
||||
You have to adjust this configuration to use the `pvr-open-tv` and `pvr-open-radio` channels properly.
|
||||
You can optionally configure an user-defined PVR channel group.
|
||||
The PVR channels from Kodi will be populated during runtime into the state options of the `pvr-open-tv` and `pvr-open-radio` channels.
|
||||
|
||||
**shownotification** The `shownotification` channel type has three configuration parameters through which it can be customized:
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-------------|---------|---------|------------------------------------------------------------------------|
|
||||
| title | Text | openHAB | Title of the notification |
|
||||
| displayTime | Integer | 5000 | Time the notification is shown (in ms) |
|
||||
| icon | Text | | Icon to use (e.g. "alarm"). One of the openHAB icons (as listed [here](https://www.openhab.org/docs/configuration/iconsets/classic/)). |
|
||||
|
||||
## Audio Support
|
||||
|
||||
All Kodi instances are registered as an audio sink in the framework.
|
||||
Audio streams are sent to the `playnotification` channel and will change the volume of the audio sink using the `notificationVolume` defined in the properties for the thing, not the `volume`.
|
||||
|
||||
URL audio streams (e.g. an Internet radio stream) are an exception and do not get sent to the `playnotification` channel.
|
||||
Instead, these will be sent to the `playuri` channel.
|
||||
|
||||
## Full Example
|
||||
|
||||
A manual setup through a `things/kodi.things` file could look like this:
|
||||
|
||||
```java
|
||||
Thing kodi:kodi:myKodi "Kodi" @ "Living Room" [ipAddress="192.168.1.100", port=9090] {
|
||||
Channels:
|
||||
Type pvr-open-tv : pvr-open-tv [
|
||||
group="All channels"
|
||||
]
|
||||
Type shownotification : error-notification [
|
||||
title="openHAB - Error",
|
||||
icon="error"
|
||||
]
|
||||
Type shownotification : weather-notification [
|
||||
title="openHAB - Weather",
|
||||
icon="sun_clouds"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Item Configuration
|
||||
|
||||
demo.items
|
||||
|
||||
```java
|
||||
Switch myKodi_mute "Mute" { channel="kodi:kodi:myKodi:mute" }
|
||||
Dimmer myKodi_volume "Volume [%d]" { channel="kodi:kodi:myKodi:volume" }
|
||||
Player myKodi_control "Control" { channel="kodi:kodi:myKodi:control" }
|
||||
Switch myKodi_stop "Stop" { channel="kodi:kodi:myKodi:stop" }
|
||||
String myKodi_title "Title [%s]" { channel="kodi:kodi:myKodi:title" }
|
||||
String myKodi_showtitle "Show title [%s]" { channel="kodi:kodi:myKodi:showtitle" }
|
||||
String myKodi_album "Album [%s]" { channel="kodi:kodi:myKodi:album" }
|
||||
String myKodi_artist "Artist [%s]" { channel="kodi:kodi:myKodi:artist" }
|
||||
String myKodi_playuri "Play URI" { channel="kodi:kodi:myKodi:playuri" }
|
||||
String myKodi_playfavorite "Play favorite" { channel="kodi:kodi:myKodi:playfavorite" }
|
||||
String myKodi_pvropentv "Play PVR TV channel" { channel="kodi:kodi:myKodi:pvr-open-tv" }
|
||||
String myKodi_pvropenradio "Play PVR Radio channel" { channel="kodi:kodi:myKodi:pvr-open-radio" }
|
||||
String myKodi_pvrchannel "PVR channel [%s]" { channel="kodi:kodi:myKodi:pvr-channel" }
|
||||
String myKodi_notification "Notification" { channel="kodi:kodi:myKodi:shownotification" }
|
||||
String myKodi_error-notification "Error Notification" { channel="kodi:kodi:myKodi:error-notification" }
|
||||
String myKodi_weather-notification "Weather Notification" { channel="kodi:kodi:myKodi:weather-notification" }
|
||||
String myKodi_input "Input" { channel="kodi:kodi:myKodi:input" }
|
||||
String myKodi_inputtext "Inputtext" { channel="kodi:kodi:myKodi:inputtext" }
|
||||
String myKodi_systemcommand "Systemcommand" { channel="kodi:kodi:myKodi:systemcommand" }
|
||||
String myKodi_mediatype "Mediatype [%s]" { channel="kodi:kodi:myKodi:mediatype" }
|
||||
String myKodi_genrelist "Genres [%s]" { channel="kodi:kodi:myKodi:genreList" }
|
||||
Image myKodi_thumbnail { channel="kodi:kodi:myKodi:thumbnail" }
|
||||
Image myKodi_fanart { channel="kodi:kodi:myKodi:fanart" }
|
||||
Number:Time myKodi_currenttime "Current Time [%d %unit%]" { channel="kodi:kodi:myKodi:currenttime" }
|
||||
Number:Dimensionless myKodi_ctp "Current Time [%d %unit%]" { channel="kodi:kodi:myKodi:currenttimepercentage" }
|
||||
Number:Time myKodi_duration "Duration [%d %unit%]" { channel="kodi:kodi:myKodi:duration" }
|
||||
String myKodi_playnotification "Play notification URI" { channel="kodi:kodi:myKodi:playnotification" }
|
||||
```
|
||||
|
||||
### Sitemap Configuration
|
||||
|
||||
demo.sitemap
|
||||
|
||||
```perl
|
||||
sitemap demo label="myKodi"
|
||||
{
|
||||
Frame label="myKodi" {
|
||||
Switch item=myKodi_mute
|
||||
Slider item=myKodi_volume
|
||||
Selection item=myKodi_control mappings=[PLAY='Play', PAUSE='Pause', NEXT='Next', PREVIOUS='Previous', FASTFORWARD='Fastforward', REWIND='Rewind']
|
||||
Default item=myKodi_control
|
||||
Switch item=myKodi_stop
|
||||
Text item=myKodi_title
|
||||
Text item=myKodi_showtitle
|
||||
Text item=myKodi_album
|
||||
Text item=myKodi_artist
|
||||
Selection item=myKodi_pvropentv
|
||||
Selection item=myKodi_pvropenchannel
|
||||
Text item=myKodi_pvrchannel
|
||||
Selection item=myKodi_input mappings=[Up='Up', Down='Down', Left='Left', Right='Right', Select='Select', Back='Back', Home='Home', ContextMenu='ContextMenu', Info='Info']
|
||||
Selection item=myKodi_systemcommand
|
||||
Text item=myKodi_mediatype
|
||||
Text item=myKodi_genrelist
|
||||
Image item=myKodi_thumbnail
|
||||
Image item=myKodi_fanart
|
||||
Text item=myKodi_currenttime
|
||||
Text item=myKodi_ctp
|
||||
Text item=myKodi_duration
|
||||
}
|
||||
}
|
||||
```
|
||||
17
bundles/org.openhab.binding.kodi/pom.xml
Normal file
17
bundles/org.openhab.binding.kodi/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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 http://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>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.kodi</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Kodi Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.kodi-${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-kodi" description="Kodi Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-http</feature>
|
||||
<feature>openhab-transport-upnp</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.kodi/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.kodi.internal.handler.KodiHandler;
|
||||
import org.openhab.core.audio.AudioFormat;
|
||||
import org.openhab.core.audio.AudioHTTPServer;
|
||||
import org.openhab.core.audio.AudioSink;
|
||||
import org.openhab.core.audio.AudioStream;
|
||||
import org.openhab.core.audio.FixedLengthAudioStream;
|
||||
import org.openhab.core.audio.URLAudioStream;
|
||||
import org.openhab.core.audio.UnsupportedAudioFormatException;
|
||||
import org.openhab.core.audio.UnsupportedAudioStreamException;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This makes Kodi to serve as an {@link AudioSink}.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution and API
|
||||
* @author Paul Frank - Adapted for Kodi
|
||||
* @author Christoph Weitkamp - Improvements for playing audio notifications
|
||||
*/
|
||||
public class KodiAudioSink implements AudioSink {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(KodiAudioSink.class);
|
||||
|
||||
private static final Set<AudioFormat> SUPPORTED_AUDIO_FORMATS = Collections
|
||||
.unmodifiableSet(Stream.of(AudioFormat.MP3, AudioFormat.WAV).collect(Collectors.toSet()));
|
||||
private static final Set<Class<? extends AudioStream>> SUPPORTED_AUDIO_STREAMS = Collections
|
||||
.unmodifiableSet(Stream.of(FixedLengthAudioStream.class, URLAudioStream.class).collect(Collectors.toSet()));
|
||||
// Needed because Kodi does multiple requests for the stream
|
||||
private static final int STREAM_TIMEOUT = 30;
|
||||
|
||||
private final KodiHandler handler;
|
||||
private final AudioHTTPServer audioHTTPServer;
|
||||
private final String callbackUrl;
|
||||
|
||||
public KodiAudioSink(KodiHandler handler, AudioHTTPServer audioHTTPServer, String callbackUrl) {
|
||||
this.handler = handler;
|
||||
this.audioHTTPServer = audioHTTPServer;
|
||||
this.callbackUrl = callbackUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return handler.getThing().getUID().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(Locale locale) {
|
||||
return handler.getThing().getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(AudioStream audioStream)
|
||||
throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
|
||||
if (audioStream == null) {
|
||||
// in case the audioStream is null, this should be interpreted as a request to end any currently playing
|
||||
// stream.
|
||||
logger.trace("Stop currently playing stream.");
|
||||
handler.stop();
|
||||
} else {
|
||||
AudioFormat format = audioStream.getFormat();
|
||||
if (!AudioFormat.MP3.isCompatible(format) && !AudioFormat.WAV.isCompatible(format)) {
|
||||
throw new UnsupportedAudioFormatException("Currently only MP3 and WAV formats are supported.", format);
|
||||
}
|
||||
|
||||
if (audioStream instanceof URLAudioStream) {
|
||||
// it is an external URL, the speaker can access it itself and play it
|
||||
String url = ((URLAudioStream) audioStream).getURL();
|
||||
logger.trace("Processing audioStream URL {} of format {}.", url, format);
|
||||
handler.playURI(new StringType(url));
|
||||
} else if (audioStream instanceof FixedLengthAudioStream) {
|
||||
if (callbackUrl != null) {
|
||||
// we serve it on our own HTTP server for 30 seconds as Kodi requests the stream several times
|
||||
// Form the URL for streaming the notification from the OH2 web server
|
||||
String url = callbackUrl
|
||||
+ audioHTTPServer.serve((FixedLengthAudioStream) audioStream, STREAM_TIMEOUT);
|
||||
logger.trace("Processing audioStream URL {} of format {}.", url, format);
|
||||
handler.playNotificationSoundURI(new StringType(url));
|
||||
} else {
|
||||
logger.warn("We do not have any callback url, so Kodi cannot play the audio stream!");
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedAudioStreamException(
|
||||
"Kodi can only handle URLAudioStream or FixedLengthAudioStreams.", audioStream.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AudioFormat> getSupportedFormats() {
|
||||
return SUPPORTED_AUDIO_FORMATS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<? extends AudioStream>> getSupportedStreams() {
|
||||
return SUPPORTED_AUDIO_STREAMS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PercentType getVolume() {
|
||||
return handler.getVolume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolume(PercentType volume) {
|
||||
handler.setVolume(volume);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link KodiBinding} class defines common constants, which are used across
|
||||
* the whole binding.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christoph Weitkamp - Added channels for opening PVR TV or Radio streams
|
||||
* @author Andreas Reinhardt & Christoph Weitkamp - Added channels for thumbnail and fanart
|
||||
* @author Christoph Weitkamp - Improvements for playing audio notifications
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KodiBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "kodi";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_KODI = new ThingTypeUID(BINDING_ID, "kodi");
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_KODI);
|
||||
|
||||
// List of thing parameters names
|
||||
public static final String HOST_PARAMETER = "ipAddress";
|
||||
public static final String WS_PORT_PARAMETER = "port";
|
||||
public static final String HTTP_PORT_PARAMETER = "httpPort";
|
||||
public static final String HTTP_USER_PARAMETER = "httpUser";
|
||||
public static final String HTTP_PASSWORD_PARAMETER = "httpPassword";
|
||||
public static final String REFRESH_PARAMETER = "refreshInterval";
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_MUTE = "mute";
|
||||
public static final String CHANNEL_VOLUME = "volume";
|
||||
public static final String CHANNEL_STOP = "stop";
|
||||
public static final String CHANNEL_CONTROL = "control";
|
||||
public static final String CHANNEL_PLAYURI = "playuri";
|
||||
public static final String CHANNEL_PLAYFAVORITE = "playfavorite";
|
||||
public static final String CHANNEL_PVR_OPEN_TV = "pvr-open-tv";
|
||||
public static final String CHANNEL_PVR_OPEN_RADIO = "pvr-open-radio";
|
||||
public static final String CHANNEL_SHOWNOTIFICATION = "shownotification";
|
||||
public static final String CHANNEL_PLAYNOTIFICATION = "playnotification";
|
||||
public static final String CHANNEL_PROFILE = "profile";
|
||||
|
||||
public static final String CHANNEL_INPUT = "input";
|
||||
public static final String CHANNEL_INPUTTEXT = "inputtext";
|
||||
public static final String CHANNEL_INPUTACTION = "inputaction";
|
||||
|
||||
public static final String CHANNEL_SYSTEMCOMMAND = "systemcommand";
|
||||
|
||||
public static final String CHANNEL_ARTIST = "artist";
|
||||
public static final String CHANNEL_TITLE = "title";
|
||||
public static final String CHANNEL_ORIGINALTITLE = "originaltitle";
|
||||
public static final String CHANNEL_SHOWTITLE = "showtitle";
|
||||
public static final String CHANNEL_ALBUM = "album";
|
||||
public static final String CHANNEL_MEDIATYPE = "mediatype";
|
||||
public static final String CHANNEL_MEDIAID = "mediaid";
|
||||
public static final String CHANNEL_MEDIAFILE = "mediafile";
|
||||
public static final String CHANNEL_GENRELIST = "genreList";
|
||||
public static final String CHANNEL_PVR_CHANNEL = "pvr-channel";
|
||||
public static final String CHANNEL_THUMBNAIL = "thumbnail";
|
||||
public static final String CHANNEL_FANART = "fanart";
|
||||
public static final String CHANNEL_AUDIO_CODEC = "audio-codec";
|
||||
public static final String CHANNEL_AUDIO_CHANNELS = "audio-channels";
|
||||
public static final String CHANNEL_AUDIO_INDEX = "audio-index";
|
||||
public static final String CHANNEL_AUDIO_LANGUAGE = "audio-language";
|
||||
public static final String CHANNEL_AUDIO_NAME = "audio-name";
|
||||
public static final String CHANNEL_VIDEO_CODEC = "video-codec";
|
||||
public static final String CHANNEL_VIDEO_INDEX = "video-index";
|
||||
public static final String CHANNEL_VIDEO_HEIGHT = "video-height";
|
||||
public static final String CHANNEL_VIDEO_WIDTH = "video-width";
|
||||
public static final String CHANNEL_SUBTITLE_ENABLED = "subtitle-enabled";
|
||||
public static final String CHANNEL_SUBTITLE_INDEX = "subtitle-index";
|
||||
public static final String CHANNEL_SUBTITLE_LANGUAGE = "subtitle-language";
|
||||
public static final String CHANNEL_SUBTITLE_NAME = "subtitle-name";
|
||||
|
||||
public static final String CHANNEL_CURRENTTIME = "currenttime";
|
||||
public static final String CHANNEL_CURRENTTIMEPERCENTAGE = "currenttimepercentage";
|
||||
public static final String CHANNEL_DURATION = "duration";
|
||||
public static final String CHANNEL_UNIQUEID_IMDB = "uniqueid-imdb";
|
||||
public static final String CHANNEL_UNIQUEID_IMDBTVSHOW = "uniqueid-imdbtvshow";
|
||||
public static final String CHANNEL_UNIQUEID_TMDB = "uniqueid-tmdb";
|
||||
public static final String CHANNEL_UNIQUEID_TMDBTVSHOW = "uniqueid-tmdbtvshow";
|
||||
public static final String CHANNEL_UNIQUEID_TMDBEPISODE = "uniqueid-tmdbepisode";
|
||||
public static final String CHANNEL_UNIQUEID_DOUBAN = "uniqueid-douban";
|
||||
public static final String CHANNEL_MPAA = "mpaa";
|
||||
public static final String CHANNEL_RATING = "rating";
|
||||
public static final String CHANNEL_USERRATING = "userrating";
|
||||
|
||||
public static final String CHANNEL_SEASON = "season";
|
||||
public static final String CHANNEL_EPISODE = "episode";
|
||||
|
||||
public static final String CHANNEL_TYPE_SHOWNOTIFICATION = "shownotification";
|
||||
public static final String CHANNEL_TYPE_SHOWNOTIFICATION_PARAM_TITLE = "title";
|
||||
public static final String CHANNEL_TYPE_SHOWNOTIFICATION_PARAM_DISPLAYTIME = "displayTime";
|
||||
public static final String CHANNEL_TYPE_SHOWNOTIFICATION_PARAM_ICON = "icon";
|
||||
|
||||
// Module Properties
|
||||
public static final String PROPERTY_VERSION = "version";
|
||||
|
||||
// Used for Discovery service
|
||||
public static final String MANUFACTURER = "XBMC Foundation";
|
||||
public static final String UPNP_DEVICE_TYPE = "MediaRenderer";
|
||||
|
||||
public static final String PVR_TV = "tv";
|
||||
public static final String PVR_RADIO = "radio";
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.binding.BaseDynamicCommandDescriptionProvider;
|
||||
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||
import org.openhab.core.thing.type.DynamicCommandDescriptionProvider;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* Dynamic provider of command options.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@Component(service = { DynamicCommandDescriptionProvider.class, KodiDynamicCommandDescriptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class KodiDynamicCommandDescriptionProvider extends BaseDynamicCommandDescriptionProvider {
|
||||
@Activate
|
||||
public KodiDynamicCommandDescriptionProvider(
|
||||
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
||||
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* Dynamic provider of state options while leaving other state description fields as original.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@Component(service = { DynamicStateDescriptionProvider.class, KodiDynamicStateDescriptionProvider.class })
|
||||
@NonNullByDefault
|
||||
public class KodiDynamicStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
|
||||
@Activate
|
||||
public KodiDynamicStateDescriptionProvider(
|
||||
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal;
|
||||
|
||||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.kodi.internal.model.KodiAudioStream;
|
||||
import org.openhab.binding.kodi.internal.model.KodiSubtitle;
|
||||
import org.openhab.binding.kodi.internal.model.KodiSystemProperties;
|
||||
import org.openhab.binding.kodi.internal.protocol.KodiConnection;
|
||||
import org.openhab.core.library.types.RawType;
|
||||
|
||||
/**
|
||||
* Interface which has to be implemented by a class in order to get status
|
||||
* updates from a {@link KodiConnection}
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christoph Weitkamp - Added channels for opening PVR TV or Radio streams
|
||||
* @author Christoph Weitkamp - Improvements for playing audio notifications
|
||||
*/
|
||||
public interface KodiEventListener extends EventListener {
|
||||
public enum KodiState {
|
||||
PLAY,
|
||||
PAUSE,
|
||||
END,
|
||||
STOP,
|
||||
REWIND,
|
||||
FASTFORWARD
|
||||
}
|
||||
|
||||
public enum KodiPlaylistState {
|
||||
ADD,
|
||||
ADDED,
|
||||
INSERT,
|
||||
REMOVE,
|
||||
REMOVED,
|
||||
CLEAR
|
||||
}
|
||||
|
||||
void updateConnectionState(boolean connected);
|
||||
|
||||
void updateScreenSaverState(boolean screenSaveActive);
|
||||
|
||||
void updatePlaylistState(KodiPlaylistState playlistState);
|
||||
|
||||
void updateVolume(int volume);
|
||||
|
||||
void updatePlayerState(KodiState state);
|
||||
|
||||
void updateMuted(boolean muted);
|
||||
|
||||
void updateMediaID(int mediaid);
|
||||
|
||||
void updateUniqueIDDouban(String uniqueid);
|
||||
|
||||
void updateUniqueIDImdb(String uniqueid);
|
||||
|
||||
void updateUniqueIDTmdb(String uniqueid);
|
||||
|
||||
void updateUniqueIDImdbtvshow(String uniqueid);
|
||||
|
||||
void updateUniqueIDTmdbtvshow(String uniqueid);
|
||||
|
||||
void updateUniqueIDTmdbepisode(String uniqueid);
|
||||
|
||||
void updateTitle(String title);
|
||||
|
||||
void updateOriginalTitle(String originaltitle);
|
||||
|
||||
void updateShowTitle(String title);
|
||||
|
||||
void updateAlbum(String album);
|
||||
|
||||
void updateArtistList(List<String> artistList);
|
||||
|
||||
void updateMediaType(String mediaType);
|
||||
|
||||
void updateGenreList(List<String> genreList);
|
||||
|
||||
void updatePVRChannel(String channel);
|
||||
|
||||
void updateThumbnail(@Nullable RawType thumbnail);
|
||||
|
||||
void updateFanart(@Nullable RawType fanart);
|
||||
|
||||
void updateAudioStreamOptions(List<KodiAudioStream> audioStreamList);
|
||||
|
||||
void updateAudioCodec(String codec);
|
||||
|
||||
void updateAudioName(String name);
|
||||
|
||||
void updateAudioIndex(int index);
|
||||
|
||||
void updateAudioChannels(int channels);
|
||||
|
||||
void updateAudioLanguage(String language);
|
||||
|
||||
void updateVideoCodec(String codec);
|
||||
|
||||
void updateVideoIndex(int index);
|
||||
|
||||
void updateVideoWidth(int width);
|
||||
|
||||
void updateVideoHeight(int height);
|
||||
|
||||
void updateSubtitleOptions(List<KodiSubtitle> subtitleList);
|
||||
|
||||
void updateSubtitleEnabled(boolean enabled);
|
||||
|
||||
void updateSubtitleIndex(int index);
|
||||
|
||||
void updateSubtitleName(String name);
|
||||
|
||||
void updateSubtitleLanguage(String language);
|
||||
|
||||
void updateCurrentTime(long currentTime);
|
||||
|
||||
void updateCurrentTimePercentage(double currentTimePercentage);
|
||||
|
||||
void updateDuration(long duration);
|
||||
|
||||
void updateSystemProperties(@Nullable KodiSystemProperties systemProperties);
|
||||
|
||||
void updateEpisode(int episode);
|
||||
|
||||
void updateSeason(int season);
|
||||
|
||||
void updateMediaFile(String mediafile);
|
||||
|
||||
void updateRating(double rating);
|
||||
|
||||
void updateUserRating(double rating);
|
||||
|
||||
void updateMpaa(String mpaa);
|
||||
|
||||
void updateCurrentProfile(String profile);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal;
|
||||
|
||||
import static org.openhab.binding.kodi.internal.KodiBindingConstants.*;
|
||||
|
||||
import java.util.Dictionary;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.openhab.binding.kodi.internal.handler.KodiHandler;
|
||||
import org.openhab.core.audio.AudioHTTPServer;
|
||||
import org.openhab.core.audio.AudioSink;
|
||||
import org.openhab.core.io.net.http.WebSocketFactory;
|
||||
import org.openhab.core.net.HttpServiceUtil;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
import org.openhab.core.thing.Thing;
|
||||
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.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link KodiHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christoph Weitkamp - Improvements on channels for opening PVR TV or Radio streams
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.kodi")
|
||||
public class KodiHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(KodiHandlerFactory.class);
|
||||
|
||||
private final AudioHTTPServer audioHTTPServer;
|
||||
private final NetworkAddressService networkAddressService;
|
||||
private final KodiDynamicCommandDescriptionProvider commandDescriptionProvider;
|
||||
private final KodiDynamicStateDescriptionProvider stateDescriptionProvider;
|
||||
private final WebSocketClient webSocketClient;
|
||||
|
||||
private final Map<String, @Nullable ServiceRegistration<AudioSink>> audioSinkRegistrations = new ConcurrentHashMap<>();
|
||||
|
||||
// url (scheme+server+port) to use for playing notification sounds
|
||||
private @Nullable String callbackUrl;
|
||||
|
||||
@Activate
|
||||
public KodiHandlerFactory(final @Reference AudioHTTPServer audioHTTPServer,
|
||||
final @Reference NetworkAddressService networkAddressService,
|
||||
final @Reference KodiDynamicCommandDescriptionProvider commandDescriptionProvider,
|
||||
final @Reference KodiDynamicStateDescriptionProvider stateDescriptionProvider,
|
||||
final @Reference WebSocketFactory webSocketFactory) {
|
||||
this.audioHTTPServer = audioHTTPServer;
|
||||
this.networkAddressService = networkAddressService;
|
||||
this.commandDescriptionProvider = commandDescriptionProvider;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.webSocketClient = webSocketFactory.getCommonWebSocketClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate(ComponentContext componentContext) {
|
||||
super.activate(componentContext);
|
||||
Dictionary<String, Object> properties = componentContext.getProperties();
|
||||
callbackUrl = (String) properties.get("callbackUrl");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_KODI)) {
|
||||
String callbackUrl = createCallbackUrl();
|
||||
KodiHandler handler = new KodiHandler(thing, commandDescriptionProvider, stateDescriptionProvider,
|
||||
webSocketClient, callbackUrl);
|
||||
|
||||
// register the Kodi as an audio sink
|
||||
KodiAudioSink audioSink = new KodiAudioSink(handler, audioHTTPServer, callbackUrl);
|
||||
@SuppressWarnings("unchecked")
|
||||
ServiceRegistration<AudioSink> reg = (ServiceRegistration<AudioSink>) bundleContext
|
||||
.registerService(AudioSink.class.getName(), audioSink, new Hashtable<>());
|
||||
audioSinkRegistrations.put(thing.getUID().toString(), reg);
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private @Nullable String createCallbackUrl() {
|
||||
if (callbackUrl != null) {
|
||||
return callbackUrl;
|
||||
} else {
|
||||
final String ipAddress = networkAddressService.getPrimaryIpv4HostAddress();
|
||||
if (ipAddress == null) {
|
||||
logger.warn("No network interface could be found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// we do not use SSL as it can cause certificate validation issues.
|
||||
final int port = HttpServiceUtil.getHttpServicePort(bundleContext);
|
||||
if (port == -1) {
|
||||
logger.warn("Cannot find port of the http service.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return "http://" + ipAddress + ":" + port;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterHandler(Thing thing) {
|
||||
super.unregisterHandler(thing);
|
||||
ServiceRegistration<AudioSink> reg = audioSinkRegistrations.get(thing.getUID().toString());
|
||||
if (reg != null) {
|
||||
reg.unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal;
|
||||
|
||||
import org.openhab.binding.kodi.internal.KodiEventListener.KodiState;
|
||||
|
||||
/**
|
||||
* The {@link KodiPlayerState} is responsible for saving the state of a player.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
public class KodiPlayerState {
|
||||
private int savedPlaylistID;
|
||||
private int savedVolume;
|
||||
private KodiState savedState;
|
||||
|
||||
public int getSavedPlaylistID() {
|
||||
return savedPlaylistID;
|
||||
}
|
||||
|
||||
public void setPlaylistID(int savedPlaylistID) {
|
||||
this.savedPlaylistID = savedPlaylistID;
|
||||
}
|
||||
|
||||
public int getSavedVolume() {
|
||||
return savedVolume;
|
||||
}
|
||||
|
||||
public void setSavedVolume(int savedVolume) {
|
||||
this.savedVolume = savedVolume;
|
||||
}
|
||||
|
||||
public KodiState getSavedState() {
|
||||
return savedState;
|
||||
}
|
||||
|
||||
public void setSavedState(KodiState savedState) {
|
||||
this.savedState = savedState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.config;
|
||||
|
||||
/**
|
||||
* Channel configuration from openHAB.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
public class KodiChannelConfig {
|
||||
private String group;
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(final String group) {
|
||||
this.group = group;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.config;
|
||||
|
||||
/**
|
||||
* Thing configuration from openHAB.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
* @author Christoph Weitkamp - Improvements for playing audio notifications
|
||||
*/
|
||||
public class KodiConfig {
|
||||
private String ipAddress;
|
||||
private Integer port;
|
||||
private Integer httpPort;
|
||||
private String httpUser;
|
||||
private String httpPassword;
|
||||
private Integer refreshInterval;
|
||||
private Integer notificationTimeout;
|
||||
private Integer notificationVolume;
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public Integer getHttpPort() {
|
||||
return httpPort;
|
||||
}
|
||||
|
||||
public void setHttpPort(Integer httpPort) {
|
||||
this.httpPort = httpPort;
|
||||
}
|
||||
|
||||
public String getHttpUser() {
|
||||
return httpUser;
|
||||
}
|
||||
|
||||
public void setHttpUser(String httpUser) {
|
||||
this.httpUser = httpUser;
|
||||
}
|
||||
|
||||
public String getHttpPassword() {
|
||||
return httpPassword;
|
||||
}
|
||||
|
||||
public void setHttpPassword(String httpPassword) {
|
||||
this.httpPassword = httpPassword;
|
||||
}
|
||||
|
||||
public Integer getRefreshInterval() {
|
||||
return refreshInterval;
|
||||
}
|
||||
|
||||
public void setRefreshInterval(Integer refreshInterval) {
|
||||
this.refreshInterval = refreshInterval;
|
||||
}
|
||||
|
||||
public Integer getNotificationTimeout() {
|
||||
return notificationTimeout;
|
||||
}
|
||||
|
||||
public void setNotificationTimeout(Integer notificationTimeout) {
|
||||
this.notificationTimeout = notificationTimeout;
|
||||
}
|
||||
|
||||
public Integer getNotificationVolume() {
|
||||
return notificationVolume;
|
||||
}
|
||||
|
||||
public void setNotificationVolume(Integer notificationVolume) {
|
||||
this.notificationVolume = notificationVolume;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.kodi.internal.KodiBindingConstants.*;
|
||||
|
||||
import java.util.Dictionary;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.jupnp.model.meta.RemoteDevice;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An UpnpDiscoveryParticipant which allows to discover Kodi AVRs.
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
* @author Christoph Weitkamp - Use "discovery.kodi:background=false" to disable discovery service
|
||||
*/
|
||||
@Component(immediate = true, configurationPid = "discovery.kodi")
|
||||
@NonNullByDefault
|
||||
public class KodiUpnpDiscoveryParticipant implements UpnpDiscoveryParticipant {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(KodiUpnpDiscoveryParticipant.class);
|
||||
|
||||
private boolean isAutoDiscoveryEnabled = true;
|
||||
|
||||
@Activate
|
||||
protected void activate(ComponentContext componentContext) {
|
||||
activateOrModifyService(componentContext);
|
||||
}
|
||||
|
||||
@Modified
|
||||
protected void modified(ComponentContext componentContext) {
|
||||
activateOrModifyService(componentContext);
|
||||
}
|
||||
|
||||
private void activateOrModifyService(ComponentContext componentContext) {
|
||||
Dictionary<String, @Nullable Object> properties = componentContext.getProperties();
|
||||
String autoDiscoveryPropertyValue = (String) properties.get("background");
|
||||
if (autoDiscoveryPropertyValue != null && !autoDiscoveryPropertyValue.isEmpty()) {
|
||||
isAutoDiscoveryEnabled = Boolean.valueOf(autoDiscoveryPropertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return SUPPORTED_THING_TYPES_UIDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DiscoveryResult createResult(RemoteDevice device) {
|
||||
if (isAutoDiscoveryEnabled) {
|
||||
ThingUID thingUid = getThingUID(device);
|
||||
if (thingUid != null) {
|
||||
String friendlyName = device.getDetails().getFriendlyName();
|
||||
String label = friendlyName == null || friendlyName.isEmpty() ? device.getDisplayString()
|
||||
: friendlyName;
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(HOST_PARAMETER, device.getIdentity().getDescriptorURL().getHost());
|
||||
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(thingUid).withLabel(label)
|
||||
.withProperties(properties).withRepresentationProperty(HOST_PARAMETER).build();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingUID getThingUID(RemoteDevice device) {
|
||||
String manufacturer = device.getDetails().getManufacturerDetails().getManufacturer();
|
||||
if (containsIgnoreCase(manufacturer, MANUFACTURER)) {
|
||||
logger.debug("Manufacturer matched: search: {}, device value: {}.", MANUFACTURER, manufacturer);
|
||||
String type = device.getType().getType();
|
||||
if (containsIgnoreCase(type, UPNP_DEVICE_TYPE)) {
|
||||
logger.debug("Device type matched: search: {}, device value: {}.", UPNP_DEVICE_TYPE, type);
|
||||
return new ThingUID(THING_TYPE_KODI, device.getIdentity().getUdn().getIdentifierString());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean containsIgnoreCase(final @Nullable String str, final String searchStr) {
|
||||
return str != null && str.toLowerCase().contains(searchStr.toLowerCase());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi audio stream (see https://kodi.wiki/view/JSON-RPC_API/v9#Player.Audio.Stream)
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KodiAudioStream {
|
||||
private int bitrate;
|
||||
private int channels;
|
||||
private @NonNullByDefault({}) String codec;
|
||||
private int index;
|
||||
private @NonNullByDefault({}) String language;
|
||||
private @NonNullByDefault({}) String name;
|
||||
|
||||
public int getBitrate() {
|
||||
return bitrate;
|
||||
}
|
||||
|
||||
public void setBitrate(int bitrate) {
|
||||
this.bitrate = bitrate;
|
||||
}
|
||||
|
||||
public int getChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
public void setChannels(int channels) {
|
||||
this.channels = channels;
|
||||
}
|
||||
|
||||
public String getCodec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
public void setCodec(String codec) {
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi base item
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
public abstract class KodiBaseItem {
|
||||
/**
|
||||
* The label of the item
|
||||
*/
|
||||
private String label;
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(final String label) {
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi duration (https://kodi.wiki/view/JSON-RPC_API/v9#Global.Time)
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KodiDuration {
|
||||
/**
|
||||
* The hours of the duration
|
||||
*/
|
||||
private long hours;
|
||||
/**
|
||||
* The minutes of the duration
|
||||
*/
|
||||
private long minutes;
|
||||
/**
|
||||
* The seconds of the duration
|
||||
*/
|
||||
private long seconds;
|
||||
/**
|
||||
* The milliseconds of the duration
|
||||
*/
|
||||
private long milliseconds;
|
||||
|
||||
public long getHours() {
|
||||
return hours;
|
||||
}
|
||||
|
||||
public void setHours(long hours) {
|
||||
this.hours = hours;
|
||||
}
|
||||
|
||||
public long getMinutes() {
|
||||
return minutes;
|
||||
}
|
||||
|
||||
public void setMinutes(long minutes) {
|
||||
this.minutes = minutes;
|
||||
}
|
||||
|
||||
public long getSeconds() {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
public void setSeconds(long seconds) {
|
||||
this.seconds = seconds;
|
||||
}
|
||||
|
||||
public long getMilliseconds() {
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
public void setMilliseconds(long milliseconds) {
|
||||
this.milliseconds = milliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this KodiDuration to the total length in seconds.
|
||||
*
|
||||
* @return the total length of the duration in seconds
|
||||
*/
|
||||
public long toSeconds() {
|
||||
return TimeUnit.MILLISECONDS.toSeconds(toMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this KodiDuration to the total length in milliseconds.
|
||||
*
|
||||
* @return the total length of the duration in milliseconds
|
||||
*/
|
||||
public long toMillis() {
|
||||
return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds).plusMillis(milliseconds).toMillis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi favorite.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KodiFavorite {
|
||||
// handle titles which are wrapped e.g. [COLOR FFE95E01]Title[/COLOR]
|
||||
private static final Pattern TITLE_PATTERN = Pattern.compile("(\\[COLOR\\s\\w{8}\\])|(\\[/COLOR\\])");
|
||||
|
||||
/**
|
||||
* The title of the favorite
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* The type of the favorite
|
||||
*/
|
||||
private String favoriteType = "unknown";
|
||||
|
||||
/**
|
||||
* The path of the favorite
|
||||
*/
|
||||
@Nullable
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* The window of the favorite
|
||||
*/
|
||||
@Nullable
|
||||
private String window;
|
||||
|
||||
/**
|
||||
* The parameters of the favorites window
|
||||
*/
|
||||
@Nullable
|
||||
private String windowParameter;
|
||||
|
||||
/**
|
||||
* Constructs a favorite with the given title.
|
||||
*
|
||||
* @param title title of the favorite
|
||||
*/
|
||||
public KodiFavorite(final String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the favorite.
|
||||
*
|
||||
* @return the title of the favorite
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of the favorite.
|
||||
*
|
||||
* @param title title of the favorite
|
||||
*/
|
||||
public void setTitle(final String title) {
|
||||
Matcher m = TITLE_PATTERN.matcher(title);
|
||||
this.title = m.replaceAll("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the favorite.
|
||||
*
|
||||
* @return the type of the favorite
|
||||
*/
|
||||
public String getFavoriteType() {
|
||||
return favoriteType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the favorite.
|
||||
*
|
||||
* @param favoriteType type of the favorite. Valid values are: "media", "window", "script" or "unknown"
|
||||
*/
|
||||
public void setFavoriteType(final String favoriteType) {
|
||||
this.favoriteType = favoriteType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the favorite.
|
||||
*
|
||||
* @return the path of the favorite
|
||||
*/
|
||||
@Nullable
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path of the favorite.
|
||||
*
|
||||
* @param path path of the favorite
|
||||
*/
|
||||
public void setPath(final String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window of the favorite.
|
||||
*
|
||||
* @return the window of the favorite
|
||||
*/
|
||||
@Nullable
|
||||
public String getWindow() {
|
||||
return window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the window of the favorite.
|
||||
*
|
||||
* @param window the window of the favorite
|
||||
*/
|
||||
public void setWindow(final String window) {
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameters of the favorites window.
|
||||
*
|
||||
* @return the parameters of the favorites window
|
||||
*/
|
||||
@Nullable
|
||||
public String getWindowParameter() {
|
||||
return windowParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parameters of the favorites window.
|
||||
*
|
||||
* @param windowParameter the parameters of the favorites window
|
||||
*/
|
||||
public void setWindowParameter(final String windowParameter) {
|
||||
this.windowParameter = windowParameter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi PVR channel
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
public class KodiPVRChannel extends KodiBaseItem {
|
||||
/**
|
||||
* The PVR channel id
|
||||
*/
|
||||
private int channelId;
|
||||
/**
|
||||
* The PVR channel group id
|
||||
*/
|
||||
private int channelGroupId;
|
||||
|
||||
public int getId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public void setId(int channelId) {
|
||||
this.channelId = channelId;
|
||||
}
|
||||
|
||||
public int getChannelGroupId() {
|
||||
return channelGroupId;
|
||||
}
|
||||
|
||||
public void setChannelGroupId(int channelGroupId) {
|
||||
this.channelGroupId = channelGroupId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi PVR channel group
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
public class KodiPVRChannelGroup extends KodiBaseItem {
|
||||
/**
|
||||
* The PVR channel group id
|
||||
*/
|
||||
private int channelGroupId;
|
||||
|
||||
/**
|
||||
* The PVR channel type
|
||||
*/
|
||||
private String channelType;
|
||||
|
||||
public int getId() {
|
||||
return channelGroupId;
|
||||
}
|
||||
|
||||
public void setId(final int channelGroupId) {
|
||||
this.channelGroupId = channelGroupId;
|
||||
}
|
||||
|
||||
public String getChannelType() {
|
||||
return channelType;
|
||||
}
|
||||
|
||||
public void setChannelType(final String channelType) {
|
||||
this.channelType = channelType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi profile (see https://kodi.wiki/view/JSON-RPC_API/v8#Profiles.GetProfiles)
|
||||
*
|
||||
* @author Jan Hendriks - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KodiProfile extends KodiBaseItem {
|
||||
private int lockmode;
|
||||
private String thumbnail = "";
|
||||
|
||||
public int getLockmode() {
|
||||
return lockmode;
|
||||
}
|
||||
|
||||
public void setLockmode(int lockmode) {
|
||||
this.lockmode = lockmode;
|
||||
}
|
||||
|
||||
public String getThumbnail() {
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
public void setThumbnail(String thumbnail) {
|
||||
this.thumbnail = thumbnail;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi subtitle stream (see https://kodi.wiki/view/JSON-RPC_API/v9#Player.Subtitle)
|
||||
*
|
||||
* @author Meng Yiqi - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KodiSubtitle {
|
||||
private int index;
|
||||
private String language = "";
|
||||
private String name = "";
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Class representing Kodi system properties (https://kodi.wiki/view/JSON-RPC_API/v9#System.Property.Value)
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KodiSystemProperties {
|
||||
@SerializedName("canhibernate")
|
||||
private boolean canHibernate;
|
||||
@SerializedName("canreboot")
|
||||
private boolean canReboot;
|
||||
@SerializedName("cansuspend")
|
||||
private boolean canSuspend;
|
||||
@SerializedName("canshutdown")
|
||||
private boolean canShutdown;
|
||||
|
||||
public boolean canHibernate() {
|
||||
return canHibernate;
|
||||
}
|
||||
|
||||
public void setCanHibernate(boolean canHibernate) {
|
||||
this.canHibernate = canHibernate;
|
||||
}
|
||||
|
||||
public boolean canReboot() {
|
||||
return canReboot;
|
||||
}
|
||||
|
||||
public void setCanReboot(boolean canReboot) {
|
||||
this.canReboot = canReboot;
|
||||
}
|
||||
|
||||
public boolean canSuspend() {
|
||||
return canSuspend;
|
||||
}
|
||||
|
||||
public void setCansuspend(boolean canSuspend) {
|
||||
this.canSuspend = canSuspend;
|
||||
}
|
||||
|
||||
public boolean canShutdown() {
|
||||
return canShutdown;
|
||||
}
|
||||
|
||||
public void setCanShutdown(boolean canShutdown) {
|
||||
this.canShutdown = canShutdown;
|
||||
}
|
||||
|
||||
public boolean canQuit() {
|
||||
return !canHibernate && !canReboot && !canShutdown && !canSuspend;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi UniqueID
|
||||
*
|
||||
* @author Meng Yiqi - Initial contribution
|
||||
*/
|
||||
public class KodiUniqueID {
|
||||
private String imdb;
|
||||
private String tmdb;
|
||||
private String imdbtvshow;
|
||||
private String tmdbtvshow;
|
||||
private String tmdbepisode;
|
||||
private String douban;
|
||||
|
||||
public String getImdb() {
|
||||
return imdb;
|
||||
}
|
||||
|
||||
public void setImdb(String imdb) {
|
||||
this.imdb = imdb;
|
||||
}
|
||||
|
||||
public String getTmdb() {
|
||||
return tmdb;
|
||||
}
|
||||
|
||||
public void setTmdb(String tmdb) {
|
||||
this.tmdb = tmdb;
|
||||
}
|
||||
|
||||
public String getImdbtvshow() {
|
||||
return imdbtvshow;
|
||||
}
|
||||
|
||||
public void setImdbtvshow(String imdbtvshow) {
|
||||
this.imdbtvshow = imdbtvshow;
|
||||
}
|
||||
|
||||
public String getTmdbtvshow() {
|
||||
return tmdbtvshow;
|
||||
}
|
||||
|
||||
public void setTmdbtvshow(String tmdbtvshow) {
|
||||
this.tmdbtvshow = tmdbtvshow;
|
||||
}
|
||||
|
||||
public String getTmdbepisode() {
|
||||
return tmdbepisode;
|
||||
}
|
||||
|
||||
public void setTmdbepisode(String tmdbepisode) {
|
||||
this.tmdbtvshow = tmdbepisode;
|
||||
}
|
||||
|
||||
public String getDouban() {
|
||||
return douban;
|
||||
}
|
||||
|
||||
public void setDouban(String douban) {
|
||||
this.douban = douban;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Class representing a Kodi video stream (see https://kodi.wiki/view/JSON-RPC_API/v9#Player.Video.Stream)
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KodiVideoStream {
|
||||
private @NonNullByDefault({}) String codec;
|
||||
private int height;
|
||||
private int index;
|
||||
private @NonNullByDefault({}) String language;
|
||||
private @NonNullByDefault({}) String name;
|
||||
private int width;
|
||||
|
||||
public String getCodec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
public void setCodec(String codec) {
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* KodiClientSocket implements the low level communication to Kodi through
|
||||
* websocket. Usually this communication is done through port 9090
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
*/
|
||||
public class KodiClientSocket {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(KodiClientSocket.class);
|
||||
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private static final int REQUEST_TIMEOUT_MS = 60000;
|
||||
|
||||
private CountDownLatch commandLatch = null;
|
||||
private JsonObject commandResponse = null;
|
||||
private int nextMessageId = 1;
|
||||
|
||||
private boolean connected = false;
|
||||
|
||||
private final JsonParser parser = new JsonParser();
|
||||
private final Gson mapper = new Gson();
|
||||
private final URI uri;
|
||||
private final WebSocketClient client;
|
||||
private Session session;
|
||||
private Future<?> sessionFuture;
|
||||
|
||||
private final KodiClientSocketEventListener eventHandler;
|
||||
|
||||
public KodiClientSocket(KodiClientSocketEventListener eventHandler, URI uri, ScheduledExecutorService scheduler,
|
||||
WebSocketClient webSocketClient) {
|
||||
this.eventHandler = eventHandler;
|
||||
this.uri = uri;
|
||||
this.scheduler = scheduler;
|
||||
this.client = webSocketClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a connection to the Kodi host and begin listening for updates over the async http web socket
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized void open() throws IOException {
|
||||
if (isConnected()) {
|
||||
logger.warn("open: connection is already open");
|
||||
}
|
||||
KodiWebSocketListener socket = new KodiWebSocketListener();
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
|
||||
sessionFuture = client.connect(socket, uri, request);
|
||||
}
|
||||
|
||||
/***
|
||||
* Close this connection to the Kodi instance
|
||||
*/
|
||||
public void close() {
|
||||
// if there is an old web socket then clean up and destroy
|
||||
if (session != null) {
|
||||
session.close();
|
||||
session = null;
|
||||
}
|
||||
|
||||
if (sessionFuture != null && !sessionFuture.isDone()) {
|
||||
sessionFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
if (session == null || !session.isOpen()) {
|
||||
return false;
|
||||
}
|
||||
return connected;
|
||||
}
|
||||
|
||||
@WebSocket
|
||||
public class KodiWebSocketListener {
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onConnect(Session wssession) {
|
||||
logger.trace("Connected to server");
|
||||
session = wssession;
|
||||
connected = true;
|
||||
if (eventHandler != null) {
|
||||
scheduler.submit(() -> {
|
||||
try {
|
||||
eventHandler.onConnectionOpened();
|
||||
} catch (Exception e) {
|
||||
logger.debug("Error handling onConnectionOpened(): {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String message) {
|
||||
logger.trace("Message received from server: {}", message);
|
||||
final JsonObject json = parser.parse(message).getAsJsonObject();
|
||||
if (json.has("id")) {
|
||||
int messageId = json.get("id").getAsInt();
|
||||
if (messageId == nextMessageId - 1) {
|
||||
commandResponse = json;
|
||||
commandLatch.countDown();
|
||||
}
|
||||
} else {
|
||||
logger.trace("Event received from server: {}", json);
|
||||
if (eventHandler != null) {
|
||||
scheduler.submit(() -> {
|
||||
try {
|
||||
eventHandler.handleEvent(json);
|
||||
} catch (Exception e) {
|
||||
logger.debug("Error handling event {} player state change message: {}", json,
|
||||
e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OnWebSocketClose
|
||||
public void onClose(int statusCode, String reason) {
|
||||
logger.trace("Closing a WebSocket due to {}", reason);
|
||||
session = null;
|
||||
connected = false;
|
||||
if (eventHandler != null) {
|
||||
scheduler.submit(() -> {
|
||||
try {
|
||||
eventHandler.onConnectionClosed();
|
||||
} catch (Exception e) {
|
||||
logger.debug("Error handling onConnectionClosed(): {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@OnWebSocketError
|
||||
public void onError(Throwable error) {
|
||||
logger.trace("Error occured: {}", error.getMessage());
|
||||
onClose(0, error.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(String str) throws IOException {
|
||||
if (isConnected()) {
|
||||
logger.trace("send message: {}", str);
|
||||
session.getRemote().sendString(str);
|
||||
} else {
|
||||
throw new IOException("Socket not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
public JsonElement callMethod(String methodName) {
|
||||
return callMethod(methodName, null);
|
||||
}
|
||||
|
||||
public synchronized JsonElement callMethod(String methodName, JsonObject params) {
|
||||
try {
|
||||
JsonObject payloadObject = new JsonObject();
|
||||
payloadObject.addProperty("jsonrpc", "2.0");
|
||||
payloadObject.addProperty("id", nextMessageId);
|
||||
payloadObject.addProperty("method", methodName);
|
||||
|
||||
if (params != null) {
|
||||
payloadObject.add("params", params);
|
||||
}
|
||||
|
||||
String message = mapper.toJson(payloadObject);
|
||||
|
||||
commandLatch = new CountDownLatch(1);
|
||||
commandResponse = null;
|
||||
nextMessageId++;
|
||||
|
||||
sendMessage(message);
|
||||
if (commandLatch.await(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
|
||||
logger.debug("callMethod returns: {}", commandResponse);
|
||||
if (commandResponse.has("result")) {
|
||||
return commandResponse.get("result");
|
||||
} else {
|
||||
JsonElement error = commandResponse.get("error");
|
||||
logger.debug("Error received from server: {}", error);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
logger.debug("Timeout during callMethod({}, {})", methodName, params);
|
||||
return null;
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
logger.debug("Error during callMethod({}, {}): {}", methodName, params, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.protocol;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* This interface has to be implemented for classes which need to be able to receive events from KodiClientSocket
|
||||
*
|
||||
* @author Paul Frank - Initial contribution
|
||||
*/
|
||||
public interface KodiClientSocketEventListener {
|
||||
|
||||
void handleEvent(JsonObject json);
|
||||
|
||||
void onConnectionClosed();
|
||||
|
||||
void onConnectionOpened();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.config.core.ConfigConstants;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This is a simple file based cache implementation.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ByteArrayFileCache {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ByteArrayFileCache.class);
|
||||
|
||||
private static final String MD5_ALGORITHM = "MD5";
|
||||
|
||||
static final String CACHE_FOLDER_NAME = "cache";
|
||||
private static final char EXTENSION_SEPARATOR = '.';
|
||||
private static final char UNIX_SEPARATOR = '/';
|
||||
private static final char WINDOWS_SEPARATOR = '\\';
|
||||
|
||||
private final File cacheFolder;
|
||||
|
||||
static final long ONE_DAY_IN_MILLIS = TimeUnit.DAYS.toMillis(1);
|
||||
private int expiry = 0;
|
||||
|
||||
private static final Map<String, File> FILES_IN_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link ByteArrayFileCache} instance for a service. Creates a <code>cache</code> folder under
|
||||
* <code>$userdata/cache/$servicePID</code>.
|
||||
*
|
||||
* @param servicePID PID of the service
|
||||
*/
|
||||
public ByteArrayFileCache(String servicePID) {
|
||||
// TODO track and limit folder size
|
||||
// TODO support user specific folder
|
||||
cacheFolder = new File(new File(ConfigConstants.getUserDataFolder(), CACHE_FOLDER_NAME), servicePID);
|
||||
if (!cacheFolder.exists()) {
|
||||
logger.debug("Creating cache folder '{}'", cacheFolder.getAbsolutePath());
|
||||
cacheFolder.mkdirs();
|
||||
}
|
||||
logger.debug("Using cache folder '{}'", cacheFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ByteArrayFileCache} instance for a service. Creates a <code>cache</code> folder under
|
||||
* <code>$userdata/cache/$servicePID/</code>.
|
||||
*
|
||||
* @param servicePID PID of the service
|
||||
* @param int the days for how long the files stay in the cache valid. Must be positive. 0 to
|
||||
* disables this functionality.
|
||||
*/
|
||||
public ByteArrayFileCache(String servicePID, int expiry) {
|
||||
this(servicePID);
|
||||
if (expiry < 0) {
|
||||
throw new IllegalArgumentException("Cache expiration time must be greater than or equal to 0");
|
||||
}
|
||||
this.expiry = expiry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to the cache. If the cache previously contained a file for the key, the old file is replaced by the
|
||||
* new content.
|
||||
*
|
||||
* @param key the key with which the file is to be associated
|
||||
* @param content the content for the file to be associated with the specified key
|
||||
*/
|
||||
public void put(String key, byte[] content) {
|
||||
writeFile(getUniqueFile(key), content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to the cache.
|
||||
*
|
||||
* @param key the key with which the file is to be associated
|
||||
* @param content the content for the file to be associated with the specified key
|
||||
*/
|
||||
public void putIfAbsent(String key, byte[] content) {
|
||||
File fileInCache = getUniqueFile(key);
|
||||
if (fileInCache.exists()) {
|
||||
logger.debug("File '{}' present in cache", fileInCache.getName());
|
||||
// update time of last use
|
||||
fileInCache.setLastModified(System.currentTimeMillis());
|
||||
} else {
|
||||
writeFile(fileInCache, content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to the cache and returns the content of the file.
|
||||
*
|
||||
* @param key the key with which the file is to be associated
|
||||
* @param content the content for the file to be associated with the specified key
|
||||
* @return the content of the file associated with the given key
|
||||
*/
|
||||
public byte[] putIfAbsentAndGet(String key, byte[] content) {
|
||||
putIfAbsent(key, content);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given content to the given {@link File}.
|
||||
*
|
||||
* @param fileInCache the {@link File}
|
||||
* @param content the content to be written
|
||||
*/
|
||||
private void writeFile(File fileInCache, byte[] content) {
|
||||
logger.debug("Caching file '{}'", fileInCache.getName());
|
||||
try {
|
||||
Files.write(fileInCache.toPath(), content);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Could not write file '{}' to cache", fileInCache.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the key is present in the cache.
|
||||
*
|
||||
* @param key the key whose presence in the cache is to be tested
|
||||
* @return true if the cache contains a file for the specified key
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
return getUniqueFile(key).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the file associated with the given key from the cache.
|
||||
*
|
||||
* @param key the key whose associated file is to be removed
|
||||
*/
|
||||
public void remove(String key) {
|
||||
deleteFile(getUniqueFile(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given {@link File}.
|
||||
*
|
||||
* @param fileInCache the {@link File}
|
||||
*/
|
||||
private void deleteFile(File fileInCache) {
|
||||
if (fileInCache.exists()) {
|
||||
logger.debug("Deleting file '{}' from cache", fileInCache.getName());
|
||||
fileInCache.delete();
|
||||
} else {
|
||||
logger.debug("File '{}' not found in cache", fileInCache.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all files from the cache.
|
||||
*/
|
||||
public void clear() {
|
||||
File[] filesInCache = cacheFolder.listFiles();
|
||||
if (filesInCache != null && filesInCache.length > 0) {
|
||||
logger.debug("Deleting all files from cache");
|
||||
Arrays.stream(filesInCache).forEach(File::delete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes expired files from the cache.
|
||||
*/
|
||||
public void clearExpired() {
|
||||
// exit if expiry is set to 0 (disabled)
|
||||
if (expiry <= 0) {
|
||||
return;
|
||||
}
|
||||
File[] filesInCache = cacheFolder.listFiles();
|
||||
if (filesInCache != null && filesInCache.length > 0) {
|
||||
logger.debug("Deleting expired files from cache");
|
||||
Arrays.stream(filesInCache).filter(file -> isExpired(file)).forEach(File::delete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given {@link File} is expired.
|
||||
*
|
||||
* @param fileInCache the {@link File}
|
||||
* @return <code>true</code> if the file is expired, <code>false</code> otherwise
|
||||
*/
|
||||
private boolean isExpired(File fileInCache) {
|
||||
// exit if expiry is set to 0 (disabled)
|
||||
if (expiry <= 0) {
|
||||
return false;
|
||||
}
|
||||
return expiry * ONE_DAY_IN_MILLIS < System.currentTimeMillis() - fileInCache.lastModified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of the file associated with the given key, if it is present.
|
||||
*
|
||||
* @param key the key whose associated file is to be returned
|
||||
* @return the content of the file associated with the given key
|
||||
*/
|
||||
public byte[] get(String key) {
|
||||
return readFile(getUniqueFile(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the content from the given {@link File}, if it is present.
|
||||
*
|
||||
* @param fileInCache the {@link File}
|
||||
* @return the content of the file
|
||||
*/
|
||||
private byte[] readFile(File fileInCache) {
|
||||
if (fileInCache.exists()) {
|
||||
logger.debug("Reading file '{}' from cache", fileInCache.getName());
|
||||
// update time of last use
|
||||
fileInCache.setLastModified(System.currentTimeMillis());
|
||||
try {
|
||||
return Files.readAllBytes(fileInCache.toPath());
|
||||
} catch (IOException e) {
|
||||
logger.warn("Could not read file '{}' from cache", fileInCache.getName(), e);
|
||||
}
|
||||
} else {
|
||||
logger.debug("File '{}' not found in cache", fileInCache.getName());
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a unique {@link File} from the key with which the file is to be associated.
|
||||
*
|
||||
* @param key the key with which the file is to be associated
|
||||
* @return unique file for the file associated with the given key
|
||||
*/
|
||||
File getUniqueFile(String key) {
|
||||
String uniqueFileName = getUniqueFileName(key);
|
||||
if (FILES_IN_CACHE.containsKey(uniqueFileName)) {
|
||||
return FILES_IN_CACHE.get(uniqueFileName);
|
||||
} else {
|
||||
String fileExtension = getFileExtension(key);
|
||||
File fileInCache = new File(cacheFolder,
|
||||
uniqueFileName + (fileExtension == null ? "" : EXTENSION_SEPARATOR + fileExtension));
|
||||
FILES_IN_CACHE.put(uniqueFileName, fileInCache);
|
||||
return fileInCache;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extension of a file name.
|
||||
*
|
||||
* @param fileName the file name to retrieve the extension of
|
||||
* @return the extension of the file or null if none exists
|
||||
*/
|
||||
@Nullable
|
||||
String getFileExtension(String fileName) {
|
||||
int extensionPos = fileName.lastIndexOf(EXTENSION_SEPARATOR);
|
||||
int lastSeparatorPos = Math.max(fileName.lastIndexOf(UNIX_SEPARATOR), fileName.lastIndexOf(WINDOWS_SEPARATOR));
|
||||
return lastSeparatorPos > extensionPos ? null : fileName.substring(extensionPos + 1).replaceFirst("\\?.*$", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a unique file name from the key with which the file is to be associated.
|
||||
*
|
||||
* @param key the key with which the file is to be associated
|
||||
* @return unique file name for the file associated with the given key
|
||||
*/
|
||||
String getUniqueFileName(String key) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance(MD5_ALGORITHM);
|
||||
byte[] bytesOfKey = key.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] md5Hash = md.digest(bytesOfKey);
|
||||
BigInteger bigInt = new BigInteger(1, md5Hash);
|
||||
String fileNameHash = bigInt.toString(16);
|
||||
// We need to zero pad it if you actually want the full 32 chars
|
||||
while (fileNameHash.length() < 32) {
|
||||
fileNameHash = "0" + fileNameHash;
|
||||
}
|
||||
return fileNameHash;
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
// should not happen
|
||||
logger.error("Could not create MD5 hash for key '{}'", key, ex);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="kodi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Kodi Binding</name>
|
||||
<description>This is the binding for Kodi.</description>
|
||||
<author>Paul Frank</author>
|
||||
|
||||
<config-description>
|
||||
<parameter name="callbackUrl" type="text">
|
||||
<label>Callback URL</label>
|
||||
<description>url to use for playing notification sounds, e.g. http://192.168.0.2:8080</description>
|
||||
<required>false</required>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?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:kodi:kodi">
|
||||
<parameter-group name="network">
|
||||
<label>Network</label>
|
||||
<description>Network settings.</description>
|
||||
</parameter-group>
|
||||
<parameter-group name="connection">
|
||||
<label>Connection</label>
|
||||
<description>Connection settings.</description>
|
||||
</parameter-group>
|
||||
<parameter-group name="notification">
|
||||
<label>Notification</label>
|
||||
<description>Notification settings.</description>
|
||||
</parameter-group>
|
||||
<parameter name="ipAddress" type="text" required="true" groupName="network">
|
||||
<label>Network Address</label>
|
||||
<description>The IP or host name of the Kodi</description>
|
||||
<context>network-address</context>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" required="true" min="1" max="65535" groupName="network">
|
||||
<label>Web Socket Service Port</label>
|
||||
<description>Port for the web socket service</description>
|
||||
<default>9090</default>
|
||||
</parameter>
|
||||
<parameter name="httpPort" type="integer" required="true" min="1" max="65535" groupName="network">
|
||||
<label>HTTP Service Port</label>
|
||||
<description>Port for the HTTP service.</description>
|
||||
<default>8080</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="httpUser" type="text" required="false" groupName="network">
|
||||
<label>Username</label>
|
||||
<description>User name to access HTTP service.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="httpPassword" type="text" required="false" groupName="network">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>Password to access the HTTP service.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" required="false" min="1" max="60" unit="s"
|
||||
groupName="connection">
|
||||
<label>Refresh Interval</label>
|
||||
<description>The refresh interval to poll Kodi API (in s).</description>
|
||||
<default>10</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="notificationTimeout" type="integer" unit="s" required="true" groupName="notification">
|
||||
<label>Notification Timeout</label>
|
||||
<description>Maximum amount of time for which the notification will be played (in s).</description>
|
||||
<default>20</default>
|
||||
</parameter>
|
||||
<parameter name="notificationVolume" type="integer" min="0" max="100" step="1" unit="%"
|
||||
groupName="notification">
|
||||
<label>Notification Sound Volume</label>
|
||||
<description>Specifies the volume applied to a notification sound (in %).</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="channel-type:kodi:pvr-channel">
|
||||
<parameter name="group" type="text" required="true">
|
||||
<label>PVR Channel Group</label>
|
||||
<description>The PVR channel group name used to identify the channel id.</description>
|
||||
<default>All channels</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="channel-type:kodi:shownotification">
|
||||
<parameter name="title" type="text" required="false">
|
||||
<label>Notification Title</label>
|
||||
<description>Title to show for the notification.</description>
|
||||
<default>openHAB</default>
|
||||
</parameter>
|
||||
<parameter name="displayTime" type="integer" required="false" min="100" unit="ms">
|
||||
<label>Display Time</label>
|
||||
<description>Time the notification is displayed.</description>
|
||||
<default>5000</default>
|
||||
</parameter>
|
||||
<parameter name="icon" type="text" required="false">
|
||||
<label>Icon</label>
|
||||
<description>Icon name to show with the notification.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,114 @@
|
||||
# binding
|
||||
binding.kodi.name = Kodi Binding
|
||||
binding.kodi.description = Dieses Binding integriert das Kodi Media Center. Durch dieses kann das Media Center gesteuert werden.
|
||||
|
||||
# binding config
|
||||
binding.config.kodi.callbackUrl.label = Callback URL
|
||||
binding.config.kodi.callbackUrl.description = URL zum Abspielen von Benachrichtigungen (z.B. http://192.168.0.2:8080).
|
||||
|
||||
# thing types
|
||||
thing-type.kodi.kodi.label = Kodi Media Center
|
||||
thing-type.kodi.kodi.description = Kodi Media Center
|
||||
|
||||
# thing types config
|
||||
thing-type.config.kodi.kodi.group.network.label = Netzwerk
|
||||
thing-type.config.kodi.kodi.group.network.description = Einstellungen für das Netzwerk.
|
||||
thing-type.config.kodi.kodi.group.connection.label = Verbindung
|
||||
thing-type.config.kodi.kodi.group.connection.description = Einstellungen für die Verbindung.
|
||||
thing-type.config.kodi.kodi.group.notification.label = Benachrichtigungen
|
||||
thing-type.config.kodi.kodi.group.notification.description = Einstellungen für Benachrichtigungen.
|
||||
|
||||
thing-type.config.kodi.kodi.ipAddress.label = IP-Adresse
|
||||
thing-type.config.kodi.kodi.ipAddress.description = Lokale IP-Adresse oder Hostname des Kodi Media Centers.
|
||||
thing-type.config.kodi.kodi.port.label = Port (Webservice)
|
||||
thing-type.config.kodi.kodi.port.description = Port des Kodi Media Centers Webservices.
|
||||
thing-type.config.kodi.kodi.httpPort.label = Port (Weboberfläche)
|
||||
thing-type.config.kodi.kodi.httpPort.description = Port der Kodi Media Center Weboberfläche.
|
||||
thing-type.config.kodi.kodi.httpUser.label = Benutzer
|
||||
thing-type.config.kodi.kodi.httpUser.description = Benutzer zur Authentifizierung an der Kodi Media Center Weboberfläche.
|
||||
thing-type.config.kodi.kodi.httpPassword.label = Passwort
|
||||
thing-type.config.kodi.kodi.httpPassword.description = Passwort zur Authentifizierung an der Kodi Media Center Weboberfläche.
|
||||
thing-type.config.kodi.kodi.refreshInterval.label = Abfrageintervall
|
||||
thing-type.config.kodi.kodi.refreshInterval.description = Intervall zur Abfrage des Kodi Media Centers (in s).
|
||||
thing-type.config.kodi.kodi.notificationTimeout.label = Timeout für Benachrichtigungen
|
||||
thing-type.config.kodi.kodi.notificationTimeout.description = Timeout für Benachrichtigungen (in s).
|
||||
thing-type.config.kodi.kodi.notificationVolume.label = Lautstärke von Benachrichtigungen
|
||||
thing-type.config.kodi.kodi.notificationVolume.description = Lautstärke von Benachrichtigungen (in %).
|
||||
|
||||
# channel types
|
||||
channel-type.kodi.stop.label = Stop
|
||||
channel-type.kodi.stop.description = Ermöglicht das Stoppen der Wiedergabe.
|
||||
channel-type.kodi.playuri.label = URI abspielen
|
||||
channel-type.kodi.playuri.description = Ermöglicht das Abspielen einer URI.
|
||||
channel-type.kodi.playfavorite.label = Favorit abspielen oder öffnen
|
||||
channel-type.kodi.playfavorite.description = Ermöglicht das Abspielen oder Öffnen eines Favoriten.
|
||||
channel-type.kodi.pvr-open-tv.label = PVR TV Kanal
|
||||
channel-type.kodi.pvr-open-tv.description = Ermöglicht das Abspielen eines PVR TV Kanals.
|
||||
channel-type.kodi.pvr-open-radio.label = PVR Radio Kanal
|
||||
channel-type.kodi.pvr-open-radio.description = Ermöglicht das Abspielen eines PVR Radio Kanals.
|
||||
channel-type.kodi.pvr-channel.label = PVR Kanal
|
||||
channel-type.kodi.pvr-channel.description = Zeigt den Titel des aktuellen PVR Kanals an.
|
||||
channel-type.kodi.shownotification.label = Nachricht anzeigen
|
||||
channel-type.kodi.shownotification.description = Ermöglicht das Anzeigen einer Nachricht auf dem Kodi Media Center.
|
||||
channel-type.kodi.input.label = Tastendruck
|
||||
channel-type.kodi.input.description = Ermöglicht das Senden eines Eingabebefehls zur Steuerung der UI an das Kodi Media Center.
|
||||
channel-type.kodi.input.state.option.Back = Zurück
|
||||
channel-type.kodi.input.state.option.ContextMenu = Kontextmenü
|
||||
channel-type.kodi.input.state.option.Down = Runter
|
||||
channel-type.kodi.input.state.option.Home = Home
|
||||
channel-type.kodi.input.state.option.Info = Info Dialog
|
||||
channel-type.kodi.input.state.option.Left = Links
|
||||
channel-type.kodi.input.state.option.Right = Rechts
|
||||
channel-type.kodi.input.state.option.Select = Auswählen
|
||||
channel-type.kodi.input.state.option.ShowCodec = Codec Info
|
||||
channel-type.kodi.input.state.option.ShowOSD = Bildschirmanzeige
|
||||
channel-type.kodi.input.state.option.ShowPlayerProcessInfo = Player Info
|
||||
channel-type.kodi.input.state.option.Up = Hoch
|
||||
channel-type.kodi.inputtext.label = Texteingabe
|
||||
channel-type.kodi.inputtext.description = Ermöglicht das Senden eines Eingabetextes (Unicode) an das Kodi Media Center.
|
||||
channel-type.kodi.inputaction.label = Aktion
|
||||
channel-type.kodi.inputaction.description = Ermöglicht das Senden einer vordefinierten Aktion an das Kodi Media Center.
|
||||
channel-type.kodi.systemcommand.label = Systembefehl
|
||||
channel-type.kodi.systemcommand.description = Ermöglicht das Senden eines Systembefehls um das Kodi Media Center neu zu starten, in den Ruhezustand oder Stromsparmodus zu versetzen oder herunterzufahren.
|
||||
channel-type.kodi.systemcommand.command.option.Shutdown = Herunterfahren
|
||||
channel-type.kodi.systemcommand.command.option.Suspend = Bereitschaft
|
||||
channel-type.kodi.systemcommand.command.option.Hibernate = Ruhezustand
|
||||
channel-type.kodi.systemcommand.command.option.Reboot = Neustart
|
||||
channel-type.kodi.systemcommand.command.option.Quit = Verlassen
|
||||
channel-type.kodi.showtitle.label = Show Titel
|
||||
channel-type.kodi.showtitle.description = Zeigt den Titel der aktuellen Show an.
|
||||
channel-type.kodi.album.label = Album
|
||||
channel-type.kodi.album.description = Zeigt das Album des aktuellen Stücks an.
|
||||
channel-type.kodi.mediatype.label = Medientyp
|
||||
channel-type.kodi.mediatype.description = Zeigt den Medientyp des aktuellen Stücks oder Films (z. B. movie, song) an.
|
||||
channel-type.kodi.genreList.label = Genre Liste
|
||||
channel-type.kodi.genreList.description = Zeigt eine durch Komma getrennte Liste der Genres des aktuellen Stücks oder Films an.
|
||||
channel-type.kodi.thumbnail.label = Thumbnail
|
||||
channel-type.kodi.thumbnail.description = Zeigt das Thumbnail des aktuellen Stücks oder Films an.
|
||||
channel-type.kodi.fanart.label = Fan Art
|
||||
channel-type.kodi.fanart.description = Zeigt das Fan Art des aktuellen Stücks oder Films an.
|
||||
channel-type.kodi.playnotification.label = Benachrichtigung abspielen
|
||||
channel-type.kodi.playnotification.description = Ermöglicht das Abspielen einer Benachrichtigung.
|
||||
channel-type.kodi.codec.label = Codec
|
||||
channel-type.kodi.codec.description = Zeigt den Codec des aktuellen Stücks oder Films an.
|
||||
thing-type.kodi.kodi.channel.audio-codec.label = Audio-Codec
|
||||
thing-type.kodi.kodi.channel.audio-codec.description = Zeigt den Audio-Codec des aktuellen Stücks oder Films an.
|
||||
thing-type.kodi.kodi.channel.video-codec.label = Video-Codec
|
||||
thing-type.kodi.kodi.channel.video-codec.description = Zeigt den Video-Codec des aktuellen Stücks oder Films an.
|
||||
channel-type.kodi.currenttime.label = Laufzeit
|
||||
channel-type.kodi.currenttime.description = Zeigt die Laufzeit des aktuellen Stücks oder Films an.
|
||||
channel-type.kodi.currenttimepercentage.label = Laufzeit in Prozent
|
||||
channel-type.kodi.currenttimepercentage.description = Zeigt die Laufzeit des aktuellen Stücks oder Films an.
|
||||
channel-type.kodi.duration.label = Dauer
|
||||
channel-type.kodi.duration.description = Zeigt die Dauer des aktuellen Stücks oder Films an.
|
||||
|
||||
# channel types config
|
||||
channel-type.config.kodi.pvr-channel.group.label = PVR Kanal Gruppe
|
||||
channel-type.config.kodi.pvr-channel.group.description = Gruppe der verwendbaren PVR Kanäle.
|
||||
|
||||
channel-type.config.kodi.shownotification.title.label = Titel
|
||||
channel-type.config.kodi.shownotification.title.description = Titel der Nachricht.
|
||||
channel-type.config.kodi.shownotification.displayTime.label = Anzeigezeit
|
||||
channel-type.config.kodi.shownotification.displayTime.description = Anzeigezeit der Nachricht.
|
||||
channel-type.config.kodi.shownotification.icon.label = Icon
|
||||
channel-type.config.kodi.shownotification.icon.description = Icon der Nachricht.
|
||||
@@ -0,0 +1,557 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="kodi"
|
||||
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">
|
||||
|
||||
<!-- Kodi Thing Type -->
|
||||
<thing-type id="kodi" extensible="shownotification">
|
||||
<label>Kodi Mediacenter</label>
|
||||
<description>Kodi Mediacenter Binding</description>
|
||||
|
||||
<channels>
|
||||
<channel id="volume" typeId="system.volume"/>
|
||||
<channel id="mute" typeId="system.mute"/>
|
||||
<channel id="control" typeId="system.media-control"/>
|
||||
<channel id="stop" typeId="stop"/>
|
||||
<channel id="playuri" typeId="playuri"/>
|
||||
<channel id="pvr-open-tv" typeId="pvr-open-tv"/>
|
||||
<channel id="pvr-open-radio" typeId="pvr-open-radio"/>
|
||||
<channel id="pvr-channel" typeId="pvr-channel"/>
|
||||
<channel id="shownotification" typeId="shownotification"/>
|
||||
<channel id="input" typeId="input"/>
|
||||
<channel id="inputtext" typeId="inputtext"/>
|
||||
<channel id="inputaction" typeId="inputaction"/>
|
||||
<channel id="systemcommand" typeId="systemcommand"/>
|
||||
<channel id="title" typeId="system.media-title"/>
|
||||
<channel id="originaltitle" typeId="system.media-title"/>
|
||||
<channel id="showtitle" typeId="showtitle"/>
|
||||
<channel id="album" typeId="album"/>
|
||||
<channel id="artist" typeId="system.media-artist"/>
|
||||
<channel id="mediatype" typeId="mediatype"/>
|
||||
<channel id="genreList" typeId="genreList"/>
|
||||
<channel id="thumbnail" typeId="thumbnail"/>
|
||||
<channel id="fanart" typeId="fanart"/>
|
||||
<channel id="playfavorite" typeId="playfavorite"/>
|
||||
<channel id="playnotification" typeId="playnotification"/>
|
||||
<channel id="profile" typeId="profile"/>
|
||||
<channel id="audio-name" typeId="name">
|
||||
<label>Audio Name</label>
|
||||
</channel>
|
||||
<channel id="audio-channels" typeId="channels">
|
||||
<label>Audio Channels</label>
|
||||
</channel>
|
||||
<channel id="audio-index" typeId="index">
|
||||
<label>Audio Index</label>
|
||||
</channel>
|
||||
<channel id="audio-language" typeId="language">
|
||||
<label>Audio Language</label>
|
||||
</channel>
|
||||
<channel id="audio-codec" typeId="codec">
|
||||
<label>Audio Codec</label>
|
||||
<description>Audio codec of currently playing media</description>
|
||||
</channel>
|
||||
<channel id="video-width" typeId="width"/>
|
||||
<channel id="video-height" typeId="height"/>
|
||||
<channel id="video-index" typeId="index">
|
||||
<label>Video Index</label>
|
||||
</channel>
|
||||
<channel id="video-codec" typeId="codec">
|
||||
<label>Video Codec</label>
|
||||
<description>Video codec of currently playing media</description>
|
||||
</channel>
|
||||
<channel id="subtitle-enabled" typeId="enabled">
|
||||
<label>Subtitle Enabled</label>
|
||||
</channel>
|
||||
<channel id="subtitle-name" typeId="name">
|
||||
<label>Subtitle Name</label>
|
||||
</channel>
|
||||
<channel id="subtitle-index" typeId="index">
|
||||
<label>Subtitle Index</label>
|
||||
</channel>
|
||||
<channel id="subtitle-language" typeId="language">
|
||||
<label>Subtitle Language</label>
|
||||
</channel>
|
||||
<channel id="currenttime" typeId="currenttime"/>
|
||||
<channel id="currenttimepercentage" typeId="currenttimepercentage"/>
|
||||
<channel id="duration" typeId="duration"/>
|
||||
<channel id="mediafile" typeId="file"/>
|
||||
<channel id="mediaid" typeId="id">
|
||||
<label>Kodi Media ID</label>
|
||||
<description>media_id in kodi database</description>
|
||||
</channel>
|
||||
<channel id="uniqueid-douban" typeId="uniqueid">
|
||||
<label>Douban ID</label>
|
||||
<description>example usage - http://www.douban.com/subject/3036644/</description>
|
||||
</channel>
|
||||
<channel id="uniqueid-imdb" typeId="uniqueid">
|
||||
<label>IMDB ID</label>
|
||||
<description>example usage - http://www.imdb.com/title/tt7207268/</description>
|
||||
</channel>
|
||||
<channel id="uniqueid-imdbtvshow" typeId="uniqueid">
|
||||
<label>IMDB TVSHOW ID</label>
|
||||
<description>example usage - http://www.imdb.com/title/tt0426769/</description>
|
||||
</channel>
|
||||
<channel id="uniqueid-tmdb" typeId="uniqueid">
|
||||
<label>TMDB ID</label>
|
||||
<description>example usage - http://www.themoviedb.org/movie/123456789</description>
|
||||
</channel>
|
||||
<channel id="uniqueid-tmdbepisode" typeId="uniqueid">
|
||||
<label>TMDB EPISODE ID</label>
|
||||
<description>example usage - http://www.themoviedb.org/tv/12225/season/5/episode/15/</description>
|
||||
</channel>
|
||||
<channel id="uniqueid-tmdbtvshow" typeId="uniqueid">
|
||||
<label>TMDB TVSHOW ID</label>
|
||||
<description>example usage - http://www.themoviedb.org/tv/12225/</description>
|
||||
</channel>
|
||||
<channel id="mpaa" typeId="mpaa"/>
|
||||
<channel id="rating" typeId="rating"/>
|
||||
<channel id="userrating" typeId="rating">
|
||||
<label>User Rating</label>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="version">unknown</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ipAddress</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:kodi:kodi"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Kodi Commands -->
|
||||
<channel-type id="stop">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Stop</label>
|
||||
<description>Stops the player. ON if the player is stopped.</description>
|
||||
</channel-type>
|
||||
<channel-type id="playuri" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Play URI</label>
|
||||
<description>Play the given URI</description>
|
||||
</channel-type>
|
||||
<channel-type id="playfavorite">
|
||||
<item-type>String</item-type>
|
||||
<label>Play or Open a Favorite</label>
|
||||
<description>Play or open the given favorite by sending a command with the favorite's title</description>
|
||||
</channel-type>
|
||||
<channel-type id="pvr-open-tv" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Play PVR TV</label>
|
||||
<description>Play the given PVR TV channel by sending a command with the channel's name</description>
|
||||
<state pattern="%s"/>
|
||||
<config-description-ref uri="channel-type:kodi:pvr-channel"/>
|
||||
</channel-type>
|
||||
<channel-type id="pvr-open-radio" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Play PVR Radio</label>
|
||||
<description>Play the given PVR Radio channel by sending a command with the channel's name</description>
|
||||
<state pattern="%s"/>
|
||||
<config-description-ref uri="channel-type:kodi:pvr-channel"/>
|
||||
</channel-type>
|
||||
<channel-type id="shownotification" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Show Notification</label>
|
||||
<description>Shows a notification on the UI</description>
|
||||
<config-description-ref uri="channel-type:kodi:shownotification"/>
|
||||
</channel-type>
|
||||
<channel-type id="playnotification" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Play Notification</label>
|
||||
<description>Plays a notification sound by a given URI</description>
|
||||
</channel-type>
|
||||
<channel-type id="input" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Send a Key</label>
|
||||
<description>Sends a key stroke to Kodi to navigate in the UI</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="Back">Back</option>
|
||||
<option value="ContextMenu">Show context Menu</option>
|
||||
<option value="Down">Down</option>
|
||||
<option value="Home">Home</option>
|
||||
<option value="Info">Show information dialog</option>
|
||||
<option value="Left">Left</option>
|
||||
<option value="Right">Right</option>
|
||||
<option value="Select">Select</option>
|
||||
<option value="ShowCodec">Show codec information</option>
|
||||
<option value="ShowOSD">Show on-screen display</option>
|
||||
<option value="ShowPlayerProcessInfo">Show player process info</option>
|
||||
<option value="Up">Up</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="inputtext" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Sends Text as Input</label>
|
||||
<description>Sends a generic input (unicode) text to Kodi</description>
|
||||
</channel-type>
|
||||
<channel-type id="inputaction" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Execute an Action</label>
|
||||
<description>Sends a predefined action to Kodi to control the UI and/or perform other tasks</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="left">left</option>
|
||||
<option value="right">right</option>
|
||||
<option value="up">up</option>
|
||||
<option value="down">down</option>
|
||||
<option value="pageup">pageup</option>
|
||||
<option value="pagedown">pagedown</option>
|
||||
<option value="select">select</option>
|
||||
<option value="highlight">highlight</option>
|
||||
<option value="parentdir">parentdir</option>
|
||||
<option value="parentfolder">parentfolder</option>
|
||||
<option value="back">back</option>
|
||||
<option value="menu">menu</option>
|
||||
<option value="previousmenu">previousmenu</option>
|
||||
<option value="info">info</option>
|
||||
<option value="pause">pause</option>
|
||||
<option value="stop">stop</option>
|
||||
<option value="skipnext">skipnext</option>
|
||||
<option value="skipprevious">skipprevious</option>
|
||||
<option value="fullscreen">fullscreen</option>
|
||||
<option value="aspectratio">aspectratio</option>
|
||||
<option value="stepforward">stepforward</option>
|
||||
<option value="stepback">stepback</option>
|
||||
<option value="bigstepforward">bigstepforward</option>
|
||||
<option value="bigstepback">bigstepback</option>
|
||||
<option value="chapterorbigstepforward">chapterorbigstepforward</option>
|
||||
<option value="chapterorbigstepback">chapterorbigstepback</option>
|
||||
<option value="osd">osd</option>
|
||||
<option value="showsubtitles">showsubtitles</option>
|
||||
<option value="nextsubtitle">nextsubtitle</option>
|
||||
<option value="cyclesubtitle">cyclesubtitle</option>
|
||||
<option value="playerdebug">playerdebug</option>
|
||||
<option value="codecinfo">codecinfo</option>
|
||||
<option value="playerprocessinfo">playerprocessinfo</option>
|
||||
<option value="nextpicture">nextpicture</option>
|
||||
<option value="previouspicture">previouspicture</option>
|
||||
<option value="zoomout">zoomout</option>
|
||||
<option value="zoomin">zoomin</option>
|
||||
<option value="playlist">playlist</option>
|
||||
<option value="queue">queue</option>
|
||||
<option value="zoomnormal">zoomnormal</option>
|
||||
<option value="zoomlevel1">zoomlevel1</option>
|
||||
<option value="zoomlevel2">zoomlevel2</option>
|
||||
<option value="zoomlevel3">zoomlevel3</option>
|
||||
<option value="zoomlevel4">zoomlevel4</option>
|
||||
<option value="zoomlevel5">zoomlevel5</option>
|
||||
<option value="zoomlevel6">zoomlevel6</option>
|
||||
<option value="zoomlevel7">zoomlevel7</option>
|
||||
<option value="zoomlevel8">zoomlevel8</option>
|
||||
<option value="zoomlevel9">zoomlevel9</option>
|
||||
<option value="nextcalibration">nextcalibration</option>
|
||||
<option value="resetcalibration">resetcalibration</option>
|
||||
<option value="analogmove">analogmove</option>
|
||||
<option value="analogmovex">analogmovex</option>
|
||||
<option value="analogmovey">analogmovey</option>
|
||||
<option value="rotate">rotate</option>
|
||||
<option value="rotateccw">rotateccw</option>
|
||||
<option value="close">close</option>
|
||||
<option value="subtitledelayminus">subtitledelayminus</option>
|
||||
<option value="subtitledelay">subtitledelay</option>
|
||||
<option value="subtitledelayplus">subtitledelayplus</option>
|
||||
<option value="audiodelayminus">audiodelayminus</option>
|
||||
<option value="audiodelay">audiodelay</option>
|
||||
<option value="audiodelayplus">audiodelayplus</option>
|
||||
<option value="subtitleshiftup">subtitleshiftup</option>
|
||||
<option value="subtitleshiftdown">subtitleshiftdown</option>
|
||||
<option value="subtitlealign">subtitlealign</option>
|
||||
<option value="audionextlanguage">audionextlanguage</option>
|
||||
<option value="verticalshiftup">verticalshiftup</option>
|
||||
<option value="verticalshiftdown">verticalshiftdown</option>
|
||||
<option value="nextresolution">nextresolution</option>
|
||||
<option value="audiotoggledigital">audiotoggledigital</option>
|
||||
<option value="number0">number0</option>
|
||||
<option value="number1">number1</option>
|
||||
<option value="number2">number2</option>
|
||||
<option value="number3">number3</option>
|
||||
<option value="number4">number4</option>
|
||||
<option value="number5">number5</option>
|
||||
<option value="number6">number6</option>
|
||||
<option value="number7">number7</option>
|
||||
<option value="number8">number8</option>
|
||||
<option value="number9">number9</option>
|
||||
<option value="smallstepback">smallstepback</option>
|
||||
<option value="fastforward">fastforward</option>
|
||||
<option value="rewind">rewind</option>
|
||||
<option value="play">play</option>
|
||||
<option value="playpause">playpause</option>
|
||||
<option value="switchplayer">switchplayer</option>
|
||||
<option value="delete">delete</option>
|
||||
<option value="copy">copy</option>
|
||||
<option value="move">move</option>
|
||||
<option value="screenshot">screenshot</option>
|
||||
<option value="rename">rename</option>
|
||||
<option value="togglewatched">togglewatched</option>
|
||||
<option value="scanitem">scanitem</option>
|
||||
<option value="reloadkeymaps">reloadkeymaps</option>
|
||||
<option value="volumeup">volumeup</option>
|
||||
<option value="volumedown">volumedown</option>
|
||||
<option value="mute">mute</option>
|
||||
<option value="backspace">backspace</option>
|
||||
<option value="scrollup">scrollup</option>
|
||||
<option value="scrolldown">scrolldown</option>
|
||||
<option value="analogfastforward">analogfastforward</option>
|
||||
<option value="analogrewind">analogrewind</option>
|
||||
<option value="moveitemup">moveitemup</option>
|
||||
<option value="moveitemdown">moveitemdown</option>
|
||||
<option value="contextmenu">contextmenu</option>
|
||||
<option value="shift">shift</option>
|
||||
<option value="symbols">symbols</option>
|
||||
<option value="cursorleft">cursorleft</option>
|
||||
<option value="cursorright">cursorright</option>
|
||||
<option value="showtime">showtime</option>
|
||||
<option value="analogseekforward">analogseekforward</option>
|
||||
<option value="analogseekback">analogseekback</option>
|
||||
<option value="showpreset">showpreset</option>
|
||||
<option value="nextpreset">nextpreset</option>
|
||||
<option value="previouspreset">previouspreset</option>
|
||||
<option value="lockpreset">lockpreset</option>
|
||||
<option value="randompreset">randompreset</option>
|
||||
<option value="increasevisrating">increasevisrating</option>
|
||||
<option value="decreasevisrating">decreasevisrating</option>
|
||||
<option value="showvideomenu">showvideomenu</option>
|
||||
<option value="enter">enter</option>
|
||||
<option value="increaserating">increaserating</option>
|
||||
<option value="decreaserating">decreaserating</option>
|
||||
<option value="setrating">setrating</option>
|
||||
<option value="togglefullscreen">togglefullscreen</option>
|
||||
<option value="nextscene">nextscene</option>
|
||||
<option value="previousscene">previousscene</option>
|
||||
<option value="nextletter">nextletter</option>
|
||||
<option value="prevletter">prevletter</option>
|
||||
<option value="jumpsms2">jumpsms2</option>
|
||||
<option value="jumpsms3">jumpsms3</option>
|
||||
<option value="jumpsms4">jumpsms4</option>
|
||||
<option value="jumpsms5">jumpsms5</option>
|
||||
<option value="jumpsms6">jumpsms6</option>
|
||||
<option value="jumpsms7">jumpsms7</option>
|
||||
<option value="jumpsms8">jumpsms8</option>
|
||||
<option value="jumpsms9">jumpsms9</option>
|
||||
<option value="filter">filter</option>
|
||||
<option value="filterclear">filterclear</option>
|
||||
<option value="filtersms2">filtersms2</option>
|
||||
<option value="filtersms3">filtersms3</option>
|
||||
<option value="filtersms4">filtersms4</option>
|
||||
<option value="filtersms5">filtersms5</option>
|
||||
<option value="filtersms6">filtersms6</option>
|
||||
<option value="filtersms7">filtersms7</option>
|
||||
<option value="filtersms8">filtersms8</option>
|
||||
<option value="filtersms9">filtersms9</option>
|
||||
<option value="firstpage">firstpage</option>
|
||||
<option value="lastpage">lastpage</option>
|
||||
<option value="guiprofile">guiprofile</option>
|
||||
<option value="red">red</option>
|
||||
<option value="green">green</option>
|
||||
<option value="yellow">yellow</option>
|
||||
<option value="blue">blue</option>
|
||||
<option value="increasepar">increasepar</option>
|
||||
<option value="decreasepar">decreasepar</option>
|
||||
<option value="volampup">volampup</option>
|
||||
<option value="volampdown">volampdown</option>
|
||||
<option value="volumeamplification">volumeamplification</option>
|
||||
<option value="createbookmark">createbookmark</option>
|
||||
<option value="createepisodebookmark">createepisodebookmark</option>
|
||||
<option value="settingsreset">settingsreset</option>
|
||||
<option value="settingslevelchange">settingslevelchange</option>
|
||||
<option value="stereomode">stereomode</option>
|
||||
<option value="nextstereomode">nextstereomode</option>
|
||||
<option value="previousstereomode">previousstereomode</option>
|
||||
<option value="togglestereomode">togglestereomode</option>
|
||||
<option value="stereomodetomono">stereomodetomono</option>
|
||||
<option value="channelup">channelup</option>
|
||||
<option value="channeldown">channeldown</option>
|
||||
<option value="previouschannelgroup">previouschannelgroup</option>
|
||||
<option value="nextchannelgroup">nextchannelgroup</option>
|
||||
<option value="playpvr">playpvr</option>
|
||||
<option value="playpvrtv">playpvrtv</option>
|
||||
<option value="playpvrradio">playpvrradio</option>
|
||||
<option value="record">record</option>
|
||||
<option value="togglecommskip">togglecommskip</option>
|
||||
<option value="showtimerrule">showtimerrule</option>
|
||||
<option value="leftclick">leftclick</option>
|
||||
<option value="rightclick">rightclick</option>
|
||||
<option value="middleclick">middleclick</option>
|
||||
<option value="doubleclick">doubleclick</option>
|
||||
<option value="longclick">longclick</option>
|
||||
<option value="wheelup">wheelup</option>
|
||||
<option value="wheeldown">wheeldown</option>
|
||||
<option value="mousedrag">mousedrag</option>
|
||||
<option value="mousemove">mousemove</option>
|
||||
<option value="tap">tap</option>
|
||||
<option value="longpress">longpress</option>
|
||||
<option value="pangesture">pangesture</option>
|
||||
<option value="zoomgesture">zoomgesture</option>
|
||||
<option value="rotategesture">rotategesture</option>
|
||||
<option value="swipeleft">swipeleft</option>
|
||||
<option value="swiperight">swiperight</option>
|
||||
<option value="swipeup">swipeup</option>
|
||||
<option value="swipedown">swipedown</option>
|
||||
<option value="error">error</option>
|
||||
<option value="noop">noop</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="systemcommand" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Send System Command</label>
|
||||
<description>Sends a system command to Kodi. This allows to shutdown/suspend/hibernate/reboot/quit Kodi</description>
|
||||
<command>
|
||||
<options>
|
||||
<option value="Shutdown">Shutdown</option>
|
||||
<option value="Suspend">Suspend</option>
|
||||
<option value="Hibernate">Hibernate</option>
|
||||
<option value="Reboot">Reboot</option>
|
||||
<option value="Quit">Quit</option>
|
||||
</options>
|
||||
</command>
|
||||
</channel-type>
|
||||
|
||||
<!-- Kodi variables -->
|
||||
<channel-type id="pvr-channel" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>PVR Channel Title</label>
|
||||
<description>Title of the current PVR channel</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
<channel-type id="showtitle">
|
||||
<item-type>String</item-type>
|
||||
<label>Show Title</label>
|
||||
<description>Title of the current show</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
<channel-type id="album">
|
||||
<item-type>String</item-type>
|
||||
<label>Album</label>
|
||||
<description>Album name of the current song</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
<channel-type id="mediatype">
|
||||
<item-type>String</item-type>
|
||||
<label>Media Type</label>
|
||||
<description>Media type of the current file</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
<channel-type id="genreList">
|
||||
<item-type>String</item-type>
|
||||
<label>Genres</label>
|
||||
<description>Comma-separated list of genres of the current file</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="thumbnail">
|
||||
<item-type>Image</item-type>
|
||||
<label>Thumbnail</label>
|
||||
<description>The current thumbnail</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="fanart">
|
||||
<item-type>Image</item-type>
|
||||
<label>Fanart</label>
|
||||
<description>The current fanart</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="codec" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Codec</label>
|
||||
<description>Codec of currently playing media</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="currenttime">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Current Time</label>
|
||||
<description>Current time of currently playing media</description>
|
||||
<state pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="currenttimepercentage" advanced="true">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Current Time in Percent</label>
|
||||
<description>Current time of currently playing media</description>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="duration">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Duration</label>
|
||||
<description>Length of currently playing media</description>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="id" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Id</label>
|
||||
<description>id in kodi database</description>
|
||||
<state readOnly="true" pattern="%d"/>
|
||||
</channel-type>
|
||||
<channel-type id="enabled">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Enabled</label>
|
||||
<description>enabled</description>
|
||||
</channel-type>
|
||||
<channel-type id="channels" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Channels</label>
|
||||
<description>Channels</description>
|
||||
<state readOnly="true" pattern="%d"/>
|
||||
</channel-type>
|
||||
<channel-type id="width" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Video Width</label>
|
||||
<description>Video Width</description>
|
||||
<state readOnly="true" pattern="%d"/>
|
||||
</channel-type>
|
||||
<channel-type id="height" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Video Height</label>
|
||||
<description>Video Height</description>
|
||||
<state readOnly="true" pattern="%d"/>
|
||||
</channel-type>
|
||||
<channel-type id="index" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Index</label>
|
||||
<description>stream index</description>
|
||||
<state pattern="%d"/>
|
||||
</channel-type>
|
||||
<channel-type id="name" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Name</label>
|
||||
<description>Name</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="uniqueid" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>UniqueID</label>
|
||||
<description>UniqueID</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="language" advanced="false">
|
||||
<item-type>String</item-type>
|
||||
<label>Language</label>
|
||||
<description>stream Language</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="file" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>File</label>
|
||||
<description>file path and name</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="rating" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Rating</label>
|
||||
<description>Rating</description>
|
||||
<state readOnly="true" pattern="%.1f"/>
|
||||
</channel-type>
|
||||
<channel-type id="mpaa" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>MPAA Rating</label>
|
||||
<description>MPAA Rating</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="profile" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Profile</label>
|
||||
<description>Profile of Kodi</description>
|
||||
<state pattern="%s"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.kodi.internal.utils;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.openhab.core.config.core.ConfigConstants;
|
||||
|
||||
/**
|
||||
* Test class for the {@link ByteArrayFileCache} class.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
*/
|
||||
public class ByteArrayFileCacheTest {
|
||||
|
||||
private static final String SERVICE_PID = "org.openhab.binding.kodi";
|
||||
|
||||
private static final File USERDATA_FOLDER = new File(ConfigConstants.getUserDataFolder());
|
||||
private static final File CACHE_FOLDER = new File(USERDATA_FOLDER, ByteArrayFileCache.CACHE_FOLDER_NAME);
|
||||
private static final File SERVICE_CACHE_FOLDER = new File(CACHE_FOLDER, SERVICE_PID);
|
||||
|
||||
private static final String MP3_FILE_NAME = SERVICE_CACHE_FOLDER.getAbsolutePath() + "doorbell.mp3";
|
||||
private static final String TXT_FILE_NAME = SERVICE_CACHE_FOLDER.getAbsolutePath() + "doorbell.txt";
|
||||
|
||||
private static final byte[] EMPTY_BUFFER = new byte[0];
|
||||
|
||||
private ByteArrayFileCache subject;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
subject = new ByteArrayFileCache(SERVICE_PID);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
// delete all files
|
||||
subject.clear();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
// delete all folders
|
||||
SERVICE_CACHE_FOLDER.delete();
|
||||
CACHE_FOLDER.delete();
|
||||
USERDATA_FOLDER.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFileExtension() {
|
||||
assertThat(subject.getFileExtension("/var/log/openhab2/"), is(nullValue()));
|
||||
assertThat(subject.getFileExtension("/var/log/foo.bar/"), is(nullValue()));
|
||||
assertThat(subject.getFileExtension("doorbell.mp3"), is(equalTo("mp3")));
|
||||
assertThat(subject.getFileExtension("/tmp/doorbell.mp3"), is(equalTo("mp3")));
|
||||
assertThat(subject.getFileExtension(MP3_FILE_NAME), is(equalTo("mp3")));
|
||||
assertThat(subject.getFileExtension(TXT_FILE_NAME), is(equalTo("txt")));
|
||||
assertThat(subject.getFileExtension("/var/log/openhab2/.."), is(""));
|
||||
assertThat(subject.getFileExtension(".hidden"), is(equalTo("hidden")));
|
||||
assertThat(subject.getFileExtension("C:\\Program Files (x86)\\java\\bin\\javaw.exe"), is(equalTo("exe")));
|
||||
assertThat(subject.getFileExtension("https://www.youtube.com/watch?v=qYrpPrLY868"), is(nullValue()));
|
||||
assertThat(subject.getFileExtension(
|
||||
"a52fc16cca77ab2bf6abe51e463ce501.jpg?response-content-type=image%2Fjpeg&test=1"), is("jpg"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUniqueFileNameIsUnique() {
|
||||
String mp3UniqueFileName = subject.getUniqueFileName(MP3_FILE_NAME);
|
||||
assertThat(mp3UniqueFileName, is(equalTo(subject.getUniqueFileName(MP3_FILE_NAME))));
|
||||
|
||||
String txtUniqueFileName = subject.getUniqueFileName(TXT_FILE_NAME);
|
||||
assertThat(txtUniqueFileName, is(equalTo(subject.getUniqueFileName(TXT_FILE_NAME))));
|
||||
|
||||
assertThat(mp3UniqueFileName, is(not(equalTo(txtUniqueFileName))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGet() {
|
||||
assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPut() throws IOException {
|
||||
byte[] buffer = readFile();
|
||||
subject.put(MP3_FILE_NAME, buffer);
|
||||
|
||||
assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutIfAbsent() throws IOException {
|
||||
byte[] buffer = readFile();
|
||||
subject.putIfAbsent(MP3_FILE_NAME, buffer);
|
||||
|
||||
assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutIfAbsentAndGet() throws IOException {
|
||||
byte[] buffer = readFile();
|
||||
|
||||
assertThat(subject.putIfAbsentAndGet(MP3_FILE_NAME, buffer), is(equalTo(buffer)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsKey() throws IOException {
|
||||
assertThat(subject.containsKey(MP3_FILE_NAME), is(false));
|
||||
|
||||
subject.put(MP3_FILE_NAME, readFile());
|
||||
|
||||
assertThat(subject.containsKey(MP3_FILE_NAME), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() throws IOException {
|
||||
subject.put(MP3_FILE_NAME, readFile());
|
||||
subject.remove(MP3_FILE_NAME);
|
||||
|
||||
assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear() throws IOException {
|
||||
subject.put(MP3_FILE_NAME, readFile());
|
||||
subject.clear();
|
||||
|
||||
assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearExpiredClearsNothing() throws IOException {
|
||||
byte[] buffer = readFile();
|
||||
subject.put(MP3_FILE_NAME, buffer);
|
||||
subject.clearExpired();
|
||||
|
||||
assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearExpired() throws IOException {
|
||||
subject = new ByteArrayFileCache(SERVICE_PID, 1);
|
||||
|
||||
subject.put(MP3_FILE_NAME, readFile());
|
||||
|
||||
// manipulate time of last use
|
||||
File fileInCache = subject.getUniqueFile(MP3_FILE_NAME);
|
||||
fileInCache.setLastModified(System.currentTimeMillis() - 2 * ByteArrayFileCache.ONE_DAY_IN_MILLIS);
|
||||
|
||||
subject.clearExpired();
|
||||
|
||||
assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER)));
|
||||
}
|
||||
|
||||
private byte[] readFile() throws IOException {
|
||||
byte[] buffer;
|
||||
try (InputStream is = ByteArrayFileCacheTest.class.getResourceAsStream("/sounds/doorbell.mp3")) {
|
||||
buffer = new byte[is.available()];
|
||||
is.read(buffer);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user