* [emotiva] Fixes issue with missing data in source channels.
Signed-off-by: Espen Fossen <espenaf@junta.no>
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;
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;
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) {
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;
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;
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;
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;
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
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
}
}
/**
- * 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) {
- 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);
- if (lastKeepAliveMessageTimestamp.isBefore(deviceGoneGracePeriod)) {
- logger.debug(
- "Last KeepAlive message received '{}', over grace-period by '{}', consider '{}' gone, setting OFFLINE and disposing",
- lastKeepAliveMessageTimestamp,
- Duration.between(lastKeepAliveMessageTimestamp, deviceGoneGracePeriod),
- thing.getThingTypeUID());
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
- "@text/message.processor.connection.error.keep-alive");
- // Connection lost, avoid sending unsubscription messages
- udpSenderActive = false;
- disconnect();
- scheduleConnectRetry(retryConnectInMinutes);
+ 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);
+ if (lastKeepAliveMessageTimestamp.isBefore(deviceGoneGracePeriod)) {
+ logger.debug(
+ "Last KeepAlive message received '{}', over grace-period by '{}', consider '{}' gone, setting OFFLINE and disposing",
+ lastKeepAliveMessageTimestamp,
+ Duration.between(lastKeepAliveMessageTimestamp, deviceGoneGracePeriod),
+ thing.getThingTypeUID());
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ "@text/message.processor.connection.error.keep-alive");
+ // Connection lost, avoid sending unsubscription messages
+ udpSenderActive = false;
+ disconnect();
+ scheduleConnectRetry(retryConnectInMinutes);
+ }
}
}
} else if (ThingStatus.OFFLINE.equals(getThing().getStatusInfo().getStatus())) {
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());
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())) {
// 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
+ 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,
}
}
- 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) {
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",
}
} else {
try {
- EmotivaControlDTO dto = emotivaRequest.createDTO(ohCommand, stateMap.get(channelUID.getId()));
- 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));
- } 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));
- } else if (ohCommand instanceof QuantityType<?> value) {
- updateChannelState(CHANNEL_ZONE2_VOLUME, volumeDecibelToPercentage(value.toString()));
- }
- } else if (ohCommand instanceof OnOffType value) {
- if (value.equals(OnOffType.ON) && emotivaRequest.getOnCommand().equals(power_on)) {
- localSendingService.sendUpdate(EmotivaSubscriptionTags.speakerChannels(), config);
+ 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));
+ } 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));
+ } else if (ohCommand instanceof QuantityType<?> value) {
+ updateChannelState(CHANNEL_ZONE2_VOLUME, volumeDecibelToPercentage(value.toString()));
+ }
+ } else if (ohCommand instanceof OnOffType value) {
+ if (value.equals(OnOffType.ON) && emotivaRequest.getOnCommand().equals(power_on)) {
+ localSendingService.sendUpdate(EmotivaSubscriptionTags.speakerChannels(), config);
+ }
}
}
} catch (InterruptedIOException e) {
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);
}
}
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();
}
}
--- /dev/null
+/**
+ * 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);
+ }
+}
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");
}
}
receiveNotifyFailures = 0; // message successfully received, unset failure counter
- handleReceivedData(answer, receivedData, localListener);
+ handleReceivedData(answer, localListener);
} catch (Exception e) {
listenerNotifyActive = false;
}
}
- 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);
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;
}
}
- 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);
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)));
}
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)));
}
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");
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;
}
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;
/**
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);
}
}
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;
/**
public EmotivaUnsubscribeDTO() {
}
- public EmotivaUnsubscribeDTO(List<EmotivaCommandDTO> commands) {
- this.commands = commands;
- }
-
public EmotivaUnsubscribeDTO(EmotivaSubscriptionTags[] emotivaCommandTypes) {
List<EmotivaCommandDTO> list = new ArrayList<>();
for (EmotivaSubscriptionTags commandType : emotivaCommandTypes) {
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());
}
}
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;
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)) {
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())) {
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);
private EmotivaControlDTO matchToCommandMap(Command ohCommand, String mapName) {
if (ohCommand instanceof StringType value) {
- Map<EmotivaControlCommands, String> commandMap = commandMaps.get(mapName);
- if (commandMap != null) {
- for (EmotivaControlCommands command : commandMap.keySet()) {
- String map = commandMap.get(command);
- if (map != null && map.equals(value.toString())) {
- return EmotivaControlDTO.create(EmotivaControlCommands.matchToInput(command.toString()));
- } else if (command.name().equalsIgnoreCase(value.toString())) {
- return EmotivaControlDTO.create(command);
- }
+ Map<EmotivaControlCommands, String> commandMap = state.getCommandMap(mapName);
+ for (EmotivaControlCommands command : commandMap.keySet()) {
+ String map = commandMap.get(command);
+ if (map != null && map.equals(value.toString())) {
+ return EmotivaControlDTO.create(EmotivaControlCommands.matchToInput(command.toString()));
+ } else if (command.name().equalsIgnoreCase(value.toString())) {
+ return EmotivaControlDTO.create(command);
}
}
}
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 + '}';
}
}
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() {
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;
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));
--- /dev/null
+/**
+ * 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"));
+ }
+}
}
@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
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;
@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>")));
}
}
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;
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;
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() {
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");
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"));
@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));