Skip to content

Commit

Permalink
[emotiva] Fix missing data in source channels (openhab#17365)
Browse files Browse the repository at this point in the history
* [emotiva] Fixes issue with missing data in source channels.

Signed-off-by: Espen Fossen <espenaf@junta.no>
  • Loading branch information
espenaf authored and matchews committed Oct 18, 2024
1 parent c47e456 commit 2eaa640
Show file tree
Hide file tree
Showing 16 changed files with 427 additions and 225 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@

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;
Expand Down Expand Up @@ -69,11 +67,11 @@ private static int clamp(int volumeInPercentage, int min, int max) {
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) {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,15 @@ private void listenUnhandledInterruption() throws InterruptedException {
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");
}
continue;
}
receiveNotifyFailures = 0; // message successfully received, unset failure counter

handleReceivedData(answer, receivedData, localListener);
handleReceivedData(answer, localListener);
} catch (Exception e) {
listenerNotifyActive = false;

Expand All @@ -190,12 +188,11 @@ private void listenUnhandledInterruption() throws InterruptedException {
}
}

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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;
Expand Down Expand Up @@ -122,12 +123,11 @@ public void connect(Consumer<EmotivaUdpResponse> listener, boolean logNotThrowEx
}
}

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);
Expand Down Expand Up @@ -158,7 +158,7 @@ public void send(EmotivaControlDTO dto) throws IOException {
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)));
}

Expand All @@ -171,7 +171,7 @@ public void sendUpdate(EmotivaSubscriptionTags[] tags, EmotivaConfiguration conf
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)));
}

Expand All @@ -196,14 +196,13 @@ public void send(String msg) throws IOException {
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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void setThingHandler(ThingHandler handler) {
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@

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;

/**
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

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;

/**
Expand All @@ -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) {
Expand All @@ -50,7 +46,11 @@ public EmotivaUnsubscribeDTO(EmotivaSubscriptionTags tag) {
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());
}
}
Loading

0 comments on commit 2eaa640

Please sign in to comment.