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.milight.internal.handler;
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.openhab.binding.milight.internal.MilightThingState;
17 import org.openhab.binding.milight.internal.protocol.ProtocolConstants;
18 import org.openhab.binding.milight.internal.protocol.QueueItem;
19 import org.openhab.binding.milight.internal.protocol.QueuedSend;
20 import org.openhab.core.thing.Thing;
23 * Implements functionality for the Dual White bulbs for protocol version 3.
24 * Color temperature is supported for this type of bulbs.
26 * @author David Graeff - Initial contribution
29 public class MilightV3WhiteHandler extends AbstractLedV3Handler {
30 protected static final int BRIGHTNESS_LEVELS = 11;
32 public MilightV3WhiteHandler(Thing thing, QueuedSend sendQueue) {
33 super(thing, sendQueue, 0);
36 private static final byte COMMAND_FULL[] = { (byte) 0xB5, (byte) 0xB8, (byte) 0xBD, (byte) 0xB7, (byte) 0xB2 };
37 private static final byte COMMAND_ON[] = { (byte) 0x35, (byte) 0x38, (byte) 0x3D, (byte) 0x37, (byte) 0x32 };
38 private static final byte COMMAND_OFF[] = { (byte) 0x39, (byte) 0x3B, (byte) 0x33, (byte) 0x3A, (byte) 0x36 };
39 private static final byte COMMAND_NIGHTMODE[] = { (byte) 0xB9, (byte) 0xBB, (byte) 0xB3, (byte) 0xBA, (byte) 0xB6 };
40 private static final byte PREV_ANIMATION_MODE[] = { 0x27, 0x00, 0x55 };
41 private static final byte NEXT_ANIMATION_MODE[] = { 0x27, 0x00, 0x55 };
43 protected void setFull(int zone, MilightThingState state) {
44 sendQueue.queue(createRepeatable(uidc(ProtocolConstants.CAT_BRIGHTNESS_SET),
45 new byte[] { COMMAND_FULL[zone], 0x00, 0x55 }));
46 state.brightness = 100;
50 public void setHSB(int hue, int saturation, int brightness, MilightThingState state) {
51 // This bulb type only supports a brightness value
52 if (brightness != -1) {
53 setBrightness(brightness, state);
58 public void setPower(boolean on, MilightThingState state) {
60 sendQueue.queue(createRepeatable(uidc(ProtocolConstants.CAT_POWER_MODE),
61 new byte[] { COMMAND_ON[config.zone], 0x00, 0x55 }));
63 sendQueue.queue(createRepeatable(uidc(ProtocolConstants.CAT_POWER_MODE),
64 new byte[] { COMMAND_OFF[config.zone], 0x00, 0x55 }));
70 public void whiteMode(MilightThingState state) {
74 public void nightMode(MilightThingState state) {
75 final byte cOn[] = { COMMAND_ON[config.zone], 0x00, 0x55 };
76 final byte cNight[] = { COMMAND_NIGHTMODE[config.zone], 0x00, 0x55 };
77 sendQueue.queue(createRepeatable(uidc(ProtocolConstants.CAT_POWER_MODE), cOn).addRepeatable(cNight));
81 public void setColorTemperature(int colorTemp, MilightThingState state) {
82 // White Bulbs: 11 levels of temperature + Off.
85 // Reset bulb to known state
89 oldLevel = BRIGHTNESS_LEVELS;
90 } else if (colorTemp >= 100) {
92 newLevel = BRIGHTNESS_LEVELS;
95 newLevel = (int) Math.ceil((colorTemp * BRIGHTNESS_LEVELS) / 100.0);
96 oldLevel = (int) Math.ceil((state.colorTemperature * BRIGHTNESS_LEVELS) / 100.0);
99 final int repeatCount = Math.abs(newLevel - oldLevel);
100 if (newLevel > oldLevel) {
101 for (int i = 0; i < repeatCount; i++) {
102 changeColorTemperature(1, state);
104 } else if (newLevel < oldLevel) {
105 for (int i = 0; i < repeatCount; i++) {
106 changeColorTemperature(-1, state);
110 state.colorTemperature = colorTemp;
114 public void changeColorTemperature(int colorTempRelative, MilightThingState state) {
115 state.colorTemperature = Math.min(100, Math.max(state.colorTemperature + colorTempRelative, 0));
116 final byte cOn[] = { COMMAND_ON[config.zone], 0x00, 0x55 };
117 final byte cTemp[] = { (byte) (colorTempRelative > 0 ? 0x3E : 0x3F), 0x00, 0x55 };
118 sendQueue.queue(createRepeatable(cOn).addNonRepeatable(cTemp));
121 // This just emulates an absolute brightness command with the relative commands.
123 public void setBrightness(int value, MilightThingState state) {
125 setPower(false, state);
127 } else if (value >= 100) {
128 setFull(config.zone, state);
132 // White Bulbs: 11 levels of brightness + Off.
133 final int newLevel = (int) Math.ceil((value * BRIGHTNESS_LEVELS) / 100.0);
135 // When turning on start from full brightness
137 final byte cFull[] = { COMMAND_FULL[config.zone], 0x00, 0x55 };
138 QueueItem item = createRepeatable(cFull);
139 boolean skipFirst = false;
141 if (state.brightness == 0) {
142 oldLevel = BRIGHTNESS_LEVELS;
144 oldLevel = (int) Math.ceil((state.brightness * BRIGHTNESS_LEVELS) / 100.0);
147 if (newLevel == oldLevel) {
152 final int repeatCount = Math.abs(newLevel - oldLevel);
154 logger.debug("milight: dim from '{}' with command '{}' via '{}' steps.", String.valueOf(state.brightness),
155 String.valueOf(value), repeatCount);
157 int op = newLevel > oldLevel ? +1 : -1;
158 final byte cOn[] = { COMMAND_ON[config.zone], 0x00, 0x55 };
159 for (int i = 0; i < repeatCount; i++) {
160 final byte[] cBr = { (byte) (op < 0 ? 0x34 : 0x3C), 0x00, 0x55 };
161 item = item.addRepeatable(cOn).addNonRepeatable(cBr);
164 final QueueItem nextItem = item.next;
165 if (nextItem != null && skipFirst) {
166 sendQueue.queue(nextItem);
168 sendQueue.queue(item);
171 state.brightness = value;
175 public void changeBrightness(int relativeBrightness, MilightThingState state) {
176 if (relativeBrightness == 0) {
179 state.brightness = Math.min(Math.max(state.brightness + relativeBrightness, 0), 100);
181 if (state.brightness == 0) {
182 sendQueue.queue(createRepeatable(uidc(ProtocolConstants.CAT_POWER_MODE),
183 new byte[] { COMMAND_OFF[config.zone], 0x00, 0x55 }));
185 final byte cOn[] = { COMMAND_ON[config.zone], 0x00, 0x55 };
186 final byte cBr[] = { (byte) (relativeBrightness < 0 ? 0x34 : 0x3C), 0x00, 0x55 };
187 sendQueue.queue(createRepeatable(cOn).addNonRepeatable(cBr));
192 public void changeSpeed(int relativeSpeed, MilightThingState state) {
196 public void previousAnimationMode(MilightThingState state) {
197 final byte cOn[] = { COMMAND_ON[config.zone], 0x00, 0x55 };
198 sendQueue.queue(createRepeatable(cOn).addNonRepeatable(PREV_ANIMATION_MODE));
199 state.animationMode = Math.max(state.animationMode - 1, 0);
203 public void nextAnimationMode(MilightThingState state) {
204 final byte cOn[] = { COMMAND_ON[config.zone], 0x00, 0x55 };
205 sendQueue.queue(createRepeatable(cOn).addNonRepeatable(NEXT_ANIMATION_MODE));
206 state.animationMode = Math.min(state.animationMode + 1, MAX_ANIM_MODES);