2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.lcn.internal.subhandler;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.openhab.binding.lcn.internal.LcnBindingConstants;
22 import org.openhab.binding.lcn.internal.LcnModuleHandler;
23 import org.openhab.binding.lcn.internal.common.DimmerOutputCommand;
24 import org.openhab.binding.lcn.internal.common.LcnChannelGroup;
25 import org.openhab.binding.lcn.internal.common.LcnDefs;
26 import org.openhab.binding.lcn.internal.common.LcnException;
27 import org.openhab.binding.lcn.internal.common.PckGenerator;
28 import org.openhab.binding.lcn.internal.connection.ModInfo;
29 import org.openhab.core.library.types.HSBType;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.PercentType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.library.types.UpDownType;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * Handles Commands and State changes of dimmer outputs of an LCN module.
40 * @author Fabian Wolter - Initial contribution
43 public class LcnModuleOutputSubHandler extends AbstractLcnModuleSubHandler {
44 private final Logger logger = LoggerFactory.getLogger(LcnModuleOutputSubHandler.class);
45 private static final int COLOR_RAMP_MS = 1000;
46 private static final String OUTPUT_COLOR = "color";
47 private static final Pattern PERCENT_PATTERN;
48 private static final Pattern NATIVE_PATTERN;
49 private volatile HSBType currentColor = new HSBType();
50 private volatile PercentType output4 = new PercentType();
52 public LcnModuleOutputSubHandler(LcnModuleHandler handler, ModInfo info) {
57 PERCENT_PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + "A(?<outputId>\\d)(?<percent>\\d{3})");
58 NATIVE_PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + "O(?<outputId>\\d)(?<value>\\d{3})");
62 public Collection<Pattern> getPckStatusMessagePatterns() {
63 return Arrays.asList(NATIVE_PATTERN, PERCENT_PATTERN);
67 public void handleRefresh(LcnChannelGroup channelGroup, int number) {
68 info.refreshOutput(number);
72 public void handleRefresh(String groupId) {
73 if (OUTPUT_COLOR.equals(groupId)) {
74 info.refreshAllOutputs();
79 public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException {
80 // don't use OnOffType.as() here, because it returns @Nullable
81 handler.sendPck(PckGenerator.dimOutput(number, command == OnOffType.ON ? 100 : 0, 0));
85 public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, int number)
87 handler.sendPck(PckGenerator.dimOutput(number, command.doubleValue(), 0));
91 public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, String idWithoutGroup)
93 if (!OUTPUT_COLOR.equals(idWithoutGroup)) {
94 throw new LcnException("Unknown group ID: " + idWithoutGroup);
96 updateAndSendColor(new HSBType(currentColor.getHue(), currentColor.getSaturation(), command));
100 public void handleCommandHsb(HSBType command, String groupId) throws LcnException {
101 if (!OUTPUT_COLOR.equals(groupId)) {
102 throw new LcnException("Unknown group ID: " + groupId);
104 updateAndSendColor(command);
107 private synchronized void updateAndSendColor(HSBType hsbType) throws LcnException {
108 currentColor = hsbType;
109 handler.updateChannel(LcnChannelGroup.OUTPUT, OUTPUT_COLOR, currentColor);
111 if (info.getFirmwareVersion().map(v -> v >= LcnBindingConstants.FIRMWARE_2014).orElse(true)) {
112 handler.sendPck(PckGenerator.dimAllOutputs(currentColor.getRed().doubleValue(),
113 currentColor.getGreen().doubleValue(), currentColor.getBlue().doubleValue(), output4.doubleValue(),
116 handler.sendPck(PckGenerator.dimOutput(0, currentColor.getRed().doubleValue(), COLOR_RAMP_MS));
117 handler.sendPck(PckGenerator.dimOutput(1, currentColor.getGreen().doubleValue(), COLOR_RAMP_MS));
118 handler.sendPck(PckGenerator.dimOutput(2, currentColor.getBlue().doubleValue(), COLOR_RAMP_MS));
123 public void handleCommandDimmerOutput(DimmerOutputCommand command, int number) throws LcnException {
124 int rampMs = command.getRampMs();
125 if (command.isControlAllOutputs()) { // control all dimmer outputs
126 if (rampMs == LcnDefs.FIXED_RAMP_MS) {
127 // compatibility command
128 handler.sendPck(PckGenerator.controlAllOutputs(command.intValue()));
130 // command since firmware 180501
131 handler.sendPck(PckGenerator.dimAllOutputs(command.doubleValue(), command.doubleValue(),
132 command.doubleValue(), command.doubleValue(), rampMs));
134 } else if (command.isControlOutputs12()) { // control dimmer outputs 1+2
135 if (command.intValue() == 0 || command.intValue() == 100) {
136 handler.sendPck(PckGenerator.controlOutputs12(command.intValue() > 0, rampMs >= LcnDefs.FIXED_RAMP_MS));
138 // ignore ramp when dimming
139 handler.sendPck(PckGenerator.dimOutputs12(command.doubleValue()));
142 handler.sendPck(PckGenerator.dimOutput(number, command.doubleValue(), rampMs));
147 public void handleCommandString(StringType command, int number) throws LcnException {
150 switch (command.toString()) {
162 handler.sendPck(PckGenerator.setTunableWhiteMode(mode));
166 public void handleStatusMessage(Matcher matcher) {
167 int outputId = Integer.parseInt(matcher.group("outputId")) - 1;
169 if (!LcnChannelGroup.OUTPUT.isValidId(outputId)) {
170 logger.warn("outputId out of range: {}", outputId);
174 if (matcher.pattern() == PERCENT_PATTERN) {
175 percent = Integer.parseInt(matcher.group("percent"));
176 } else if (matcher.pattern() == NATIVE_PATTERN) {
177 percent = (double) Integer.parseInt(matcher.group("value")) / 2;
179 logger.warn("Unexpected pattern: {}", matcher.pattern());
183 info.onOutputResponseReceived(outputId);
185 percent = Math.min(100, Math.max(0, percent));
187 PercentType percentType = new PercentType((int) Math.round(percent));
188 fireUpdate(LcnChannelGroup.OUTPUT, outputId, percentType);
191 output4 = percentType;
196 fireUpdate(LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0, UpDownType.UP);
197 } else if (outputId == 1) {
198 fireUpdate(LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0, UpDownType.DOWN);