Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Kodi] Added dynamic state descriptions for opening pvr stream channels #3207

Merged
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FTR: this should not be done like this, see also #3292 & #3294

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor, but noting slight inconsistency in naming. Here it is channelId and getId. Elsewhere it's channelID and getChannelID. I might not even bring this up, except that elsewhere in the code channelId refers to the ESH channelId.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. I think about a renaming.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it and feel that my naming is OK in thus place. But I changed it in the rest of the binding to have a clear line when we talk about an ESH channel or a TV channel.

/**
* 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also channelGroupId is referred to as channelGroupID elsewhere in the code (e.g. the method getChannelGroupID).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

this.channelGroupId = channelGroupId;
}

public String getChannelType() {
return channelType;
}

public void setChannelType(final String channelType) {
this.channelType = channelType;
}
}
Loading