]> git.basschouten.com Git - openhab-addons.git/blob
cc45d78c8549f30b952e8844855b8655007b0ebd
[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.powermax.internal.handler;
14
15 import static org.openhab.binding.powermax.internal.PowermaxBindingConstants.*;
16
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.binding.powermax.internal.config.PowermaxX10Configuration;
20 import org.openhab.binding.powermax.internal.config.PowermaxZoneConfiguration;
21 import org.openhab.binding.powermax.internal.state.PowermaxPanelSettings;
22 import org.openhab.binding.powermax.internal.state.PowermaxPanelSettingsListener;
23 import org.openhab.binding.powermax.internal.state.PowermaxState;
24 import org.openhab.binding.powermax.internal.state.PowermaxStateContainer.Value;
25 import org.openhab.binding.powermax.internal.state.PowermaxX10Settings;
26 import org.openhab.binding.powermax.internal.state.PowermaxZoneSettings;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.thing.Bridge;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.ThingStatusInfo;
34 import org.openhab.core.thing.binding.BaseThingHandler;
35 import org.openhab.core.thing.binding.ThingHandler;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.RefreshType;
38 import org.openhab.core.types.UnDefType;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * The {@link PowermaxThingHandler} is responsible for handling commands, which are
44  * sent to one of the channels.
45  *
46  * @author Laurent Garnier - Initial contribution
47  */
48 @NonNullByDefault
49 public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPanelSettingsListener {
50
51     private static final int ZONE_NR_MIN = 1;
52     private static final int ZONE_NR_MAX = 64;
53     private static final int X10_NR_MIN = 1;
54     private static final int X10_NR_MAX = 16;
55
56     private final Logger logger = LoggerFactory.getLogger(PowermaxThingHandler.class);
57
58     private @Nullable PowermaxBridgeHandler bridgeHandler;
59
60     public PowermaxThingHandler(Thing thing) {
61         super(thing);
62     }
63
64     @Override
65     public void initialize() {
66         logger.debug("Initializing handler for thing {}", getThing().getUID());
67         Bridge bridge = getBridge();
68         if (bridge == null) {
69             initializeThingState(null, null);
70         } else {
71             initializeThingState(bridge.getHandler(), bridge.getStatus());
72         }
73     }
74
75     @Override
76     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
77         logger.debug("Bridge status changed to {} for thing {}", bridgeStatusInfo, getThing().getUID());
78         Bridge bridge = getBridge();
79         initializeThingState((bridge == null) ? null : bridge.getHandler(), bridgeStatusInfo.getStatus());
80     }
81
82     private void initializeThingState(@Nullable ThingHandler bridgeHandler, @Nullable ThingStatus bridgeStatus) {
83         if (bridgeHandler != null && bridgeStatus != null) {
84             if (bridgeStatus == ThingStatus.ONLINE) {
85                 boolean validConfig = false;
86                 String errorMsg = String.format("@text/offline.config-error-unexpected-thing-type [ \"%s\" ]",
87                         getThing().getThingTypeUID().getAsString());
88
89                 if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) {
90                     PowermaxZoneConfiguration config = getConfigAs(PowermaxZoneConfiguration.class);
91                     if (config.zoneNumber >= ZONE_NR_MIN && config.zoneNumber <= ZONE_NR_MAX) {
92                         validConfig = true;
93                     } else {
94                         errorMsg = String.format("@text/offline.config-error-invalid-zone-number [ \"%d\", \"%d\" ]",
95                                 ZONE_NR_MIN, ZONE_NR_MAX);
96                     }
97                 } else if (getThing().getThingTypeUID().equals(THING_TYPE_X10)) {
98                     PowermaxX10Configuration config = getConfigAs(PowermaxX10Configuration.class);
99                     if (config.deviceNumber >= X10_NR_MIN && config.deviceNumber <= X10_NR_MAX) {
100                         validConfig = true;
101                     } else {
102                         errorMsg = String.format("@text/offline.config-error-invalid-device-number [ \"%d\", \"%d\" ]",
103                                 X10_NR_MIN, X10_NR_MAX);
104                     }
105                 }
106
107                 if (validConfig) {
108                     updateStatus(ThingStatus.UNKNOWN);
109                     logger.debug("Set handler status to UNKNOWN for thing {} (bridge ONLINE)", getThing().getUID());
110                     PowermaxBridgeHandler powermaxBridgeHandler = (PowermaxBridgeHandler) bridgeHandler;
111                     this.bridgeHandler = powermaxBridgeHandler;
112                     powermaxBridgeHandler.registerPanelSettingsListener(this);
113                     onPanelSettingsUpdated(powermaxBridgeHandler.getPanelSettings());
114                 } else {
115                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg);
116                 }
117             } else {
118                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
119                 setAllChannelsOffline();
120                 logger.debug("Set handler status to OFFLINE for thing {} (bridge OFFLINE)", getThing().getUID());
121             }
122         } else {
123             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
124             logger.debug("Set handler status to OFFLINE for thing {}", getThing().getUID());
125         }
126     }
127
128     /**
129      * Update all channels to an UNDEF state to indicate that the bridge is offline
130      */
131     private synchronized void setAllChannelsOffline() {
132         getThing().getChannels().forEach(c -> updateState(c.getUID(), UnDefType.UNDEF));
133     }
134
135     @Override
136     public void dispose() {
137         logger.debug("Handler disposed for thing {}", getThing().getUID());
138         PowermaxBridgeHandler powermaxBridgeHandler = bridgeHandler;
139         if (powermaxBridgeHandler != null) {
140             powermaxBridgeHandler.unregisterPanelSettingsListener(this);
141         }
142         super.dispose();
143     }
144
145     @Override
146     public void handleCommand(ChannelUID channelUID, Command command) {
147         logger.debug("Received command {} from channel {}", command, channelUID.getId());
148
149         PowermaxBridgeHandler powermaxBridgeHandler = bridgeHandler;
150         if (powermaxBridgeHandler == null) {
151             return;
152         } else if (command instanceof RefreshType) {
153             updateChannelFromAlarmState(channelUID.getId(), powermaxBridgeHandler.getCurrentState());
154         } else {
155             switch (channelUID.getId()) {
156                 case BYPASSED:
157                     if (command instanceof OnOffType) {
158                         powermaxBridgeHandler.zoneBypassed(
159                                 Byte.valueOf(
160                                         (byte) (getConfigAs(PowermaxZoneConfiguration.class).zoneNumber & 0x000000FF)),
161                                 command.equals(OnOffType.ON));
162                     } else {
163                         logger.debug("Command of type {} while OnOffType is expected. Command is ignored.",
164                                 command.getClass().getSimpleName());
165                     }
166                     break;
167                 case X10_STATUS:
168                     powermaxBridgeHandler.x10Command(
169                             Byte.valueOf(
170                                     (byte) (getConfigAs(PowermaxX10Configuration.class).deviceNumber & 0x000000FF)),
171                             command);
172                     break;
173                 default:
174                     logger.debug("No available command for channel {}. Command is ignored.", channelUID.getId());
175                     break;
176             }
177         }
178     }
179
180     /**
181      * Update channel to match a new alarm system state
182      *
183      * @param channel: the channel
184      * @param state: the alarm system state
185      */
186     public void updateChannelFromAlarmState(String channel, @Nullable PowermaxState state) {
187         if (state == null || !isLinked(channel)) {
188             return;
189         }
190
191         if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) {
192             int num = getConfigAs(PowermaxZoneConfiguration.class).zoneNumber;
193
194             for (Value<?> value : state.getZone(num).getValues()) {
195                 String vChannel = value.getChannel();
196
197                 if (channel.equals(vChannel) && (value.getValue() != null)) {
198                     updateState(vChannel, value.getState());
199                 }
200             }
201         } else if (getThing().getThingTypeUID().equals(THING_TYPE_X10)) {
202             int num = getConfigAs(PowermaxX10Configuration.class).deviceNumber;
203             Boolean status = state.getPGMX10DeviceStatus(num);
204             if (channel.equals(X10_STATUS) && (status != null)) {
205                 updateState(X10_STATUS, status ? OnOffType.ON : OnOffType.OFF);
206             }
207         }
208     }
209
210     @Override
211     public void onPanelSettingsUpdated(@Nullable PowermaxPanelSettings settings) {
212         if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) {
213             PowermaxZoneConfiguration config = getConfigAs(PowermaxZoneConfiguration.class);
214             onZoneSettingsUpdated(config.zoneNumber, settings);
215         } else if (getThing().getThingTypeUID().equals(THING_TYPE_X10)) {
216             if (isNotReadyForThingStatusUpdate()) {
217                 return;
218             }
219
220             PowermaxX10Configuration config = getConfigAs(PowermaxX10Configuration.class);
221             PowermaxX10Settings deviceSettings = (settings == null) ? null
222                     : settings.getX10Settings(config.deviceNumber);
223             if (settings == null) {
224                 if (getThing().getStatus() != ThingStatus.UNKNOWN) {
225                     updateStatus(ThingStatus.UNKNOWN);
226                     logger.debug("Set handler status to UNKNOWN for thing {}", getThing().getUID());
227                 }
228             } else if (deviceSettings == null || !deviceSettings.isEnabled()) {
229                 if (getThing().getStatus() != ThingStatus.OFFLINE) {
230                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/offline.disabled-device");
231                     logger.debug("Set handler status to OFFLINE for thing {} (X10 device {} disabled)",
232                             getThing().getUID(), config.deviceNumber);
233                 }
234             } else if (getThing().getStatus() != ThingStatus.ONLINE) {
235                 updateStatus(ThingStatus.ONLINE);
236                 logger.debug("Set handler status to ONLINE for thing {} (X10 device {} enabled)", getThing().getUID(),
237                         config.deviceNumber);
238             }
239         }
240     }
241
242     @Override
243     public void onZoneSettingsUpdated(int zoneNumber, @Nullable PowermaxPanelSettings settings) {
244         if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) {
245             PowermaxZoneConfiguration config = getConfigAs(PowermaxZoneConfiguration.class);
246             if (zoneNumber == config.zoneNumber) {
247                 if (isNotReadyForThingStatusUpdate()) {
248                     return;
249                 }
250
251                 PowermaxZoneSettings zoneSettings = (settings == null) ? null
252                         : settings.getZoneSettings(config.zoneNumber);
253                 if (settings == null) {
254                     if (getThing().getStatus() != ThingStatus.UNKNOWN) {
255                         updateStatus(ThingStatus.UNKNOWN);
256                         logger.debug("Set handler status to UNKNOWN for thing {}", getThing().getUID());
257                     }
258                 } else if (zoneSettings == null) {
259                     if (getThing().getStatus() != ThingStatus.OFFLINE) {
260                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/offline.zone-not-paired");
261                         logger.debug("Set handler status to OFFLINE for thing {} (zone number {} not paired)",
262                                 getThing().getUID(), config.zoneNumber);
263                     }
264                 } else if (getThing().getStatus() != ThingStatus.ONLINE) {
265                     updateStatus(ThingStatus.ONLINE);
266                     logger.debug("Set handler status to ONLINE for thing {} (zone number {} paired)",
267                             getThing().getUID(), config.zoneNumber);
268
269                     logger.debug("Using name '{}' for {}", getThing().getLabel(), getThing().getUID());
270                     zoneSettings.setName(getThing().getLabel());
271                 }
272             }
273         }
274     }
275
276     private boolean isNotReadyForThingStatusUpdate() {
277         return (getThing().getStatus() == ThingStatus.OFFLINE)
278                 && ((getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR)
279                         || (getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE)
280                         || (getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_UNINITIALIZED));
281     }
282
283     public PowermaxZoneConfiguration getZoneConfiguration() {
284         return getConfigAs(PowermaxZoneConfiguration.class);
285     }
286
287     public PowermaxX10Configuration getX10Configuration() {
288         return getConfigAs(PowermaxX10Configuration.class);
289     }
290 }