added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user