2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.powermax.internal.handler;
15 import static org.openhab.binding.powermax.internal.PowermaxBindingConstants.*;
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;
43 * The {@link PowermaxThingHandler} is responsible for handling commands, which are
44 * sent to one of the channels.
46 * @author Laurent Garnier - Initial contribution
49 public class PowermaxThingHandler extends BaseThingHandler implements PowermaxPanelSettingsListener {
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;
56 private final Logger logger = LoggerFactory.getLogger(PowermaxThingHandler.class);
58 private @Nullable PowermaxBridgeHandler bridgeHandler;
60 public PowermaxThingHandler(Thing thing) {
65 public void initialize() {
66 logger.debug("Initializing handler for thing {}", getThing().getUID());
67 Bridge bridge = getBridge();
69 initializeThingState(null, null);
71 initializeThingState(bridge.getHandler(), bridge.getStatus());
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());
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());
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) {
94 errorMsg = String.format("@text/offline.config-error-invalid-zone-number [ \"%d\", \"%d\" ]",
95 ZONE_NR_MIN, ZONE_NR_MAX);
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) {
102 errorMsg = String.format("@text/offline.config-error-invalid-device-number [ \"%d\", \"%d\" ]",
103 X10_NR_MIN, X10_NR_MAX);
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());
115 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg);
118 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
119 setAllChannelsOffline();
120 logger.debug("Set handler status to OFFLINE for thing {} (bridge OFFLINE)", getThing().getUID());
123 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
124 logger.debug("Set handler status to OFFLINE for thing {}", getThing().getUID());
129 * Update all channels to an UNDEF state to indicate that the bridge is offline
131 private synchronized void setAllChannelsOffline() {
132 getThing().getChannels().forEach(c -> updateState(c.getUID(), UnDefType.UNDEF));
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);
146 public void handleCommand(ChannelUID channelUID, Command command) {
147 logger.debug("Received command {} from channel {}", command, channelUID.getId());
149 PowermaxBridgeHandler powermaxBridgeHandler = bridgeHandler;
150 if (powermaxBridgeHandler == null) {
152 } else if (command instanceof RefreshType) {
153 updateChannelFromAlarmState(channelUID.getId(), powermaxBridgeHandler.getCurrentState());
155 switch (channelUID.getId()) {
157 if (command instanceof OnOffType) {
158 powermaxBridgeHandler.zoneBypassed(
160 (byte) (getConfigAs(PowermaxZoneConfiguration.class).zoneNumber & 0x000000FF)),
161 command.equals(OnOffType.ON));
163 logger.debug("Command of type {} while OnOffType is expected. Command is ignored.",
164 command.getClass().getSimpleName());
168 powermaxBridgeHandler.x10Command(
170 (byte) (getConfigAs(PowermaxX10Configuration.class).deviceNumber & 0x000000FF)),
174 logger.debug("No available command for channel {}. Command is ignored.", channelUID.getId());
181 * Update channel to match a new alarm system state
183 * @param channel: the channel
184 * @param state: the alarm system state
186 public void updateChannelFromAlarmState(String channel, @Nullable PowermaxState state) {
187 if (state == null || !isLinked(channel)) {
191 if (getThing().getThingTypeUID().equals(THING_TYPE_ZONE)) {
192 int num = getConfigAs(PowermaxZoneConfiguration.class).zoneNumber;
194 for (Value<?> value : state.getZone(num).getValues()) {
195 String vChannel = value.getChannel();
197 if (channel.equals(vChannel) && (value.getValue() != null)) {
198 updateState(vChannel, value.getState());
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);
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()) {
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());
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);
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);
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()) {
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());
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);
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);
269 logger.debug("Using name '{}' for {}", getThing().getLabel(), getThing().getUID());
270 zoneSettings.setName(getThing().getLabel());
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));
283 public PowermaxZoneConfiguration getZoneConfiguration() {
284 return getConfigAs(PowermaxZoneConfiguration.class);
287 public PowermaxX10Configuration getX10Configuration() {
288 return getConfigAs(PowermaxX10Configuration.class);