[automation] Added Actions for Play and Say which sets the volume (#1854)

* Added Actions for Play and Say which sets the volume

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
Christoph Weitkamp 2020-12-01 22:57:43 +01:00 committed by GitHub
parent c5f133b3e9
commit 0e7563c901
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 51 deletions

View File

@ -16,13 +16,12 @@ import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import java.io.File;
import java.math.BigDecimal;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -42,14 +41,15 @@ import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* This class dynamically provides the Play action type.
* This class dynamically provides the Play and Say action types.
* This is necessary since there is no other way to provide dynamic config param options for module types.
*
* @author Kai Kreuzer - Initial contribution
* @author Simon Kaufmann - added "say" action
* @author Christoph Weitkamp - Added parameter volume
*/
@NonNullByDefault
@Component(immediate = true)
@Component(service = ModuleTypeProvider.class)
public class MediaActionTypeProvider implements ModuleTypeProvider {
private final AudioManager audioManager;
@ -62,43 +62,45 @@ public class MediaActionTypeProvider implements ModuleTypeProvider {
@SuppressWarnings("unchecked")
@Override
public @Nullable ModuleType getModuleType(String UID, @Nullable Locale locale) {
if (PlayActionHandler.TYPE_ID.equals(UID)) {
return getPlayActionType(locale);
} else if (SayActionHandler.TYPE_ID.equals(UID)) {
return getSayActionType(locale);
} else {
return null;
switch (UID) {
case PlayActionHandler.TYPE_ID:
return getPlayActionType(locale);
case SayActionHandler.TYPE_ID:
return getSayActionType(locale);
default:
return null;
}
}
@Override
public Collection<ModuleType> getModuleTypes(@Nullable Locale locale) {
return Stream.of(getPlayActionType(locale), getSayActionType(locale)).collect(Collectors.toList());
return List.of(getPlayActionType(locale), getSayActionType(locale));
}
private ModuleType getPlayActionType(@Nullable Locale locale) {
return new ActionType(PlayActionHandler.TYPE_ID, getConfigPlayDesc(locale), "play a sound",
"Plays a sound file.", null, Visibility.VISIBLE, new ArrayList<>(), new ArrayList<>());
"Plays a sound file. Optionally sets the volume.", null, Visibility.VISIBLE, null, null);
}
private ModuleType getSayActionType(@Nullable Locale locale) {
return new ActionType(SayActionHandler.TYPE_ID, getConfigSayDesc(locale), "say something",
"Speaks a given text through a natural voice.", null, Visibility.VISIBLE, new ArrayList<>(),
new ArrayList<>());
"Speaks a given text through a natural voice. Optionally sets the volume.", null, Visibility.VISIBLE,
null, null);
}
private List<ConfigDescriptionParameter> getConfigPlayDesc(@Nullable Locale locale) {
ConfigDescriptionParameter param1 = ConfigDescriptionParameterBuilder
.create(PlayActionHandler.PARAM_SOUND, Type.TEXT).withRequired(true).withLabel("Sound")
.withDescription("the sound to play").withOptions(getSoundOptions()).withLimitToOptions(true).build();
return List.of(param1, getAudioSinkConfigDescParam(locale));
return List.of(
ConfigDescriptionParameterBuilder.create(PlayActionHandler.PARAM_SOUND, Type.TEXT).withRequired(true)
.withLabel("Sound").withDescription("the sound to play").withOptions(getSoundOptions())
.withLimitToOptions(true).build(),
getAudioSinkConfigDescParam(locale), getVolumeConfigDescParam(locale));
}
private List<ConfigDescriptionParameter> getConfigSayDesc(@Nullable Locale locale) {
ConfigDescriptionParameter param1 = ConfigDescriptionParameterBuilder
.create(SayActionHandler.PARAM_TEXT, Type.TEXT).withRequired(true).withLabel("Text")
.withDescription("the text to speak").build();
return List.of(param1, getAudioSinkConfigDescParam(locale));
return List.of(
ConfigDescriptionParameterBuilder.create(SayActionHandler.PARAM_TEXT, Type.TEXT).withRequired(true)
.withLabel("Text").withDescription("the text to speak").build(),
getAudioSinkConfigDescParam(locale), getVolumeConfigDescParam(locale));
}
private ConfigDescriptionParameter getAudioSinkConfigDescParam(@Nullable Locale locale) {
@ -109,6 +111,14 @@ public class MediaActionTypeProvider implements ModuleTypeProvider {
return param2;
}
private ConfigDescriptionParameter getVolumeConfigDescParam(@Nullable Locale locale) {
ConfigDescriptionParameter param3 = ConfigDescriptionParameterBuilder
.create(SayActionHandler.PARAM_VOLUME, Type.INTEGER).withLabel("Volume")
.withDescription("the volume to use").withMinimum(BigDecimal.ZERO).withMaximum(BigDecimal.valueOf(100))
.withStepSize(BigDecimal.ONE).build();
return param3;
}
/**
* This method creates one option for every file that is found in the sounds directory.
* As a label, the file extension is removed and the string is capitalized.

View File

@ -15,6 +15,8 @@ package org.openhab.core.automation.module.media.internal;
import java.util.Collection;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.audio.AudioManager;
import org.openhab.core.automation.Action;
import org.openhab.core.automation.Module;
@ -22,6 +24,7 @@ import org.openhab.core.automation.handler.BaseModuleHandlerFactory;
import org.openhab.core.automation.handler.ModuleHandler;
import org.openhab.core.automation.handler.ModuleHandlerFactory;
import org.openhab.core.voice.VoiceManager;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
@ -29,13 +32,22 @@ import org.osgi.service.component.annotations.Reference;
/**
*
* @author Kai Kreuzer - Initial contribution
* @author Christoph Weitkamp - Added parameter volume
*/
@NonNullByDefault
@Component(service = ModuleHandlerFactory.class)
public class MediaModuleHandlerFactory extends BaseModuleHandlerFactory {
private static final Collection<String> TYPES = List.of(SayActionHandler.TYPE_ID, PlayActionHandler.TYPE_ID);
private VoiceManager voiceManager;
private AudioManager audioManager;
private final VoiceManager voiceManager;
private final AudioManager audioManager;
@Activate
public MediaModuleHandlerFactory(final @Reference AudioManager audioManager,
final @Reference VoiceManager voiceManager) {
this.audioManager = audioManager;
this.voiceManager = voiceManager;
}
@Override
@Deactivate
@ -49,7 +61,7 @@ public class MediaModuleHandlerFactory extends BaseModuleHandlerFactory {
}
@Override
protected ModuleHandler internalCreate(Module module, String ruleUID) {
protected @Nullable ModuleHandler internalCreate(Module module, String ruleUID) {
if (module instanceof Action) {
switch (module.getTypeUID()) {
case SayActionHandler.TYPE_ID:
@ -62,22 +74,4 @@ public class MediaModuleHandlerFactory extends BaseModuleHandlerFactory {
}
return null;
}
@Reference
protected void setAudioManager(AudioManager audioManager) {
this.audioManager = audioManager;
}
protected void unsetAudioManager(AudioManager audioManager) {
this.audioManager = null;
}
@Reference
protected void setVoiceManager(VoiceManager voiceManager) {
this.voiceManager = voiceManager;
}
protected void unsetVoiceManager(VoiceManager voiceManager) {
this.voiceManager = null;
}
}

View File

@ -12,12 +12,16 @@
*/
package org.openhab.core.automation.module.media.internal;
import java.math.BigDecimal;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.audio.AudioException;
import org.openhab.core.audio.AudioManager;
import org.openhab.core.automation.Action;
import org.openhab.core.automation.handler.BaseActionModuleHandler;
import org.openhab.core.library.types.PercentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,28 +29,39 @@ import org.slf4j.LoggerFactory;
* This is an ModuleHandler implementation for Actions that play a sound file from the file system.
*
* @author Kai Kreuzer - Initial contribution
* @author Christoph Weitkamp - Added parameter volume
*/
@NonNullByDefault
public class PlayActionHandler extends BaseActionModuleHandler {
public static final String TYPE_ID = "media.PlayAction";
public static final String PARAM_SOUND = "sound";
public static final String PARAM_SINK = "sink";
public static final String PARAM_VOLUME = "volume";
private final Logger logger = LoggerFactory.getLogger(PlayActionHandler.class);
private final AudioManager audioManager;
private final String sound;
private final String sink;
private final @Nullable PercentType volume;
public PlayActionHandler(Action module, AudioManager audioManager) {
super(module);
this.audioManager = audioManager;
this.sound = module.getConfiguration().get(PARAM_SOUND).toString();
this.sink = module.getConfiguration().get(PARAM_SINK).toString();
Object volumeParam = module.getConfiguration().get(PARAM_VOLUME);
this.volume = volumeParam instanceof BigDecimal ? new PercentType((BigDecimal) volumeParam) : null;
}
@Override
public Map<String, Object> execute(Map<String, Object> context) {
String sound = module.getConfiguration().get(PARAM_SOUND).toString();
String sink = (String) module.getConfiguration().get(PARAM_SINK);
public @Nullable Map<String, Object> execute(Map<String, Object> context) {
try {
audioManager.playFile(sound, sink);
audioManager.playFile(sound, sink, volume);
} catch (AudioException e) {
logger.error("Error playing sound '{}': {}", sound, e.getMessage());
}

View File

@ -12,35 +12,50 @@
*/
package org.openhab.core.automation.module.media.internal;
import java.math.BigDecimal;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.Action;
import org.openhab.core.automation.handler.BaseActionModuleHandler;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.voice.VoiceManager;
/**
* This is an ModuleHandler implementation for Actions that trigger a TTS output through "say".
*
* @author Kai Kreuzer - Initial contribution
* @author Christoph Weitkamp - Added parameter volume
*/
@NonNullByDefault
public class SayActionHandler extends BaseActionModuleHandler {
public static final String TYPE_ID = "media.SayAction";
public static final String PARAM_TEXT = "text";
public static final String PARAM_SINK = "sink";
public static final String PARAM_VOLUME = "volume";
private final VoiceManager voiceManager;
private final String text;
private final String sink;
private final @Nullable PercentType volume;
public SayActionHandler(Action module, VoiceManager voiceManager) {
super(module);
this.voiceManager = voiceManager;
text = module.getConfiguration().get(PARAM_TEXT).toString();
sink = module.getConfiguration().get(PARAM_SINK).toString();
Object volumeParam = module.getConfiguration().get(PARAM_VOLUME);
this.volume = volumeParam instanceof BigDecimal ? new PercentType((BigDecimal) volumeParam) : null;
}
@Override
public Map<String, Object> execute(Map<String, Object> context) {
String text = module.getConfiguration().get(PARAM_TEXT).toString();
String sink = (String) module.getConfiguration().get(PARAM_SINK);
voiceManager.say(text, null, sink);
public @Nullable Map<String, Object> execute(Map<String, Object> context) {
voiceManager.say(text, null, sink, volume);
return null;
}
}