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