From 55af4c8a8b16148562d81b3c2ad0c90b16ca22ee Mon Sep 17 00:00:00 2001 From: J-N-K Date: Wed, 5 Jun 2024 21:01:02 +0200 Subject: [PATCH] [tuya] Fix compiler warnings and simplify code (#563) Signed-off-by: Jan N. Klug --- bundles/org.smarthomej.binding.tuya/README.md | 4 + .../internal/config/ChannelConfiguration.java | 2 +- .../internal/handler/TuyaDeviceHandler.java | 101 +++++----- .../tuya/internal/local/dto/IrCode.java | 2 +- .../binding/tuya/internal/util/IrUtils.java | 172 ++++++------------ 5 files changed, 105 insertions(+), 176 deletions(-) diff --git a/bundles/org.smarthomej.binding.tuya/README.md b/bundles/org.smarthomej.binding.tuya/README.md index 2ce05814a5..c79816ad0d 100644 --- a/bundles/org.smarthomej.binding.tuya/README.md +++ b/bundles/org.smarthomej.binding.tuya/README.md @@ -122,6 +122,7 @@ It contains a comma-separated list of command options for this channel (e.g. `wh ### Type `ir-code` IR code types: + + `Tuya DIY-mode` - use study codes from real remotes. Make a virtual remote control in DIY, learn virtual buttons. @@ -139,6 +140,7 @@ IR code types: + `Samsung` - IR Code in Samsung format. **Additional options:** + * `Active Listening` - Device will be always in learning mode. After send command with key code device stays in the learning mode * `DP Study Key` - **Advanced**. DP number for study key. Uses for receive key code in learning mode. Change it own your @@ -150,11 +152,13 @@ If linked item received a command with `Key Code` (Code Library Parameter) then #### How to use IR Code in NEC format. Example, from Tasmota you need to use **_Data_** parameter, it can be with or without **_0x_** + ```json {"Time": "2023-07-05T18:17:42", "IrReceived": {"Protocol": "NEC", "Bits": 32, "Data": "0x10EFD02F"}} ``` Another example, use **_hex_** parameter + ```json { "type": "nec", "uint32": 284151855, "address": 8, "data": 11, "hex": "10EFD02F" } ``` diff --git a/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/config/ChannelConfiguration.java b/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/config/ChannelConfiguration.java index 5b216403e7..cb4b33b73b 100644 --- a/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/config/ChannelConfiguration.java +++ b/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/config/ChannelConfiguration.java @@ -32,5 +32,5 @@ public class ChannelConfiguration { public int irSendDelay = 300; public int irCodeType = 0; public String irType = ""; - public Boolean activeListen = Boolean.FALSE; + public boolean activeListen = false; } diff --git a/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/handler/TuyaDeviceHandler.java b/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/handler/TuyaDeviceHandler.java index 1dff20a348..1321762ab7 100644 --- a/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/handler/TuyaDeviceHandler.java +++ b/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/handler/TuyaDeviceHandler.java @@ -148,17 +148,16 @@ public void processDeviceStatus(Map deviceStatus) { private void processChannelStatus(Integer dp, Object value) { String channelId = dpToChannelId.get(dp); if (channelId != null) { - ChannelConfiguration configuration = channelIdToConfiguration.get(channelId); + ChannelConfiguration channelConfiguration = channelIdToConfiguration.get(channelId); ChannelTypeUID channelTypeUID = channelIdToChannelTypeUID.get(channelId); - if (configuration == null || channelTypeUID == null) { + if (channelConfiguration == null || channelTypeUID == null) { logger.warn("Could not find configuration or type for channel '{}' in thing '{}'", channelId, thing.getUID()); return; } - ChannelConfiguration channelConfiguration = channelIdToConfiguration.get(channelId); - if (channelConfiguration != null && Boolean.FALSE.equals(deviceStatusCache.get(channelConfiguration.dp2))) { + if (Boolean.FALSE.equals(deviceStatusCache.get(channelConfiguration.dp2))) { // skip update if the channel is off! return; } @@ -173,7 +172,8 @@ private void processChannelStatus(Integer dp, Object value) { return; } else if (Double.class.isAssignableFrom(value.getClass()) && CHANNEL_TYPE_UID_DIMMER.equals(channelTypeUID)) { - updateState(channelId, ConversionUtil.brightnessDecode((double) value, 0, configuration.max)); + updateState(channelId, + ConversionUtil.brightnessDecode((double) value, 0, channelConfiguration.max)); return; } else if (Double.class.isAssignableFrom(value.getClass()) && CHANNEL_TYPE_UID_NUMBER.equals(channelTypeUID)) { @@ -186,11 +186,11 @@ private void processChannelStatus(Integer dp, Object value) { updateState(channelId, OnOffType.from((boolean) value)); return; } else if (value instanceof String && CHANNEL_TYPE_UID_IR_CODE.equals(channelTypeUID)) { - if (configuration.dp == 2) { - String decoded = convertBase64Code(configuration, (String) value); + if (channelConfiguration.dp == 2) { + String decoded = convertBase64Code(channelConfiguration, (String) value); logger.info("thing {} received ir code: {}", thing.getUID(), decoded); updateState(channelId, new StringType(decoded)); - irStartLearning(configuration.activeListen); + irStartLearning(channelConfiguration.activeListen); } return; } @@ -228,14 +228,9 @@ public void connectionStatus(boolean status) { } // start learning code if thing is online and presents 'ir-code' channel - this.getThing().getChannels().stream() - .filter(channel -> CHANNEL_TYPE_UID_IR_CODE.equals(channel.getChannelTypeUID())).findFirst() - .ifPresent(channel -> { - ChannelConfiguration config = channelIdToConfiguration.get(channel.getChannelTypeUID()); - if (config != null) { - irStartLearning(config.activeListen); - } - }); + channelIdToChannelTypeUID.entrySet().stream().filter(e -> CHANNEL_TYPE_UID_IR_CODE.equals(e.getValue())) + .map(Map.Entry::getKey).findAny().map(channelIdToConfiguration::get) + .ifPresent(irCodeChannelConfig -> irStartLearning(irCodeChannelConfig.activeListen)); } else { updateStatus(ThingStatus.OFFLINE); ScheduledFuture pollingJob = this.pollingJob; @@ -250,7 +245,9 @@ public void connectionStatus(boolean status) { if (tuyaDevice != null && !disposing && (reconnectFuture == null || reconnectFuture.isDone())) { this.reconnectFuture = scheduler.schedule(this::connectDevice, 5000, TimeUnit.MILLISECONDS); } - irStopLearning(); + if (channelIdToChannelTypeUID.containsValue(CHANNEL_TYPE_UID_IR_CODE)) { + irStopLearning(); + } } } @@ -338,29 +335,34 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } else if (CHANNEL_TYPE_UID_IR_CODE.equals(channelTypeUID)) { if (command instanceof StringType) { - if (configuration.irType.equals("base64")) { - commandRequest.put(1, "study_key"); - commandRequest.put(7, command.toString()); - } else if (configuration.irType.equals("tuya-head")) { - if (configuration.irCode != null && !configuration.irCode.isEmpty()) { - commandRequest.put(1, "send_ir"); - commandRequest.put(3, configuration.irCode); - commandRequest.put(4, command.toString()); - commandRequest.put(10, configuration.irSendDelay); - commandRequest.put(13, configuration.irCodeType); - } else { - logger.warn("irCode is not set for channel {}", channelUID); + switch (configuration.irType) { + case "base64" -> { + commandRequest.put(1, "study_key"); + commandRequest.put(7, command.toString()); + } + case "tuya-head" -> { + if (!configuration.irCode.isBlank()) { + commandRequest.put(1, "send_ir"); + commandRequest.put(3, configuration.irCode); + commandRequest.put(4, command.toString()); + commandRequest.put(10, configuration.irSendDelay); + commandRequest.put(13, configuration.irCodeType); + } else { + logger.warn("irCode is not set for channel {}", channelUID); + } + } + case "nec" -> { + long code = convertHexCode(command.toString()); + String base64Code = IrUtils.necToBase64(code); + commandRequest.put(1, "study_key"); + commandRequest.put(7, base64Code); + } + case "samsung" -> { + long code = convertHexCode(command.toString()); + String base64Code = IrUtils.samsungToBase64(code); + commandRequest.put(1, "study_key"); + commandRequest.put(7, base64Code); } - } else if (configuration.irType.equals("nec")) { - long code = convertHexCode(command.toString()); - String base64Code = IrUtils.necToBase64(code); - commandRequest.put(1, "study_key"); - commandRequest.put(7, base64Code); - } else if (configuration.irType.equals("samsung")) { - long code = convertHexCode(command.toString()); - String base64Code = IrUtils.samsungToBase64(code); - commandRequest.put(1, "study_key"); - commandRequest.put(7, base64Code); } irStopLearning(); } @@ -610,7 +612,7 @@ private String convertBase64Code(ChannelConfiguration channelConfig, String enco } else { if (encoded.length() > 68) { decoded = IrUtils.base64ToNec(encoded); - if (decoded == null || decoded.isEmpty()) { + if (decoded.isEmpty()) { decoded = IrUtils.base64ToSamsung(encoded); } IrCode code = Objects.requireNonNull(gson.fromJson(decoded, IrCode.class)); @@ -620,28 +622,19 @@ private String convertBase64Code(ChannelConfiguration channelConfig, String enco } } } catch (JsonSyntaxException e) { - logger.error("Incorrect json response: {}", e.getMessage()); + logger.warn("Incorrect json response: {}", e.getMessage()); decoded = encoded; - } catch (NullPointerException e) { - logger.error("unable decode key code'{}', reason: {}", decoded, e.getMessage()); + } catch (RuntimeException e) { + logger.warn("Unable decode key code'{}', reason: {}", decoded, e.getMessage()); } return decoded; } - private void finishStudyCode() { - Map commandRequest = new HashMap<>(); - commandRequest.put(1, "study_exit"); - TuyaDevice tuyaDevice = this.tuyaDevice; - if (!commandRequest.isEmpty() && tuyaDevice != null) { - tuyaDevice.set(commandRequest); - } - } - private void repeatStudyCode() { Map commandRequest = new HashMap<>(); commandRequest.put(1, "study"); TuyaDevice tuyaDevice = this.tuyaDevice; - if (!commandRequest.isEmpty() && tuyaDevice != null) { + if (tuyaDevice != null) { tuyaDevice.set(commandRequest); } } @@ -655,7 +648,7 @@ private void irStopLearning() { } } - private void irStartLearning(Boolean available) { + private void irStartLearning(boolean available) { irStopLearning(); if (available) { logger.debug("[tuya:ir-controller] start ir learning"); diff --git a/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/local/dto/IrCode.java b/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/local/dto/IrCode.java index 83ced8e9ac..3b1cf497de 100644 --- a/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/local/dto/IrCode.java +++ b/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/local/dto/IrCode.java @@ -12,7 +12,7 @@ */ package org.smarthomej.binding.tuya.internal.local.dto; -import org.eclipse.jdt.annotation.*; +import org.eclipse.jdt.annotation.NonNullByDefault; /** * The {@link IrCode} represents the IR code decoded messages sent by Tuya devices diff --git a/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/util/IrUtils.java b/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/util/IrUtils.java index b786a85ef6..be08741624 100644 --- a/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/util/IrUtils.java +++ b/bundles/org.smarthomej.binding.tuya/src/main/java/org/smarthomej/binding/tuya/internal/util/IrUtils.java @@ -17,6 +17,7 @@ import java.util.Base64; import java.util.List; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,6 +28,7 @@ * * @author Dmitry Pyatykh - Initial contribution */ +@NonNullByDefault public class IrUtils { private static final Logger logger = LoggerFactory.getLogger(IrUtils.class); @@ -40,15 +42,14 @@ private IrUtils() { * @return the nec-format code */ public static String base64ToNec(String base64Code) { - String result = null; List pulses = base64ToPulse(base64Code); - if (pulses != null && !pulses.isEmpty()) { + if (!pulses.isEmpty()) { List res = pulsesToNec(pulses); - if (res != null && !res.isEmpty()) { - result = res.get(0); + if (!res.isEmpty()) { + return res.get(0); } } - return result; + throw new IllegalArgumentException("No pulses found or conversion result is empty."); } /** @@ -58,15 +59,14 @@ public static String base64ToNec(String base64Code) { * @return the samsung-format code */ public static String base64ToSamsung(String base64Code) { - String result = null; List pulses = base64ToPulse(base64Code); - if (pulses != null && !pulses.isEmpty()) { + if (!pulses.isEmpty()) { List res = pulsesToSamsung(pulses); - if (res != null && !res.isEmpty()) { - result = res.get(0); + if (!res.isEmpty()) { + return res.get(0); } } - return result; + throw new IllegalArgumentException("No pulses found or conversion result is empty."); } private static List base64ToPulse(String base64Code) { @@ -88,78 +88,46 @@ private static List base64ToPulse(String base64Code) { } } } catch (ArrayIndexOutOfBoundsException e) { - logger.error("Failed to convert base64 key code to pulses: {}", e.getMessage()); + logger.warn("Failed to convert base64 key code to pulses: {}", e.getMessage()); } return pulses; } - private static List pulsesToWidthEncoded(List pulses, Integer startMark, Integer startSpace, - Integer pulseThreshold, Integer spaceThreshold) { + private static List pulsesToWidthEncoded(List pulses, Integer startMark) { List ret = new ArrayList<>(); if (pulses.size() < 68) { - return null; + throw new IllegalArgumentException("Not enough pulses"); } - if (pulseThreshold == null && spaceThreshold == null) { - return null; + while (pulses.size() >= 68 && (pulses.get(0) < (startMark * 0.75) || pulses.get(0) > (startMark * 1.25))) { + pulses.remove(0); } - if (startMark != null) { - while (pulses.size() >= 68 && (pulses.get(0) < (startMark * 0.75) || pulses.get(0) > (startMark * 1.25))) { - pulses.remove(0); + while (pulses.size() >= 68) { + if (pulses.get(0) < startMark * 0.75 || pulses.get(0) > startMark * 1.25) { + throw new IllegalArgumentException( + "Pulse length is less than 3/4 startMark or more than 5/4 startMark"); } - while (pulses.size() >= 68) { - if (pulses.get(0) < startMark * 0.75 || pulses.get(0) > startMark * 1.25) { - return null; - } + // remove two first elements + pulses.remove(0); + pulses.remove(0); - if (startSpace != null - && (pulses.get(1) < (startSpace * 0.75) || pulses.get(1) > (startSpace * 1.25))) { - return null; - } + int res = 0; + long x = 0L; + + for (int i = 31; i >= 0; i--) { + res = pulses.get(1) >= (Integer) 1125 ? 1 : 0; + + x |= (long) (res) << i; // remove two first elements pulses.remove(0); pulses.remove(0); + } - Integer res = 0; - long x = 0L; - - for (int i = 31; i >= 0; i--) { - Integer pulseMatch = null; - Integer spaceMatch = null; - - if (pulseThreshold != null) { - pulseMatch = pulses.get(0) >= pulseThreshold ? 1 : 0; - } - if (spaceThreshold != null) { - spaceMatch = pulses.get(1) >= spaceThreshold ? 1 : 0; - } - - if (pulseMatch != null && spaceMatch != null) { - if (!pulseMatch.equals(spaceMatch)) { - return null; - } - res = spaceMatch; - } else if (pulseMatch == null) { - res = spaceMatch; - } else { - res = pulseMatch; - } - - if (res != null) { - x |= (long) (res) << i; - } - - // remove two first elements - pulses.remove(0); - pulses.remove(0); - } - - if (!ret.contains(x)) { - ret.add(x); - } + if (!ret.contains(x)) { + ret.add(x); } } @@ -172,7 +140,7 @@ private static List widthEncodedToPulses(long data, PulseParams param) { pulses.add(param.startSpace); for (int i = 31; i >= 0; i--) { - if ((data & (1 << i)) > 0L) { + if ((data & (1L << i)) > 0L) { pulses.add(param.pulseOne); pulses.add(param.spaceOne); } else { @@ -185,11 +153,11 @@ private static List widthEncodedToPulses(long data, PulseParams param) { return pulses; } - private static long mirrorBits(long data, int bits) { - int shift = bits - 1; + private static long mirrorBits(long data) { + int shift = 8 - 1; long out = 0; - for (int i = 0; i < bits; i++) { + for (int i = 0; i < 8; i++) { if ((data & (1L << i)) > 0L) { out |= 1L << shift; } @@ -200,16 +168,15 @@ private static long mirrorBits(long data, int bits) { private static List pulsesToNec(List pulses) { List ret = new ArrayList<>(); - List res = pulsesToWidthEncoded(pulses, 9000, null, null, 1125); - if (res == null || res.isEmpty()) { - logger.warn("[tuya:ir-controller] No ir key-code detected"); - return null; + List res = pulsesToWidthEncoded(pulses, 9000); + if (res.isEmpty()) { + throw new IllegalArgumentException("[tuya:ir-controller] No ir key-code detected"); } for (Long code : res) { - long addr = mirrorBits((code >> 24) & 0xFF, 8); - long addrNot = mirrorBits((code >> 16) & 0xFF, 8); - long data = mirrorBits((code >> 8) & 0xFF, 8); - long dataNot = mirrorBits(code & 0xFF, 8); + long addr = mirrorBits((code >> 24) & 0xFF); + long addrNot = mirrorBits((code >> 16) & 0xFF); + long data = mirrorBits((code >> 8) & 0xFF); + long dataNot = mirrorBits(code & 0xFF); if (addr != (addrNot ^ 0xFF)) { addr = (addr << 8) | addrNot; @@ -227,34 +194,8 @@ private static List pulsesToNec(List pulses) { return ret; } - private static String bytesToHex(byte[] bytes) { - final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = HEX_ARRAY[v >>> 4]; - hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; - } - return new String(hexChars); - } - - private static List necToPulses(long address, Long data) { - Long newAddress, newData; - if (data == null) { - newAddress = address; - } else { - if (address < 256) { - newAddress = mirrorBits(address, 8); - newAddress = (newAddress << 8) | (newAddress ^ 0xFF); - } else { - newAddress = (mirrorBits((address >> 8) & 0xFF, 8) << 8) | mirrorBits(address & 0xFF, 8); - } - newData = mirrorBits(data, 8); - newData = (newData << 8) | (newData & 0xFF); - newAddress = (newAddress << 16) | newData; - } - - return widthEncodedToPulses(newAddress, new PulseParams()); + private static List necToPulses(long address) { + return widthEncodedToPulses(address, new PulseParams()); } private static String pulsesToBase64(List pulses) { @@ -279,7 +220,7 @@ private static String pulsesToBase64(List pulses) { * @return the string */ public static String necToBase64(long code) { - List pulses = necToPulses(code, null); + List pulses = necToPulses(code); return pulsesToBase64(pulses); } @@ -290,26 +231,17 @@ public static String necToBase64(long code) { * @return the string */ public static String samsungToBase64(long code) { - List pulses = samsungToPulses(code, null); + List pulses = samsungToPulses(code); return pulsesToBase64(pulses); } - private static List samsungToPulses(long address, Long data) { - Long newAddress, newData; - if (data == null) { - newAddress = address; - } else { - newAddress = mirrorBits(address, 8); - newData = mirrorBits(data, 8); - newData = (newData << 8) | (newData & 0xFF); - newAddress = (newAddress << 24) + (newAddress << 16) + (newData << 8) + (newData ^ 0xFF); - } - return widthEncodedToPulses(newAddress, new PulseParams()); + private static List samsungToPulses(long address) { + return widthEncodedToPulses(address, new PulseParams()); } private static List pulsesToSamsung(List pulses) { List ret = new ArrayList<>(); - List res = pulsesToWidthEncoded(pulses, 4500, null, null, 1125); + List res = pulsesToWidthEncoded(pulses, 4500); for (Long code : res) { long addr = (code >> 24) & 0xFF; long addrNot = (code >> 16) & 0xFF; @@ -320,8 +252,8 @@ private static List pulsesToSamsung(List pulses) { "{ \"type\": \"samsung\", \"uint32\": %d, \"address\": None, \"data\": None, \"hex\": \"%08X\" }", code, code); if (addr == addrNot && data == (dataNot ^ 0xFF)) { - addr = mirrorBits(addr, 8); - data = mirrorBits(data, 8); + addr = mirrorBits(addr); + data = mirrorBits(data); d = String.format( "{ \"type\": \"samsung\", \"uint32\": %d, \"address\": %d, \"data\": %d, \"hex\": \"%08X\" }", code, addr, data, code);