2 * Copyright (c) 2010-2021 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.UpDownType;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
37 * Handles Commands and State changes of dimmer outputs of an LCN module.
39 * @author Fabian Wolter - Initial contribution
42 public class LcnModuleOutputSubHandler extends AbstractLcnModuleSubHandler {
43 private final Logger logger = LoggerFactory.getLogger(LcnModuleOutputSubHandler.class);
44 private static final int COLOR_RAMP_MS = 1000;
45 private static final String OUTPUT_COLOR = "color";
46 private static final Pattern PERCENT_PATTERN;
47 private static final Pattern NATIVE_PATTERN;
48 private volatile HSBType currentColor = new HSBType();
49 private volatile PercentType output4 = new PercentType();
51 public LcnModuleOutputSubHandler(LcnModuleHandler handler, ModInfo info) {
56 PERCENT_PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + "A(?<outputId>\\d)(?<percent>\\d+)");
57 NATIVE_PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + "O(?<outputId>\\d)(?<value>\\d+)");
61 public Collection<Pattern> getPckStatusMessagePatterns() {
62 return Arrays.asList(NATIVE_PATTERN, PERCENT_PATTERN);
66 public void handleRefresh(LcnChannelGroup channelGroup, int number) {
67 info.refreshOutput(number);
71 public void handleRefresh(String groupId) {
72 if (OUTPUT_COLOR.equals(groupId)) {
73 info.refreshAllOutputs();
78 public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException {
79 // don't use OnOffType.as() here, because it returns @Nullable
80 handler.sendPck(PckGenerator.dimOutput(number, command == OnOffType.ON ? 100 : 0, 0));
84 public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, int number)
86 handler.sendPck(PckGenerator.dimOutput(number, command.doubleValue(), 0));
90 public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, String idWithoutGroup)
92 if (!OUTPUT_COLOR.equals(idWithoutGroup)) {
93 throw new LcnException("Unknown group ID: " + idWithoutGroup);
95 updateAndSendColor(new HSBType(currentColor.getHue(), currentColor.getSaturation(), command));
99 public void handleCommandHsb(HSBType command, String groupId) throws LcnException {
100 if (!OUTPUT_COLOR.equals(groupId)) {
101 throw new LcnException("Unknown group ID: " + groupId);
103 updateAndSendColor(command);
106 private synchronized void updateAndSendColor(HSBType hsbType) throws LcnException {
107 currentColor = hsbType;
108 handler.updateChannel(LcnChannelGroup.OUTPUT, OUTPUT_COLOR, currentColor);
110 if (info.getFirmwareVersion().map(v -> v >= LcnBindingConstants.FIRMWARE_2014).orElse(true)) {
111 handler.sendPck(PckGenerator.dimAllOutputs(currentColor.getRed().doubleValue(),
112 currentColor.getGreen().doubleValue(), currentColor.getBlue().doubleValue(), output4.doubleValue(),
115 handler.sendPck(PckGenerator.dimOutput(0, currentColor.getRed().doubleValue(), COLOR_RAMP_MS));
116 handler.sendPck(PckGenerator.dimOutput(1, currentColor.getGreen().doubleValue(), COLOR_RAMP_MS));
117 handler.sendPck(PckGenerator.dimOutput(2, currentColor.getBlue().doubleValue(), COLOR_RAMP_MS));
122 public void handleCommandDimmerOutput(DimmerOutputCommand command, int number) throws LcnException {
123 int rampMs = command.getRampMs();
124 if (command.isControlAllOutputs()) { // control all dimmer outputs
125 if (rampMs == LcnDefs.FIXED_RAMP_MS) {
126 // compatibility command
127 handler.sendPck(PckGenerator.controlAllOutputs(command.intValue()));
129 // command since firmware 180501
130 handler.sendPck(PckGenerator.dimAllOutputs(command.doubleValue(), command.doubleValue(),
131 command.doubleValue(), command.doubleValue(), rampMs));
133 } else if (command.isControlOutputs12()) { // control dimmer outputs 1+2
134 if (command.intValue() == 0 || command.intValue() == 100) {
135 handler.sendPck(PckGenerator.controlOutputs12(command.intValue() > 0, rampMs >= LcnDefs.FIXED_RAMP_MS));
137 // ignore ramp when dimming
138 handler.sendPck(PckGenerator.dimOutputs12(command.doubleValue()));
141 handler.sendPck(PckGenerator.dimOutput(number, command.doubleValue(), rampMs));
146 public void handleStatusMessage(Matcher matcher) {
147 int outputId = Integer.parseInt(matcher.group("outputId")) - 1;
149 if (!LcnChannelGroup.OUTPUT.isValidId(outputId)) {
150 logger.warn("outputId out of range: {}", outputId);
154 if (matcher.pattern() == PERCENT_PATTERN) {
155 percent = Integer.parseInt(matcher.group("percent"));
156 } else if (matcher.pattern() == NATIVE_PATTERN) {
157 percent = (double) Integer.parseInt(matcher.group("value")) / 2;
159 logger.warn("Unexpected pattern: {}", matcher.pattern());
163 info.onOutputResponseReceived(outputId);
165 percent = Math.min(100, Math.max(0, percent));
167 PercentType percentType = new PercentType((int) Math.round(percent));
168 fireUpdate(LcnChannelGroup.OUTPUT, outputId, percentType);
171 output4 = percentType;
176 fireUpdate(LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0, UpDownType.UP);
177 } else if (outputId == 1) {
178 fireUpdate(LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0, UpDownType.DOWN);