Skip to content

Commit

Permalink
[Kodi] Added dynamic state descriptions for opening pvr stream channe…
Browse files Browse the repository at this point in the history
…ls (#3207)

* Changed README.md
* Added dynamic state descriptions for opening pvr stream channels
* Added model for Kodi specific entities
* Added NPE safeguard

Signed-off-by: Christoph Weitkamp <[email protected]>
  • Loading branch information
cweitkamp authored and martinvw committed Feb 23, 2018
1 parent 56fc40b commit 4a25680
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ Import-Package:
org.openhab.binding.kodi.handler,
org.osgi.framework,
org.osgi.service.component,
org.osgi.service.component.annotations,
org.slf4j
Service-Component: OSGI-INF/*.xml
13 changes: 8 additions & 5 deletions addons/binding/org.openhab.binding.kodi/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,6 @@ public class KodiBindingConstants {
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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

Expand All @@ -33,10 +35,13 @@
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.RefreshType;
import org.eclipse.smarthome.core.types.State;
import org.eclipse.smarthome.core.types.StateOption;
import org.eclipse.smarthome.core.types.UnDefType;
import org.openhab.binding.kodi.internal.KodiDynamicStateDescriptionProvider;
import org.openhab.binding.kodi.internal.KodiEventListener;
import org.openhab.binding.kodi.internal.config.KodiChannelConfig;
import org.openhab.binding.kodi.internal.config.KodiConfig;
import org.openhab.binding.kodi.internal.model.KodiPVRChannel;
import org.openhab.binding.kodi.internal.protocol.KodiConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -48,7 +53,6 @@
* @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
*
*/
public class KodiHandler extends BaseThingHandler implements KodiEventListener {

Expand All @@ -60,9 +64,13 @@ public class KodiHandler extends BaseThingHandler implements KodiEventListener {

private ScheduledFuture<?> statusUpdaterFuture;

public KodiHandler(@NonNull Thing thing) {
private final KodiDynamicStateDescriptionProvider stateDescriptionProvider;

public KodiHandler(@NonNull Thing thing, KodiDynamicStateDescriptionProvider stateDescriptionProvider) {
super(thing);
connection = new KodiConnection(this);

this.stateDescriptionProvider = stateDescriptionProvider;
}

@Override
Expand Down Expand Up @@ -156,19 +164,15 @@ public void handleCommand(ChannelUID channelUID, Command command) {
break;
case CHANNEL_PVR_OPEN_TV:
if (command instanceof StringType) {
KodiChannelConfig config = getThing().getChannel(channelUID.getId()).getConfiguration()
.as(KodiChannelConfig.class);
playPVRChannel(command, "tv", config);
playPVRChannel(command, PVR_TV, CHANNEL_PVR_OPEN_TV);
updateState(CHANNEL_PVR_OPEN_TV, UnDefType.UNDEF);
} else if (command.equals(RefreshType.REFRESH)) {
updateState(CHANNEL_PVR_OPEN_TV, UnDefType.UNDEF);
}
break;
case CHANNEL_PVR_OPEN_RADIO:
if (command instanceof StringType) {
KodiChannelConfig config = getThing().getChannel(channelUID.getId()).getConfiguration()
.as(KodiChannelConfig.class);
playPVRChannel(command, "radio", config);
playPVRChannel(command, PVR_RADIO, CHANNEL_PVR_OPEN_RADIO);
updateState(CHANNEL_PVR_OPEN_RADIO, UnDefType.UNDEF);
} else if (command.equals(RefreshType.REFRESH)) {
updateState(CHANNEL_PVR_OPEN_RADIO, UnDefType.UNDEF);
Expand Down Expand Up @@ -247,18 +251,25 @@ public void playURI(Command command) {
connection.playURI(command.toString());
}

public void playPVRChannel(final Command command, final String channelType, final KodiChannelConfig config) {
int channelGroupID = connection.getChannelGroupID(channelType, config.getGroup());
if (channelGroupID <= 0) {
logger.warn("Received unknown PVR channel group {}. Using default.", config.getGroup());
channelGroupID = (channelType == "tv") ? 1 : 2;
}
int channelID = connection.getChannelID(channelGroupID, command.toString());
if (channelID > 0) {
connection.playPVRChannel(channelID);
public void playPVRChannel(final Command command, final String pvrChannelType, final String channelId) {
int pvrChannelGroupId = getPVRChannelGroupId(pvrChannelType, channelId);
int pvrChannelId = connection.getPVRChannelId(pvrChannelGroupId, command.toString());
if (pvrChannelId > 0) {
connection.playPVRChannel(pvrChannelId);
} else {
logger.debug("Received unknown PVR channel {}", command.toString());
logger.debug("Received unknown PVR channel '{}'.", command);
}
}

private int getPVRChannelGroupId(final String pvrChannelType, final String channelId) {
KodiChannelConfig config = getThing().getChannel(channelId).getConfiguration().as(KodiChannelConfig.class);
String pvrChannelGroupName = config.getGroup();
int pvrChannelGroupId = connection.getPVRChannelGroupId(pvrChannelType, pvrChannelGroupName);
if (pvrChannelGroupId <= 0) {
logger.debug("Received unknown PVR channel group '{}'. Using default.", pvrChannelGroupName);
pvrChannelGroupId = PVR_TV.equals(pvrChannelType) ? 1 : 2;
}
return pvrChannelGroupId;
}

public void playNotificationSoundURI(Command command) {
Expand All @@ -284,7 +295,10 @@ public void initialize() {
connection.connect(host, getIntConfigParameter(WS_PORT_PARAMETER, 9090), scheduler, getImageBaseUrl());

connectionCheckerFuture = scheduler.scheduleWithFixedDelay(() -> {
if (!connection.checkConnection()) {
if (connection.checkConnection()) {
updatePVRChannelStateDescription(PVR_TV, CHANNEL_PVR_OPEN_TV);
updatePVRChannelStateDescription(PVR_RADIO, CHANNEL_PVR_OPEN_RADIO);
} else {
updateStatus(ThingStatus.OFFLINE);
}
}, 1, 10, TimeUnit.SECONDS);
Expand All @@ -301,6 +315,17 @@ public void initialize() {
}
}

private void updatePVRChannelStateDescription(final String pvrChannelType, final String channelId) {
if (isLinked(channelId)) {
int pvrChannelGroupId = getPVRChannelGroupId(pvrChannelType, channelId);
List<StateOption> options = new ArrayList<>();
for (KodiPVRChannel pvrChannel : connection.getPVRChannels(pvrChannelGroupId)) {
options.add(new StateOption(pvrChannel.getLabel(), pvrChannel.getLabel()));
}
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), channelId), options);
}
}

@Override
public void updateConnectionState(boolean connected) {
if (connected) {
Expand Down Expand Up @@ -422,5 +447,4 @@ private State createImage(RawType image) {
return image;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2018 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.kodi.internal;

import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.thing.Channel;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.type.DynamicStateDescriptionProvider;
import org.eclipse.smarthome.core.types.StateDescription;
import org.eclipse.smarthome.core.types.StateOption;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;

/**
* Dynamic provider of state options while leaving other state description fields as original.
*
* @author Gregory Moyer - Initial contribution
* @author Christoph Weitkamp - Adapted to Kodi binding
*/
@Component(service = { DynamicStateDescriptionProvider.class,
KodiDynamicStateDescriptionProvider.class }, immediate = true)
@NonNullByDefault
public class KodiDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider {
private final Map<ChannelUID, List<StateOption>> channelOptionsMap = new ConcurrentHashMap<>();

public void setStateOptions(ChannelUID channelUID, List<StateOption> options) {
channelOptionsMap.put(channelUID, options);
}

@Override
public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
@Nullable Locale locale) {
List<StateOption> options = channelOptionsMap.get(channel.getUID());
if (options == null) {
return null;
}

if (original != null) {
return new StateDescription(original.getMinimum(), original.getMaximum(), original.getStep(),
original.getPattern(), original.isReadOnly(), options);
}

return new StateDescription(null, null, null, null, false, options);
}

@Deactivate
public void deactivate() {
channelOptionsMap.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
* handlers.
*
* @author Paul Frank - Initial contribution
* @author Christoph Weitkamp - Improvements on channels for opening PVR TV or Radio streams
*/
@Component(service = ThingHandlerFactory.class, immediate = true, configurationPid = "binding.kodi", configurationPolicy = ConfigurationPolicy.OPTIONAL)
public class KodiHandlerFactory extends BaseThingHandlerFactory {
Expand All @@ -52,6 +53,8 @@ public class KodiHandlerFactory extends BaseThingHandlerFactory {

private Map<String, ServiceRegistration<AudioSink>> audioSinkRegistrations = new ConcurrentHashMap<>();

private KodiDynamicStateDescriptionProvider stateDescriptionProvider;

@Override
protected void activate(ComponentContext componentContext) {
super.activate(componentContext);
Expand All @@ -69,7 +72,7 @@ protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

if (thingTypeUID.equals(THING_TYPE_KODI)) {
KodiHandler handler = new KodiHandler(thing);
KodiHandler handler = new KodiHandler(thing, stateDescriptionProvider);

// register the Kodi as an audio sink
KodiAudioSink audioSink = new KodiAudioSink(handler, audioHTTPServer, createCallbackUrl());
Expand Down Expand Up @@ -132,4 +135,12 @@ protected void unsetNetworkAddressService(NetworkAddressService networkAddressSe
this.networkAddressService = null;
}

@Reference
protected void setDynamicStateDescriptionProvider(KodiDynamicStateDescriptionProvider stateDescriptionProvider) {
this.stateDescriptionProvider = stateDescriptionProvider;
}

protected void unsetDynamicStateDescriptionProvider(KodiDynamicStateDescriptionProvider stateDescriptionProvider) {
this.stateDescriptionProvider = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2018 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2018 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2018 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
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;
}
}
Loading

0 comments on commit 4a25680

Please sign in to comment.