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