[emotiva] Fix missing data in source channels (#17365)

* [emotiva] Fixes issue with missing data in source channels.

Signed-off-by: Espen Fossen <espenaf@junta.no>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Espen Fossen 2024-09-28 15:28:35 +02:00 committed by Ciprian Pascu
parent 37aa25d0a6
commit 3b8ddf81d1
16 changed files with 425 additions and 223 deletions

View File

@ -86,6 +86,9 @@ public class EmotivaBindingConstants {
public static final int DEFAULT_TRIM_MAX_DECIBEL = 12;
public static final String MAP_SOURCES_MAIN_ZONE = "sources";
public static final String MAP_SOURCES_ZONE_2 = "zone2-sources";
public static final String MAP_TUNER_CHANNELS = "tuner-channel";
public static final String MAP_TUNER_BANDS = "tuner-bands";
public static final String MAP_MODES = "modes";
/** Miscellaneous Constants **/
public static final int PROTOCOL_V3_LEVEL_MULTIPLIER = 2;

View File

@ -14,8 +14,6 @@ package org.openhab.binding.emotiva.internal;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlRequest;
@ -69,11 +67,11 @@ public class EmotivaCommandHelper {
return Math.min(Math.max(Double.valueOf(volumeInPercentage).intValue(), min), max);
}
public static EmotivaControlRequest channelToControlRequest(String id,
Map<String, Map<EmotivaControlCommands, String>> commandMaps, EmotivaProtocolVersion protocolVersion) {
public static EmotivaControlRequest channelToControlRequest(String id, EmotivaProcessorState state,
EmotivaProtocolVersion protocolVersion) {
EmotivaSubscriptionTags channelSubscription = EmotivaSubscriptionTags.fromChannelUID(id);
EmotivaControlCommands channelFromCommand = OHChannelToEmotivaCommand.fromChannelUID(id);
return new EmotivaControlRequest(id, channelSubscription, channelFromCommand, commandMaps, protocolVersion);
return new EmotivaControlRequest(id, channelSubscription, channelFromCommand, state, protocolVersion);
}
public static String getMenuPanelRowLabel(int row) {

View File

@ -20,17 +20,12 @@ import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.getMenuP
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.updateProgress;
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.volumeDecibelToPercentage;
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.volumePercentageToDecibel;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.band_am;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.band_fm;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.channel_1;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.none;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.power_on;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.STRING;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaPropertyStatus.NOT_VALID;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.protocolFromConfig;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.noSubscriptionToChannel;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_band;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_channel;
import java.io.IOException;
import java.io.InterruptedIOException;
@ -40,13 +35,10 @@ import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -74,6 +66,7 @@ import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
import org.openhab.binding.emotiva.internal.protocol.EmotivaUdpResponse;
import org.openhab.binding.emotiva.internal.protocol.EmotivaXmlUtils;
import org.openhab.core.common.NamedThreadFactory;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
@ -102,21 +95,17 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(EmotivaProcessorHandler.class);
private final Map<String, State> stateMap = Collections.synchronizedMap(new HashMap<>());
private final EmotivaConfiguration config;
/**
* Emotiva devices have trouble with too many subscriptions in same request, so subscriptions are dividing into
* those general group channels, and the rest.
* groups.
*/
private final EmotivaSubscriptionTags[] generalSubscription = EmotivaSubscriptionTags.generalChannels();
private final EmotivaSubscriptionTags[] nonGeneralSubscriptions = EmotivaSubscriptionTags.nonGeneralChannels();
private final List<EmotivaSubscriptionTags> generalSubscription = EmotivaSubscriptionTags.channels("general");
private final List<EmotivaSubscriptionTags> mainZoneSubscriptions = EmotivaSubscriptionTags.channels("main-zone");
private final List<EmotivaSubscriptionTags> zone2Subscriptions = EmotivaSubscriptionTags.channels("zone2");
private final EnumMap<EmotivaControlCommands, String> sourcesMainZone;
private final EnumMap<EmotivaControlCommands, String> sourcesZone2;
private final EnumMap<EmotivaSubscriptionTags, String> modes;
private final Map<String, Map<EmotivaControlCommands, String>> commandMaps = new ConcurrentHashMap<>();
private final EmotivaProcessorState state = new EmotivaProcessorState();
private final EmotivaTranslationProvider i18nProvider;
private @Nullable ScheduledFuture<?> pollingJob;
@ -141,41 +130,6 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
this.i18nProvider = i18nProvider;
this.config = getConfigAs(EmotivaConfiguration.class);
this.retryConnectInMinutes = config.retryConnectInMinutes;
sourcesMainZone = new EnumMap<>(EmotivaControlCommands.class);
commandMaps.put(MAP_SOURCES_MAIN_ZONE, sourcesMainZone);
sourcesZone2 = new EnumMap<>(EmotivaControlCommands.class);
commandMaps.put(MAP_SOURCES_ZONE_2, sourcesZone2);
EnumMap<EmotivaControlCommands, String> channels = new EnumMap<>(
Map.ofEntries(Map.entry(channel_1, channel_1.getLabel()),
Map.entry(EmotivaControlCommands.channel_2, EmotivaControlCommands.channel_2.getLabel()),
Map.entry(EmotivaControlCommands.channel_3, EmotivaControlCommands.channel_3.getLabel()),
Map.entry(EmotivaControlCommands.channel_4, EmotivaControlCommands.channel_4.getLabel()),
Map.entry(EmotivaControlCommands.channel_5, EmotivaControlCommands.channel_5.getLabel()),
Map.entry(EmotivaControlCommands.channel_6, EmotivaControlCommands.channel_6.getLabel()),
Map.entry(EmotivaControlCommands.channel_7, EmotivaControlCommands.channel_7.getLabel()),
Map.entry(EmotivaControlCommands.channel_8, EmotivaControlCommands.channel_8.getLabel()),
Map.entry(EmotivaControlCommands.channel_9, EmotivaControlCommands.channel_9.getLabel()),
Map.entry(EmotivaControlCommands.channel_10, EmotivaControlCommands.channel_10.getLabel()),
Map.entry(EmotivaControlCommands.channel_11, EmotivaControlCommands.channel_11.getLabel()),
Map.entry(EmotivaControlCommands.channel_12, EmotivaControlCommands.channel_12.getLabel()),
Map.entry(EmotivaControlCommands.channel_13, EmotivaControlCommands.channel_13.getLabel()),
Map.entry(EmotivaControlCommands.channel_14, EmotivaControlCommands.channel_14.getLabel()),
Map.entry(EmotivaControlCommands.channel_15, EmotivaControlCommands.channel_15.getLabel()),
Map.entry(EmotivaControlCommands.channel_16, EmotivaControlCommands.channel_16.getLabel()),
Map.entry(EmotivaControlCommands.channel_17, EmotivaControlCommands.channel_17.getLabel()),
Map.entry(EmotivaControlCommands.channel_18, EmotivaControlCommands.channel_18.getLabel()),
Map.entry(EmotivaControlCommands.channel_19, EmotivaControlCommands.channel_19.getLabel()),
Map.entry(EmotivaControlCommands.channel_20, EmotivaControlCommands.channel_20.getLabel())));
commandMaps.put(tuner_channel.getEmotivaName(), channels);
EnumMap<EmotivaControlCommands, String> bands = new EnumMap<>(
Map.of(band_am, band_am.getLabel(), band_fm, band_fm.getLabel()));
commandMaps.put(tuner_band.getEmotivaName(), bands);
modes = new EnumMap<>(EmotivaSubscriptionTags.class);
}
@Override
@ -222,7 +176,8 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
try {
logger.debug("Connection attempt '{}'", attempt);
sendConnector.sendSubscription(generalSubscription, config);
sendConnector.sendSubscription(nonGeneralSubscriptions, config);
sendConnector.sendSubscription(mainZoneSubscriptions, config);
sendConnector.sendSubscription(zone2Subscriptions, config);
} catch (IOException e) {
// network or socket failure, also wait 2 sec and try again
}
@ -264,29 +219,33 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
}
/**
* Starts a polling job for connection to th device, adds the
* Starts a polling job for connection to the device, adds the
* {@link EmotivaBindingConstants#DEFAULT_KEEP_ALIVE_IN_MILLISECONDS} as a time buffer for checking, to avoid
* flapping state or minor network issues.
*/
private void startPollingKeepAlive() {
final ScheduledFuture<?> localRefreshJob = this.pollingJob;
if (localRefreshJob == null || localRefreshJob.isCancelled()) {
logger.debug("Start polling");
int delay = stateMap.get(EmotivaSubscriptionTags.keepAlive.name()) != null
&& stateMap.get(EmotivaSubscriptionTags.keepAlive.name()) instanceof Number keepAlive
? keepAlive.intValue()
: config.keepAlive;
pollingJob = scheduler.scheduleWithFixedDelay(this::checkKeepAliveTimestamp,
delay + DEFAULT_KEEP_ALIVE_IN_MILLISECONDS, delay + DEFAULT_KEEP_ALIVE_IN_MILLISECONDS,
Number keepAliveConfig = state.getChannel(EmotivaSubscriptionTags.keepAlive)
.filter(channel -> channel instanceof Number).map(keepAlive -> (Number) keepAlive)
.orElse(new DecimalType(config.keepAlive));
// noinspection ConstantConditions
long delay = keepAliveConfig == null
? DEFAULT_KEEP_ALIVE_IN_MILLISECONDS + DEFAULT_KEEP_ALIVE_IN_MILLISECONDS
: keepAliveConfig.longValue() + DEFAULT_KEEP_ALIVE_IN_MILLISECONDS;
pollingJob = scheduler.scheduleWithFixedDelay(this::checkKeepAliveTimestamp, delay, delay,
TimeUnit.MILLISECONDS);
logger.debug("Started scheduled job to check connection to device, with an {}ms internal", delay);
}
}
private void checkKeepAliveTimestamp() {
if (ThingStatus.ONLINE.equals(getThing().getStatusInfo().getStatus())) {
State state = stateMap.get(LAST_SEEN_STATE_NAME);
if (state instanceof Number value) {
Optional<State> lastSeenState = state.getChannel(LAST_SEEN_STATE_NAME);
if (lastSeenState.isPresent()) {
if (lastSeenState.get() instanceof Number value) {
Instant lastKeepAliveMessageTimestamp = Instant.ofEpochSecond(value.longValue());
Instant deviceGoneGracePeriod = Instant.now().minus(config.keepAlive, ChronoUnit.MILLIS)
.minus(DEFAULT_KEEP_ALIVE_CONSIDERED_LOST_IN_MILLISECONDS, ChronoUnit.MILLIS);
@ -304,6 +263,7 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
scheduleConnectRetry(retryConnectInMinutes);
}
}
}
} else if (ThingStatus.OFFLINE.equals(getThing().getStatusInfo().getStatus())) {
logger.debug("Keep alive pool job, '{}' is '{}'", getThing().getThingTypeUID(),
getThing().getStatusInfo().getStatus());
@ -325,9 +285,10 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
return;
}
if (object instanceof EmotivaAckDTO answerDto) {
if (object instanceof EmotivaAckDTO) {
// Currently not supported to revert a failed command update, just used for logging for now.
logger.trace("Processing received '{}' with '{}'", EmotivaAckDTO.class.getSimpleName(), answerDto);
logger.trace("Processing received '{}' with '{}'", EmotivaAckDTO.class.getSimpleName(),
emotivaUdpResponse.answer());
} else if (object instanceof EmotivaBarNotifyWrapper answerDto) {
logger.trace("Processing received '{}' with '{}'", EmotivaBarNotifyWrapper.class.getSimpleName(),
emotivaUdpResponse.answer());
@ -364,13 +325,15 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
logger.trace("Processing received '{}' with '{}'", EmotivaSubscriptionResponse.class.getSimpleName(),
emotivaUdpResponse.answer());
// Populates static input sources, except input
sourcesMainZone.putAll(EmotivaControlCommands.getCommandsFromType(EmotivaCommandType.SOURCE_MAIN_ZONE));
sourcesMainZone.remove(EmotivaControlCommands.input);
commandMaps.put(MAP_SOURCES_MAIN_ZONE, sourcesMainZone);
EnumMap<EmotivaControlCommands, String> sourceMainZone = EmotivaControlCommands
.getCommandsFromType(EmotivaCommandType.SOURCE_MAIN_ZONE);
sourceMainZone.remove(EmotivaControlCommands.input);
state.setSourcesMainZone(sourceMainZone);
sourcesZone2.putAll(EmotivaControlCommands.getCommandsFromType(EmotivaCommandType.SOURCE_ZONE2));
sourcesZone2.remove(EmotivaControlCommands.zone2_input);
commandMaps.put(MAP_SOURCES_ZONE_2, sourcesZone2);
EnumMap<EmotivaControlCommands, String> sourcesZone2 = EmotivaControlCommands
.getCommandsFromType(EmotivaCommandType.SOURCE_ZONE2);
sourcesZone2.remove(EmotivaControlCommands.input);
state.setSourcesZone2(sourcesZone2);
if (answerDto.getProperties() == null) {
for (EmotivaNotifyDTO dto : xmlUtils.unmarshallToNotification(answerDto.getTags())) {
@ -528,11 +491,9 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
// Add/Update user assigned name for inputs
if (subscriptionTag.getChannel().startsWith(CHANNEL_INPUT1.substring(0, CHANNEL_INPUT1.indexOf("-") + 1))
&& "true".equals(visible)) {
logger.debug("Adding '{}' to dynamic source input list", trimmedValue);
sourcesMainZone.put(EmotivaControlCommands.matchToInput(subscriptionTag.name()), trimmedValue);
commandMaps.put(MAP_SOURCES_MAIN_ZONE, sourcesMainZone);
logger.debug("sources list is now {}", sourcesMainZone.size());
state.updateSourcesMainZone(EmotivaControlCommands.matchToInput(subscriptionTag.name()), trimmedValue);
logger.debug("Adding '{}' to dynamic source input list, map is now {}", trimmedValue,
state.getSourcesMainZone());
}
// Add/Update audio modes
@ -541,7 +502,7 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
+ subscriptionTag.getChannel().substring(subscriptionTag.getChannel().indexOf("_") + 1));
logger.debug("Adding '{} ({})' from channel '{}' to dynamic mode list", trimmedValue, modeName,
subscriptionTag.getChannel());
modes.put(EmotivaSubscriptionTags.fromChannelUID(subscriptionTag.getChannel()), trimmedValue);
state.updateModes(EmotivaSubscriptionTags.fromChannelUID(subscriptionTag.getChannel()), trimmedValue);
}
findChannelDatatypeAndUpdateChannel(subscriptionTag.getChannel(), trimmedValue,
@ -630,10 +591,10 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
}
}
private void updateChannelState(String channelID, State state) {
stateMap.put(channelID, state);
logger.trace("Updating channel '{}' with '{}'", channelID, state);
updateState(channelID, state);
private void updateChannelState(String channelID, State channelState) {
state.updateChannel(channelID, channelState);
logger.trace("Updating channel '{}' with '{}'", channelID, channelState);
updateState(channelID, channelState);
}
private void updateVolumeChannels(String value, String muteChannel, String volumeChannel, String volumeDbChannel) {
@ -651,10 +612,10 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
EmotivaUdpSendingService localSendingService = sendingService;
if (localSendingService != null) {
EmotivaControlRequest emotivaRequest = channelToControlRequest(channelUID.getId(), commandMaps,
EmotivaControlRequest emotivaRequest = channelToControlRequest(channelUID.getId(), state,
protocolFromConfig(config.protocolVersion));
if (ohCommand instanceof RefreshType) {
stateMap.remove(channelUID.getId());
state.removeChannel(channelUID.getId());
if (emotivaRequest.getDefaultCommand().equals(none)) {
logger.debug("Found controlCommand 'none' for request '{}' from channel '{}' with RefreshType",
@ -665,20 +626,22 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
}
} else {
try {
EmotivaControlDTO dto = emotivaRequest.createDTO(ohCommand, stateMap.get(channelUID.getId()));
Optional<State> channel = state.getChannel(channelUID.getId());
if (channel.isPresent()) {
EmotivaControlDTO dto = emotivaRequest.createDTO(ohCommand, channel.get());
localSendingService.send(dto);
if (emotivaRequest.getName().equals(EmotivaControlCommands.volume.name())) {
if (ohCommand instanceof PercentType value) {
updateChannelState(CHANNEL_MAIN_VOLUME_DB,
QuantityType.valueOf(volumePercentageToDecibel(value.intValue()), Units.DECIBEL));
updateChannelState(CHANNEL_MAIN_VOLUME_DB, QuantityType
.valueOf(volumePercentageToDecibel(value.intValue()), Units.DECIBEL));
} else if (ohCommand instanceof QuantityType<?> value) {
updateChannelState(CHANNEL_MAIN_VOLUME, volumeDecibelToPercentage(value.toString()));
}
} else if (emotivaRequest.getName().equals(EmotivaControlCommands.zone2_volume.name())) {
if (ohCommand instanceof PercentType value) {
updateChannelState(CHANNEL_ZONE2_VOLUME_DB,
QuantityType.valueOf(volumePercentageToDecibel(value.intValue()), Units.DECIBEL));
updateChannelState(CHANNEL_ZONE2_VOLUME_DB, QuantityType
.valueOf(volumePercentageToDecibel(value.intValue()), Units.DECIBEL));
} else if (ohCommand instanceof QuantityType<?> value) {
updateChannelState(CHANNEL_ZONE2_VOLUME, volumeDecibelToPercentage(value.toString()));
}
@ -687,6 +650,7 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
localSendingService.sendUpdate(EmotivaSubscriptionTags.speakerChannels(), config);
}
}
}
} catch (InterruptedIOException e) {
logger.debug("Interrupted during updating state for channel: '{}:{}:{}'", channelUID.getId(),
emotivaRequest.getName(), emotivaRequest.getDataType(), e);
@ -714,7 +678,8 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
try {
// Unsubscribe before disconnect
localSendingService.sendUnsubscribe(generalSubscription);
localSendingService.sendUnsubscribe(nonGeneralSubscriptions);
localSendingService.sendUnsubscribe(mainZoneSubscriptions);
localSendingService.sendUnsubscribe(zone2Subscriptions);
} catch (IOException e) {
logger.debug("Failed to unsubscribe for '{}'", config.ipAddress, e);
}
@ -772,14 +737,14 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
}
public EnumMap<EmotivaControlCommands, String> getSourcesMainZone() {
return sourcesMainZone;
return state.getSourcesMainZone();
}
public EnumMap<EmotivaControlCommands, String> getSourcesZone2() {
return sourcesZone2;
return state.getSourcesZone2();
}
public EnumMap<EmotivaSubscriptionTags, String> getModes() {
return modes;
return state.getModes();
}
}

View File

@ -0,0 +1,149 @@
/**
* Copyright (c) 2010-2024 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.emotiva.internal;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_SOURCES_MAIN_ZONE;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_SOURCES_ZONE_2;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_TUNER_BANDS;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_TUNER_CHANNELS;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.band_am;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.band_fm;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.channel_1;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
import org.openhab.core.types.State;
/**
* Holds state for Emotiva Processor.
*
* @author Espen Fossen - Initial contribution
*/
@NonNullByDefault
public class EmotivaProcessorState {
private final Map<String, State> channelStateMap;
private EnumMap<EmotivaControlCommands, String> sourcesMainZone;
private EnumMap<EmotivaControlCommands, String> sourcesZone2;
private final EnumMap<EmotivaSubscriptionTags, String> modes;
private EnumMap<EmotivaControlCommands, String> tunerChannels = new EnumMap<>(
Map.ofEntries(Map.entry(channel_1, channel_1.getLabel()),
Map.entry(EmotivaControlCommands.channel_2, EmotivaControlCommands.channel_2.getLabel()),
Map.entry(EmotivaControlCommands.channel_3, EmotivaControlCommands.channel_3.getLabel()),
Map.entry(EmotivaControlCommands.channel_4, EmotivaControlCommands.channel_4.getLabel()),
Map.entry(EmotivaControlCommands.channel_5, EmotivaControlCommands.channel_5.getLabel()),
Map.entry(EmotivaControlCommands.channel_6, EmotivaControlCommands.channel_6.getLabel()),
Map.entry(EmotivaControlCommands.channel_7, EmotivaControlCommands.channel_7.getLabel()),
Map.entry(EmotivaControlCommands.channel_8, EmotivaControlCommands.channel_8.getLabel()),
Map.entry(EmotivaControlCommands.channel_9, EmotivaControlCommands.channel_9.getLabel()),
Map.entry(EmotivaControlCommands.channel_10, EmotivaControlCommands.channel_10.getLabel()),
Map.entry(EmotivaControlCommands.channel_11, EmotivaControlCommands.channel_11.getLabel()),
Map.entry(EmotivaControlCommands.channel_12, EmotivaControlCommands.channel_12.getLabel()),
Map.entry(EmotivaControlCommands.channel_13, EmotivaControlCommands.channel_13.getLabel()),
Map.entry(EmotivaControlCommands.channel_14, EmotivaControlCommands.channel_14.getLabel()),
Map.entry(EmotivaControlCommands.channel_15, EmotivaControlCommands.channel_15.getLabel()),
Map.entry(EmotivaControlCommands.channel_16, EmotivaControlCommands.channel_16.getLabel()),
Map.entry(EmotivaControlCommands.channel_17, EmotivaControlCommands.channel_17.getLabel()),
Map.entry(EmotivaControlCommands.channel_18, EmotivaControlCommands.channel_18.getLabel()),
Map.entry(EmotivaControlCommands.channel_19, EmotivaControlCommands.channel_19.getLabel()),
Map.entry(EmotivaControlCommands.channel_20, EmotivaControlCommands.channel_20.getLabel())));
private EnumMap<EmotivaControlCommands, String> tunerBands = new EnumMap<>(
Map.of(band_am, band_am.getLabel(), band_fm, band_fm.getLabel()));
public EmotivaProcessorState() {
channelStateMap = Collections.synchronizedMap(new HashMap<>());
sourcesMainZone = new EnumMap<>(EmotivaControlCommands.class);
sourcesZone2 = new EnumMap<>(EmotivaControlCommands.class);
modes = new EnumMap<>(EmotivaSubscriptionTags.class);
}
public Optional<State> getChannel(String channelName) {
if (channelStateMap.containsKey(channelName)) {
return Optional.ofNullable(channelStateMap.get(channelName));
} else {
return Optional.empty();
}
}
public Optional<State> getChannel(EmotivaSubscriptionTags channelTagName) {
if (channelStateMap.containsKey(channelTagName.name())) {
return Optional.ofNullable(channelStateMap.get(channelTagName.name()));
} else {
return Optional.empty();
}
}
public Map<EmotivaControlCommands, String> getCommandMap(String mapName) {
return switch (mapName) {
case MAP_SOURCES_MAIN_ZONE -> sourcesMainZone;
case MAP_SOURCES_ZONE_2 -> sourcesZone2;
case MAP_TUNER_CHANNELS -> tunerChannels;
case MAP_TUNER_BANDS -> tunerBands;
default -> new EnumMap<>(EmotivaControlCommands.class);
};
}
public EnumMap<EmotivaControlCommands, String> getSourcesMainZone() {
return sourcesMainZone;
}
public EnumMap<EmotivaControlCommands, String> getSourcesZone2() {
return sourcesZone2;
}
public EnumMap<EmotivaSubscriptionTags, String> getModes() {
return modes;
}
public void setChannels(EnumMap<EmotivaControlCommands, String> map) {
tunerChannels = map;
}
public void setSourcesMainZone(EnumMap<EmotivaControlCommands, String> map) {
sourcesMainZone = map;
}
public void setSourcesZone2(EnumMap<EmotivaControlCommands, String> map) {
sourcesZone2 = map;
}
public void setTunerBands(EnumMap<EmotivaControlCommands, String> map) {
tunerBands = map;
}
public void updateChannel(String channel, State state) {
channelStateMap.put(channel, state);
}
public void updateSourcesMainZone(EmotivaControlCommands command, String value) {
sourcesMainZone.put(command, value);
}
public void updateModes(EmotivaSubscriptionTags tag, String value) {
modes.put(tag, value);
}
public void removeChannel(String channel) {
channelStateMap.remove(channel);
}
}

View File

@ -156,9 +156,7 @@ public class EmotivaUdpReceivingService {
localReceivingSocket.receive(answer); // receive packet (blocking call)
listenerNotifyActive = false;
final byte[] receivedData = Arrays.copyOfRange(answer.getData(), 0, answer.getLength() - 1);
if (receivedData.length == 0) {
if (Arrays.copyOfRange(answer.getData(), 0, answer.getLength() - 1).length == 0) {
if (isConnected()) {
logger.debug("Nothing received, this may happen during shutdown or some unknown error");
}
@ -166,7 +164,7 @@ public class EmotivaUdpReceivingService {
}
receiveNotifyFailures = 0; // message successfully received, unset failure counter
handleReceivedData(answer, receivedData, localListener);
handleReceivedData(answer, localListener);
} catch (Exception e) {
listenerNotifyActive = false;
@ -190,12 +188,11 @@ public class EmotivaUdpReceivingService {
}
}
private void handleReceivedData(DatagramPacket answer, byte[] receivedData,
Consumer<EmotivaUdpResponse> localListener) {
private void handleReceivedData(DatagramPacket answer, Consumer<EmotivaUdpResponse> localListener) {
// log & notify listener in new thread (so that listener loop continues immediately)
executorService.execute(() -> {
if (answer.getAddress() != null && answer.getLength() > 0) {
logger.trace("Received data on port '{}': {}", answer.getPort(), receivedData);
logger.trace("Received data on port '{}'", answer.getPort());
EmotivaUdpResponse emotivaUdpResponse = new EmotivaUdpResponse(
new String(answer.getData(), 0, answer.getLength()), answer.getAddress().getHostAddress());
localListener.accept(emotivaUdpResponse);

View File

@ -21,6 +21,7 @@ import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
@ -122,12 +123,11 @@ public class EmotivaUdpSendingService {
}
}
private void handleReceivedData(DatagramPacket answer, byte[] receivedData,
Consumer<EmotivaUdpResponse> localListener) {
private void handleReceivedData(DatagramPacket answer, Consumer<EmotivaUdpResponse> localListener) {
// log & notify listener in new thread (so that listener loop continues immediately)
executorService.execute(() -> {
if (answer.getAddress() != null && answer.getLength() > 0) {
logger.trace("Received data on port '{}': {}", answer.getPort(), receivedData);
logger.trace("Received data on port '{}'", answer.getPort());
EmotivaUdpResponse emotivaUdpResponse = new EmotivaUdpResponse(
new String(answer.getData(), 0, answer.getLength()), answer.getAddress().getHostAddress());
localListener.accept(emotivaUdpResponse);
@ -158,7 +158,7 @@ public class EmotivaUdpSendingService {
send(emotivaXmlUtils.marshallJAXBElementObjects(dto));
}
public void sendSubscription(EmotivaSubscriptionTags[] tags, EmotivaConfiguration config) throws IOException {
public void sendSubscription(List<EmotivaSubscriptionTags> tags, EmotivaConfiguration config) throws IOException {
send(emotivaXmlUtils.marshallJAXBElementObjects(new EmotivaSubscriptionRequest(tags, config.protocolVersion)));
}
@ -171,7 +171,7 @@ public class EmotivaUdpSendingService {
send(emotivaXmlUtils.marshallJAXBElementObjects(new EmotivaUpdateRequest(tags, config.protocolVersion)));
}
public void sendUnsubscribe(EmotivaSubscriptionTags[] defaultCommand) throws IOException {
public void sendUnsubscribe(List<EmotivaSubscriptionTags> defaultCommand) throws IOException {
send(emotivaXmlUtils.marshallJAXBElementObjects(new EmotivaUnsubscribeDTO(defaultCommand)));
}
@ -196,14 +196,13 @@ public class EmotivaUdpSendingService {
logger.debug("Sending successful");
localDatagramSocket.receive(answer);
final byte[] receivedData = Arrays.copyOfRange(answer.getData(), 0, answer.getLength() - 1);
if (receivedData.length == 0) {
if (Arrays.copyOfRange(answer.getData(), 0, answer.getLength() - 1).length == 0) {
logger.debug("Nothing received, this may happen during shutdown or some unknown error");
}
if (localListener != null) {
handleReceivedData(answer, receivedData, localListener);
handleReceivedData(answer, localListener);
}
} else {
throw new SocketException("Datagram Socket closed or not initialized");

View File

@ -65,7 +65,7 @@ public class InputStateOptionProvider extends BaseDynamicStateDescriptionProvide
public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
@Nullable Locale locale) {
ChannelTypeUID typeUID = channel.getChannelTypeUID();
if (typeUID == null || !BINDING_ID.equals(typeUID.getBindingId()) || original == null) {
if (typeUID == null || !BINDING_ID.equals(typeUID.getBindingId())) {
return null;
}

View File

@ -14,13 +14,12 @@ package org.openhab.binding.emotiva.internal.dto;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.PROTOCOL_V2;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
/**
@ -38,26 +37,18 @@ public class EmotivaSubscriptionRequest extends AbstractJAXBElementDTO {
public EmotivaSubscriptionRequest() {
}
public EmotivaSubscriptionRequest(List<EmotivaCommandDTO> commands, String protocol) {
public EmotivaSubscriptionRequest(List<EmotivaSubscriptionTags> emotivaCommandTypes, String protocol) {
this.protocol = protocol;
this.commands = commands;
}
public EmotivaSubscriptionRequest(EmotivaSubscriptionTags[] emotivaCommandTypes, String protocol) {
this.protocol = protocol;
List<EmotivaCommandDTO> list = new ArrayList<>();
for (EmotivaSubscriptionTags commandType : emotivaCommandTypes) {
list.add(EmotivaCommandDTO.fromTypeWithAck(commandType));
}
this.commands = list;
this.commands = emotivaCommandTypes.stream().map(EmotivaCommandDTO::fromTypeWithAck)
.collect(Collectors.toList());
}
public EmotivaSubscriptionRequest(EmotivaSubscriptionTags tag) {
this.commands = List.of(EmotivaCommandDTO.fromTypeWithAck(tag));
}
public EmotivaSubscriptionRequest(EmotivaControlCommands commandType, String protocol) {
public EmotivaSubscriptionRequest(EmotivaCommandDTO commandType, String protocol) {
this.protocol = protocol;
this.commands = List.of(EmotivaCommandDTO.fromTypeWithAck(commandType));
this.commands = List.of(commandType);
}
}

View File

@ -14,10 +14,10 @@ package org.openhab.binding.emotiva.internal.dto;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.xml.bind.annotation.XmlRootElement;
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
/**
@ -34,10 +34,6 @@ public class EmotivaUnsubscribeDTO extends AbstractJAXBElementDTO {
public EmotivaUnsubscribeDTO() {
}
public EmotivaUnsubscribeDTO(List<EmotivaCommandDTO> commands) {
this.commands = commands;
}
public EmotivaUnsubscribeDTO(EmotivaSubscriptionTags[] emotivaCommandTypes) {
List<EmotivaCommandDTO> list = new ArrayList<>();
for (EmotivaSubscriptionTags commandType : emotivaCommandTypes) {
@ -50,7 +46,11 @@ public class EmotivaUnsubscribeDTO extends AbstractJAXBElementDTO {
this.commands = List.of(EmotivaCommandDTO.fromType(tag));
}
public EmotivaUnsubscribeDTO(EmotivaControlCommands commandType) {
this.commands = List.of(EmotivaCommandDTO.fromType(commandType));
public EmotivaUnsubscribeDTO(EmotivaCommandDTO commandType) {
this.commands = List.of(commandType);
}
public EmotivaUnsubscribeDTO(List<EmotivaSubscriptionTags> commandType) {
this.commands = commandType.stream().map(EmotivaCommandDTO::fromTypeWithAck).collect(Collectors.toList());
}
}

View File

@ -17,13 +17,12 @@ import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.clamp;
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.volumePercentageToDecibel;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.*;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.FREQUENCY_HERTZ;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_band;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_channel;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.emotiva.internal.EmotivaProcessorState;
import org.openhab.binding.emotiva.internal.dto.EmotivaControlDTO;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
@ -53,11 +52,11 @@ public class EmotivaControlRequest {
private final EmotivaControlCommands downCommand;
private double maxValue;
private double minValue;
private final Map<String, Map<EmotivaControlCommands, String>> commandMaps;
private final EmotivaProcessorState state;
private final EmotivaProtocolVersion protocolVersion;
public EmotivaControlRequest(String channel, EmotivaSubscriptionTags channelSubscription,
EmotivaControlCommands controlCommand, Map<String, Map<EmotivaControlCommands, String>> commandMaps,
EmotivaControlCommands controlCommand, EmotivaProcessorState state,
EmotivaProtocolVersion protocolVersion) {
if (channelSubscription.equals(EmotivaSubscriptionTags.unknown)) {
if (controlCommand.equals(EmotivaControlCommands.none)) {
@ -94,7 +93,7 @@ public class EmotivaControlRequest {
this.name = defaultCommand.name();
this.dataType = defaultCommand.getDataType();
this.channel = channel;
this.commandMaps = commandMaps;
this.state = state;
this.protocolVersion = protocolVersion;
if (name.equals(EmotivaControlCommands.volume.name())
|| name.equals(EmotivaControlCommands.zone2_volume.name())) {
@ -155,10 +154,10 @@ public class EmotivaControlRequest {
case NONE -> {
switch (channel) {
case CHANNEL_TUNER_BAND -> {
return matchToCommandMap(ohCommand, tuner_band.getEmotivaName());
return matchToCommandMap(ohCommand, MAP_TUNER_BANDS);
}
case CHANNEL_TUNER_CHANNEL_SELECT -> {
return matchToCommandMap(ohCommand, tuner_channel.getEmotivaName());
return matchToCommandMap(ohCommand, MAP_TUNER_CHANNELS);
}
case CHANNEL_SOURCE -> {
return matchToCommandMap(ohCommand, MAP_SOURCES_MAIN_ZONE);
@ -284,8 +283,7 @@ public class EmotivaControlRequest {
private EmotivaControlDTO matchToCommandMap(Command ohCommand, String mapName) {
if (ohCommand instanceof StringType value) {
Map<EmotivaControlCommands, String> commandMap = commandMaps.get(mapName);
if (commandMap != null) {
Map<EmotivaControlCommands, String> commandMap = state.getCommandMap(mapName);
for (EmotivaControlCommands command : commandMap.keySet()) {
String map = commandMap.get(command);
if (map != null && map.equals(value.toString())) {
@ -295,7 +293,6 @@ public class EmotivaControlRequest {
}
}
}
}
return EmotivaControlDTO.create(EmotivaControlCommands.none);
}
@ -469,7 +466,7 @@ public class EmotivaControlRequest {
return "EmotivaControlRequest{" + "name='" + name + '\'' + ", dataType=" + dataType + ", channel='" + channel
+ '\'' + ", defaultCommand=" + defaultCommand + ", setCommand=" + setCommand + ", onCommand="
+ onCommand + ", offCommand=" + offCommand + ", upCommand=" + upCommand + ", downCommand=" + downCommand
+ ", maxValue=" + maxValue + ", minValue=" + minValue + ", commandMaps=" + commandMaps
+ ", protocolVersion=" + protocolVersion + '}';
+ ", maxValue=" + maxValue + ", minValue=" + minValue + ", state=" + state + ", protocolVersion="
+ protocolVersion + '}';
}
}

View File

@ -132,24 +132,14 @@ public enum EmotivaSubscriptionTags {
return EmotivaSubscriptionTags.unknown;
}
public static EmotivaSubscriptionTags[] generalChannels() {
public static List<EmotivaSubscriptionTags> channels(String zonePrefix) {
List<EmotivaSubscriptionTags> tags = new ArrayList<>();
for (EmotivaSubscriptionTags value : values()) {
if (value.channel.startsWith("general")) {
if (value.channel.startsWith(zonePrefix)) {
tags.add(value);
}
}
return tags.toArray(new EmotivaSubscriptionTags[0]);
}
public static EmotivaSubscriptionTags[] nonGeneralChannels() {
List<EmotivaSubscriptionTags> tags = new ArrayList<>();
for (EmotivaSubscriptionTags value : values()) {
if (!value.channel.startsWith("general")) {
tags.add(value);
}
}
return tags.toArray(new EmotivaSubscriptionTags[0]);
return tags;
}
public static EmotivaSubscriptionTags[] speakerChannels() {

View File

@ -32,8 +32,6 @@ import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.ON_O
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.PROTOCOL_V2;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.PROTOCOL_V3;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
@ -90,9 +88,9 @@ class EmotivaCommandHelperTest {
void testChannelToControlRequest(String channel, String name, EmotivaDataType emotivaDataType,
EmotivaControlCommands defaultCommand, EmotivaControlCommands onCommand, EmotivaControlCommands offCommand,
EmotivaControlCommands setCommand, EmotivaProtocolVersion version, double min, double max) {
final Map<String, Map<EmotivaControlCommands, String>> commandMaps = new ConcurrentHashMap<>();
EmotivaProcessorState state = new EmotivaProcessorState();
EmotivaControlRequest surround = EmotivaCommandHelper.channelToControlRequest(channel, commandMaps, version);
EmotivaControlRequest surround = EmotivaCommandHelper.channelToControlRequest(channel, state, version);
assertThat(surround.getName(), is(name));
assertThat(surround.getChannel(), is(channel));
assertThat(surround.getDataType(), is(emotivaDataType));

View File

@ -0,0 +1,127 @@
/**
* Copyright (c) 2010-2024 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.emotiva.internal;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_MODES;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_SOURCES_MAIN_ZONE;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_SOURCES_ZONE_2;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_TUNER_BANDS;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.MAP_TUNER_CHANNELS;
import java.util.EnumMap;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
import org.openhab.core.library.types.DecimalType;
/**
* Unit tests for the EmotivaProcessorHandlerState.
*
* @author Espen Fossen - Initial contribution
*/
@NonNullByDefault
class EmotivaProcessorStateTest {
@Test
void initialState() {
var state = new EmotivaProcessorState();
assertThat(state.getSourcesMainZone(), not(nullValue()));
assertThat(state.getSourcesMainZone().size(), is(0));
assertThat(state.getSourcesZone2(), not(nullValue()));
assertThat(state.getSourcesZone2().size(), is(0));
assertThat(state.getModes(), not(nullValue()));
assertThat(state.getModes().size(), is(0));
assertThat(state.getChannel(EmotivaSubscriptionTags.keepAlive), is(Optional.empty()));
assertThat(state.getCommandMap(MAP_SOURCES_MAIN_ZONE).size(), is(0));
assertThat(state.getCommandMap(MAP_SOURCES_ZONE_2).size(), is(0));
assertThat(state.getCommandMap(MAP_TUNER_CHANNELS).size(), is(20));
assertThat(state.getCommandMap(MAP_TUNER_BANDS).size(), is(2));
assertThat(state.getCommandMap(MAP_MODES).size(), is(0));
}
@Test
void updateAndRemoveChannel() {
var state = new EmotivaProcessorState();
assertThat(state.getChannel(EmotivaSubscriptionTags.keepAlive.getChannel()), is(Optional.empty()));
state.updateChannel(EmotivaSubscriptionTags.keepAlive.getChannel(), new DecimalType(10));
assertThat(state.getChannel(EmotivaSubscriptionTags.keepAlive.getChannel()),
is(Optional.of(new DecimalType(10))));
state.removeChannel(EmotivaSubscriptionTags.keepAlive.getChannel());
assertThat(state.getChannel(EmotivaSubscriptionTags.keepAlive.getChannel()), is(Optional.empty()));
}
@Test
void replaceSourcesMap() {
var state = new EmotivaProcessorState();
assertThat(state.getSourcesMainZone(), not(nullValue()));
assertThat(state.getSourcesMainZone().size(), is(0));
EnumMap<EmotivaControlCommands, String> sourcesMap = new EnumMap<>(EmotivaControlCommands.class);
sourcesMap.put(EmotivaControlCommands.source_1, "HDMI1");
state.setSourcesMainZone(sourcesMap);
assertThat(state.getSourcesMainZone(), not(nullValue()));
assertThat(state.getSourcesMainZone().size(), is(1));
assertThat(state.getSourcesMainZone().get(EmotivaControlCommands.source_1), is("HDMI1"));
}
@Test
void updateModes() {
var state = new EmotivaProcessorState();
state.updateModes(EmotivaSubscriptionTags.mode_auto, "Auto");
assertThat(state.getModes(), not(nullValue()));
assertThat(state.getModes().size(), is(1));
assertThat(state.getModes().get(EmotivaSubscriptionTags.mode_auto), is("Auto"));
state.updateModes(EmotivaSubscriptionTags.mode_auto, "Custom Label");
assertThat(state.getModes(), not(nullValue()));
assertThat(state.getModes().size(), is(1));
assertThat(state.getModes().get(EmotivaSubscriptionTags.mode_auto), is("Custom Label"));
}
@Test
void updateSourcesMap() {
var state = new EmotivaProcessorState();
EnumMap<EmotivaControlCommands, String> sourcesMap = new EnumMap<>(EmotivaControlCommands.class);
sourcesMap.put(EmotivaControlCommands.source_1, "HDMI1");
state.setSourcesMainZone(sourcesMap);
assertThat(state.getSourcesMainZone(), not(nullValue()));
assertThat(state.getSourcesMainZone().size(), is(1));
assertThat(state.getSourcesMainZone().get(EmotivaControlCommands.source_1), is("HDMI1"));
state.updateSourcesMainZone(EmotivaControlCommands.source_1, "SHIELD");
assertThat(state.getSourcesMainZone(), not(nullValue()));
assertThat(state.getSourcesMainZone().size(), is(1));
assertThat(state.getSourcesMainZone().get(EmotivaControlCommands.source_1), is("SHIELD"));
}
}

View File

@ -49,20 +49,16 @@ class EmotivaSubscriptionRequestTest extends AbstractDTOTestBase {
}
@Test
void marshallWithTwoSubscriptionsNoAck() {
EmotivaCommandDTO command1 = new EmotivaCommandDTO(EmotivaControlCommands.volume, "10", "yes");
EmotivaCommandDTO command2 = new EmotivaCommandDTO(EmotivaControlCommands.power_off);
void marshallWithSubscriptionNoAck() {
EmotivaCommandDTO command = new EmotivaCommandDTO(EmotivaControlCommands.volume, "10", "yes");
EmotivaSubscriptionRequest dto = new EmotivaSubscriptionRequest(List.of(command1, command2),
PROTOCOL_V2.value());
EmotivaSubscriptionRequest dto = new EmotivaSubscriptionRequest(command, PROTOCOL_V2.value());
String xmlString = xmlUtils.marshallJAXBElementObjects(dto);
assertThat(xmlString, containsString("<emotivaSubscription protocol=\"2.0\">"));
assertThat(xmlString, containsString("<volume value=\"10\" ack=\"yes\" />"));
assertThat(xmlString, containsString("<power_off />"));
assertThat(xmlString, containsString("</emotivaSubscription>"));
assertThat(xmlString, not(containsString("<volume>")));
assertThat(xmlString, not(containsString("<command>")));
}
@Test

View File

@ -16,8 +16,6 @@ import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_TUNER_RDS;
import java.util.List;
import javax.xml.bind.JAXBException;
import org.eclipse.jdt.annotation.NonNullByDefault;
@ -50,16 +48,13 @@ class EmotivaUnsubscriptionTest extends AbstractDTOTestBase {
@Test
void marshallWithTwoUnsubscriptions() {
EmotivaCommandDTO command1 = new EmotivaCommandDTO(EmotivaControlCommands.volume);
EmotivaCommandDTO command2 = new EmotivaCommandDTO(EmotivaControlCommands.power_off);
EmotivaUnsubscribeDTO dto = new EmotivaUnsubscribeDTO(List.of(command1, command2));
EmotivaUnsubscribeDTO dto = new EmotivaUnsubscribeDTO(command1);
String xmlString = xmlUtils.marshallJAXBElementObjects(dto);
assertThat(xmlString, containsString("<emotivaUnsubscribe>"));
assertThat(xmlString, containsString("<volume />"));
assertThat(xmlString, containsString("<power_off />"));
assertThat(xmlString, containsString("</emotivaUnsubscribe>"));
assertThat(xmlString, not(containsString("<volume>")));
assertThat(xmlString, not(containsString("<command>")));
}
}

View File

@ -17,15 +17,12 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.*;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.*;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_band;
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_channel;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
@ -33,8 +30,8 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.openhab.binding.emotiva.internal.EmotivaBindingConstants;
import org.openhab.binding.emotiva.internal.EmotivaCommandHelper;
import org.openhab.binding.emotiva.internal.EmotivaProcessorState;
import org.openhab.binding.emotiva.internal.dto.EmotivaControlDTO;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
@ -220,7 +217,7 @@ class EmotivaControlRequestTest {
private static final EnumMap<EmotivaControlCommands, String> RADIO_BAND_MAP = new EnumMap<>(
EmotivaControlCommands.class);
private static final Map<String, State> STATE_MAP = Collections.synchronizedMap(new HashMap<>());
private static final Map<String, Map<EmotivaControlCommands, String>> COMMAND_MAPS = new ConcurrentHashMap<>();
private static final EmotivaProcessorState state = new EmotivaProcessorState();
@BeforeAll
static void beforeAll() {
@ -228,7 +225,7 @@ class EmotivaControlRequestTest {
MAP_SOURCES_MAIN_ZONE.put(source_2, "SHIELD");
MAP_SOURCES_MAIN_ZONE.put(hdmi1, "HDMI1");
MAP_SOURCES_MAIN_ZONE.put(coax1, "Coax 1");
COMMAND_MAPS.put(EmotivaBindingConstants.MAP_SOURCES_MAIN_ZONE, MAP_SOURCES_MAIN_ZONE);
state.setSourcesMainZone(MAP_SOURCES_MAIN_ZONE);
MAP_SOURCES_ZONE_2.put(source_1, "HDMI 1");
MAP_SOURCES_ZONE_2.put(source_2, "SHIELD");
@ -236,16 +233,16 @@ class EmotivaControlRequestTest {
MAP_SOURCES_ZONE_2.put(zone2_coax1, "Coax 1");
MAP_SOURCES_ZONE_2.put(zone2_ARC, "Audio Return Channel");
MAP_SOURCES_ZONE_2.put(zone2_follow_main, "Follow Main");
COMMAND_MAPS.put(EmotivaBindingConstants.MAP_SOURCES_ZONE_2, MAP_SOURCES_ZONE_2);
state.setSourcesZone2(MAP_SOURCES_ZONE_2);
CHANNEL_MAP.put(channel_1, "Channel 1");
CHANNEL_MAP.put(channel_2, "Channel 2");
CHANNEL_MAP.put(channel_3, "My Radio Channel");
COMMAND_MAPS.put(tuner_channel.getEmotivaName(), CHANNEL_MAP);
state.setChannels(CHANNEL_MAP);
RADIO_BAND_MAP.put(band_am, "AM");
RADIO_BAND_MAP.put(band_fm, "FM");
COMMAND_MAPS.put(tuner_band.getEmotivaName(), RADIO_BAND_MAP);
state.setTunerBands(RADIO_BAND_MAP);
STATE_MAP.put(CHANNEL_TREBLE, new DecimalType(-3));
STATE_MAP.put(CHANNEL_TUNER_CHANNEL, new StringType("FM 87.50MHz"));
@ -256,7 +253,7 @@ class EmotivaControlRequestTest {
@MethodSource("channelToDTOs")
void createDTO(String channel, Command ohValue, EmotivaControlCommands controlCommand,
EmotivaProtocolVersion protocolVersion, String requestValue) {
EmotivaControlRequest controlRequest = EmotivaCommandHelper.channelToControlRequest(channel, COMMAND_MAPS,
EmotivaControlRequest controlRequest = EmotivaCommandHelper.channelToControlRequest(channel, state,
protocolVersion);
EmotivaControlDTO dto = controlRequest.createDTO(ohValue, STATE_MAP.get(channel));