mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[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>
This commit is contained in:
parent
f4805eda2b
commit
2e7dde9dda
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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,44 +219,49 @@ 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) {
|
||||
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())) {
|
||||
@ -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,26 +626,29 @@ public class EmotivaProcessorHandler extends BaseThingHandler {
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
EmotivaControlDTO dto = emotivaRequest.createDTO(ohCommand, stateMap.get(channelUID.getId()));
|
||||
localSendingService.send(dto);
|
||||
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);
|
||||
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) {
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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,15 +283,13 @@ 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 + '}';
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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));
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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>")));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user