]> git.basschouten.com Git - openhab-addons.git/blob
02db57957c99b9fff081e134143aeca72127ed2e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.deconz.internal.handler;
14
15 import static org.openhab.binding.deconz.internal.BindingConstants.*;
16
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.stream.Collectors;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.openhab.binding.deconz.internal.DeconzDynamicCommandDescriptionProvider;
23 import org.openhab.binding.deconz.internal.Util;
24 import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
25 import org.openhab.binding.deconz.internal.dto.GroupAction;
26 import org.openhab.binding.deconz.internal.dto.GroupMessage;
27 import org.openhab.binding.deconz.internal.dto.GroupState;
28 import org.openhab.binding.deconz.internal.dto.Scene;
29 import org.openhab.binding.deconz.internal.types.ResourceType;
30 import org.openhab.core.library.types.DecimalType;
31 import org.openhab.core.library.types.HSBType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.PercentType;
34 import org.openhab.core.library.types.StringType;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingTypeUID;
39 import org.openhab.core.types.Command;
40 import org.openhab.core.types.RefreshType;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import com.google.gson.Gson;
45
46 /**
47  * This group thing doesn't establish any connections, that is done by the bridge Thing.
48  *
49  * It waits for the bridge to come online, grab the websocket connection and bridge configuration
50  * and registers to the websocket connection as a listener.
51  *
52  * @author Jan N. Klug - Initial contribution
53  */
54 @NonNullByDefault
55 public class GroupThingHandler extends DeconzBaseThingHandler {
56     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPE_UIDS = Set.of(THING_TYPE_LIGHTGROUP);
57     private final Logger logger = LoggerFactory.getLogger(GroupThingHandler.class);
58     private final DeconzDynamicCommandDescriptionProvider commandDescriptionProvider;
59
60     private Map<String, String> scenes = Map.of();
61     private GroupState groupStateCache = new GroupState();
62     private String colorMode = "";
63
64     public GroupThingHandler(Thing thing, Gson gson,
65             DeconzDynamicCommandDescriptionProvider commandDescriptionProvider) {
66         super(thing, gson, ResourceType.GROUPS);
67         this.commandDescriptionProvider = commandDescriptionProvider;
68     }
69
70     @Override
71     public void initialize() {
72         ThingConfig thingConfig = getConfigAs(ThingConfig.class);
73         colorMode = thingConfig.colormode;
74
75         super.initialize();
76     }
77
78     @Override
79     public void handleCommand(ChannelUID channelUID, Command command) {
80         String channelId = channelUID.getId();
81
82         GroupAction newGroupAction = new GroupAction();
83         switch (channelId) {
84             case CHANNEL_ALL_ON:
85             case CHANNEL_ANY_ON:
86                 if (command instanceof RefreshType) {
87                     valueUpdated(channelUID.getId(), groupStateCache);
88                     return;
89                 }
90                 break;
91             case CHANNEL_ALERT:
92                 if (command instanceof StringType) {
93                     newGroupAction.alert = command.toString();
94                 } else {
95                     return;
96                 }
97                 break;
98             case CHANNEL_COLOR:
99                 if (command instanceof HSBType) {
100                     HSBType hsbCommand = (HSBType) command;
101                     // XY color is the implicit default: Use XY color mode if i) no color mode is set or ii) if the bulb
102                     // is in CT mode or iii) already in XY mode. Only if the bulb is in HS mode, use this one.
103                     if ("hs".equals(colorMode)) {
104                         newGroupAction.hue = (int) (hsbCommand.getHue().doubleValue() * HUE_FACTOR);
105                         newGroupAction.sat = Util.fromPercentType(hsbCommand.getSaturation());
106                     } else {
107                         PercentType[] xy = hsbCommand.toXY();
108                         if (xy.length < 2) {
109                             logger.warn("Failed to convert {} to xy-values", command);
110                         }
111                         newGroupAction.xy = new double[] { xy[0].doubleValue() / 100.0, xy[1].doubleValue() / 100.0 };
112                     }
113                 } else if (command instanceof PercentType) {
114                     newGroupAction.bri = Util.fromPercentType((PercentType) command);
115                 } else if (command instanceof DecimalType) {
116                     newGroupAction.bri = ((DecimalType) command).intValue();
117                 } else if (command instanceof OnOffType) {
118                     newGroupAction.on = OnOffType.ON.equals(command);
119                 } else {
120                     return;
121                 }
122                 break;
123             case CHANNEL_COLOR_TEMPERATURE:
124                 if (command instanceof DecimalType) {
125                     int miredValue = Util.kelvinToMired(((DecimalType) command).intValue());
126                     newGroupAction.ct = Util.constrainToRange(miredValue, ZCL_CT_MIN, ZCL_CT_MAX);
127                 } else {
128                     return;
129                 }
130                 break;
131             case CHANNEL_SCENE:
132                 if (command instanceof StringType) {
133                     String sceneId = scenes.get(command.toString());
134                     if (sceneId != null) {
135                         sendCommand(null, command, channelUID, "scenes/" + sceneId + "/recall", null);
136                     } else {
137                         logger.debug("Ignoring command {} for {}, scene is not found in available scenes: {}", command,
138                                 channelUID, scenes);
139                     }
140                 }
141                 return;
142             default:
143                 return;
144         }
145
146         Integer bri = newGroupAction.bri;
147         if (bri != null) {
148             newGroupAction.on = (bri > 0);
149         }
150
151         sendCommand(newGroupAction, command, channelUID, null);
152     }
153
154     @Override
155     protected void processStateResponse(DeconzBaseMessage stateResponse) {
156         if (stateResponse instanceof GroupMessage) {
157             GroupMessage groupMessage = (GroupMessage) stateResponse;
158             scenes = groupMessage.scenes.stream().collect(Collectors.toMap(scene -> scene.name, scene -> scene.id));
159             ChannelUID channelUID = new ChannelUID(thing.getUID(), CHANNEL_SCENE);
160             commandDescriptionProvider.setCommandOptions(channelUID,
161                     groupMessage.scenes.stream().map(Scene::toCommandOption).collect(Collectors.toList()));
162
163         }
164         messageReceived(config.id, stateResponse);
165     }
166
167     private void valueUpdated(String channelId, GroupState newState) {
168         switch (channelId) {
169             case CHANNEL_ALL_ON:
170                 updateState(channelId, OnOffType.from(newState.all_on));
171                 break;
172             case CHANNEL_ANY_ON:
173                 updateState(channelId, OnOffType.from(newState.any_on));
174                 break;
175             default:
176         }
177     }
178
179     @Override
180     public void messageReceived(String sensorID, DeconzBaseMessage message) {
181         if (message instanceof GroupMessage) {
182             GroupMessage groupMessage = (GroupMessage) message;
183             logger.trace("{} received {}", thing.getUID(), groupMessage);
184             GroupState groupState = groupMessage.state;
185             if (groupState != null) {
186                 updateStatus(ThingStatus.ONLINE);
187                 thing.getChannels().stream().map(c -> c.getUID().getId()).forEach(c -> valueUpdated(c, groupState));
188                 groupStateCache = groupState;
189             }
190             GroupAction groupAction = groupMessage.action;
191             if (groupAction != null) {
192                 if (colorMode.isEmpty()) {
193                     String cmode = groupAction.colormode;
194                     if (cmode != null && ("hs".equals(cmode) || "xy".equals(cmode))) {
195                         // only set the color mode if it is hs or xy, not ct
196                         colorMode = cmode;
197                     }
198                 }
199             }
200         }
201     }
202 }