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