2 * Copyright (c) 2010-2020 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.deconz.internal.handler;
15 import static org.openhab.binding.deconz.internal.BindingConstants.*;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.deconz.internal.Util;
22 import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
23 import org.openhab.binding.deconz.internal.dto.GroupAction;
24 import org.openhab.binding.deconz.internal.dto.GroupMessage;
25 import org.openhab.binding.deconz.internal.dto.GroupState;
26 import org.openhab.binding.deconz.internal.netutils.AsyncHttpClient;
27 import org.openhab.binding.deconz.internal.types.ResourceType;
28 import org.openhab.core.library.types.DecimalType;
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.thing.ChannelUID;
33 import org.openhab.core.thing.Thing;
34 import org.openhab.core.thing.ThingStatus;
35 import org.openhab.core.thing.ThingTypeUID;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.RefreshType;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 import com.google.gson.Gson;
44 * This light thing doesn't establish any connections, that is done by the bridge Thing.
46 * It waits for the bridge to come online, grab the websocket connection and bridge configuration
47 * and registers to the websocket connection as a listener.
49 * A REST API call is made to get the initial light/rollershutter state.
51 * Every light and rollershutter is supported by this Thing, because a unified state is kept
52 * in {@link #groupStateCache}. Every field that got received by the REST API for this specific
53 * sensor is published to the framework.
55 * @author Jan N. Klug - Initial contribution
58 public class GroupThingHandler extends DeconzBaseThingHandler<GroupMessage> {
59 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_LIGHTGROUP);
60 private final Logger logger = LoggerFactory.getLogger(GroupThingHandler.class);
65 private GroupState groupStateCache = new GroupState();
67 public GroupThingHandler(Thing thing, Gson gson) {
68 super(thing, gson, ResourceType.GROUPS);
72 public void handleCommand(ChannelUID channelUID, Command command) {
73 String channelId = channelUID.getId();
75 GroupAction newGroupAction = new GroupAction();
79 if (command instanceof RefreshType) {
80 valueUpdated(channelUID.getId(), groupStateCache);
85 if (command instanceof OnOffType) {
86 newGroupAction.alert = command == OnOffType.ON ? "alert" : "none";
92 if (command instanceof HSBType) {
93 HSBType hsbCommand = (HSBType) command;
94 newGroupAction.bri = Util.fromPercentType(hsbCommand.getBrightness());
95 if (newGroupAction.bri > 0) {
96 newGroupAction.hue = (int) (hsbCommand.getHue().doubleValue() * HUE_FACTOR);
97 newGroupAction.sat = Util.fromPercentType(hsbCommand.getSaturation());
99 } else if (command instanceof PercentType) {
100 newGroupAction.bri = Util.fromPercentType((PercentType) command);
101 } else if (command instanceof DecimalType) {
102 newGroupAction.bri = ((DecimalType) command).intValue();
103 } else if (command instanceof OnOffType) {
104 newGroupAction.on = OnOffType.ON.equals(command);
109 case CHANNEL_COLOR_TEMPERATURE:
110 if (command instanceof DecimalType) {
111 int miredValue = Util.kelvinToMired(((DecimalType) command).intValue());
112 newGroupAction.ct = Util.constrainToRange(miredValue, ZCL_CT_MIN, ZCL_CT_MAX);
121 if (newGroupAction.bri != null && newGroupAction.bri > 0) {
122 newGroupAction.on = true;
125 sendCommand(newGroupAction, command, channelUID, null);
129 protected @Nullable GroupMessage parseStateResponse(AsyncHttpClient.Result r) {
130 if (r.getResponseCode() == 403) {
132 } else if (r.getResponseCode() == 200) {
133 return gson.fromJson(r.getBody(), GroupMessage.class);
135 throw new IllegalStateException("Unknown status code " + r.getResponseCode() + " for full state request");
140 protected void processStateResponse(@Nullable GroupMessage stateResponse) {
141 if (stateResponse == null) {
144 messageReceived(config.id, stateResponse);
147 private void valueUpdated(String channelId, GroupState newState) {
150 updateState(channelId, OnOffType.from(newState.all_on));
153 updateState(channelId, OnOffType.from(newState.any_on));
160 public void messageReceived(String sensorID, DeconzBaseMessage message) {
161 if (message instanceof GroupMessage) {
162 GroupMessage groupMessage = (GroupMessage) message;
163 logger.trace("{} received {}", thing.getUID(), groupMessage);
164 GroupState groupState = groupMessage.state;
165 if (groupState != null) {
166 updateStatus(ThingStatus.ONLINE);
167 thing.getChannels().stream().map(c -> c.getUID().getId()).forEach(c -> valueUpdated(c, groupState));
168 groupStateCache = groupState;