2 * Copyright (c) 2010-2022 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 java.util.Collection;
18 import java.util.Collections;
19 import java.util.EventObject;
20 import java.util.List;
22 import java.util.concurrent.CopyOnWriteArrayList;
23 import java.util.concurrent.ScheduledFuture;
24 import java.util.concurrent.TimeUnit;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.powermax.internal.config.PowermaxIpConfiguration;
29 import org.openhab.binding.powermax.internal.config.PowermaxSerialConfiguration;
30 import org.openhab.binding.powermax.internal.discovery.PowermaxDiscoveryService;
31 import org.openhab.binding.powermax.internal.message.PowermaxCommManager;
32 import org.openhab.binding.powermax.internal.message.PowermaxSendType;
33 import org.openhab.binding.powermax.internal.state.PowermaxArmMode;
34 import org.openhab.binding.powermax.internal.state.PowermaxPanelSettings;
35 import org.openhab.binding.powermax.internal.state.PowermaxPanelSettingsListener;
36 import org.openhab.binding.powermax.internal.state.PowermaxPanelType;
37 import org.openhab.binding.powermax.internal.state.PowermaxState;
38 import org.openhab.binding.powermax.internal.state.PowermaxStateContainer.Value;
39 import org.openhab.binding.powermax.internal.state.PowermaxStateEvent;
40 import org.openhab.binding.powermax.internal.state.PowermaxStateEventListener;
41 import org.openhab.binding.powermax.internal.state.PowermaxZoneSettings;
42 import org.openhab.core.i18n.TimeZoneProvider;
43 import org.openhab.core.io.transport.serial.SerialPortManager;
44 import org.openhab.core.library.types.OnOffType;
45 import org.openhab.core.library.types.StringType;
46 import org.openhab.core.thing.Bridge;
47 import org.openhab.core.thing.ChannelUID;
48 import org.openhab.core.thing.Thing;
49 import org.openhab.core.thing.ThingStatus;
50 import org.openhab.core.thing.ThingStatusDetail;
51 import org.openhab.core.thing.binding.BaseBridgeHandler;
52 import org.openhab.core.thing.binding.ThingHandlerService;
53 import org.openhab.core.types.Command;
54 import org.openhab.core.types.RefreshType;
55 import org.openhab.core.types.UnDefType;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
60 * The {@link PowermaxBridgeHandler} is responsible for handling commands, which are
61 * sent to one of the channels.
63 * @author Laurent Garnier - Initial contribution
66 public class PowermaxBridgeHandler extends BaseBridgeHandler implements PowermaxStateEventListener {
68 private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
69 private static final long FIVE_MINUTES = TimeUnit.MINUTES.toMillis(5);
71 private static final int NB_EVENT_LOG = 10;
73 private static final PowermaxPanelType DEFAULT_PANEL_TYPE = PowermaxPanelType.POWERMAX_PRO;
75 private static final int JOB_REPEAT = 20;
77 private static final int MAX_DOWNLOAD_ATTEMPTS = 3;
79 private final Logger logger = LoggerFactory.getLogger(PowermaxBridgeHandler.class);
81 private final SerialPortManager serialPortManager;
82 private final TimeZoneProvider timeZoneProvider;
84 private final List<PowermaxPanelSettingsListener> listeners = new CopyOnWriteArrayList<>();
86 private @Nullable ScheduledFuture<?> globalJob;
88 /** The delay in milliseconds to reset a motion detection */
89 private long motionOffDelay;
91 /** The PIN code to use for arming/disarming the Powermax alarm system from openHAB */
92 private String pinCode;
94 /** Force the standard mode rather than trying using the Powerlink mode */
95 private boolean forceStandardMode;
97 /** The object to store the current state of the Powermax alarm system */
98 private PowermaxState currentState;
100 /** The object in charge of the communication with the Powermax alarm system */
101 private @Nullable PowermaxCommManager commManager;
103 private int remainingDownloadAttempts;
105 public PowermaxBridgeHandler(Bridge thing, SerialPortManager serialPortManager, TimeZoneProvider timeZoneProvider) {
107 this.serialPortManager = serialPortManager;
108 this.timeZoneProvider = timeZoneProvider;
110 this.currentState = new PowermaxState(new PowermaxPanelSettings(DEFAULT_PANEL_TYPE), timeZoneProvider);
114 public Collection<Class<? extends ThingHandlerService>> getServices() {
115 return Collections.singleton(PowermaxDiscoveryService.class);
118 public @Nullable PowermaxState getCurrentState() {
119 PowermaxCommManager localCommManager = commManager;
120 return (localCommManager == null) ? null : currentState;
123 public @Nullable PowermaxPanelSettings getPanelSettings() {
124 PowermaxCommManager localCommManager = commManager;
125 return (localCommManager == null) ? null : localCommManager.getPanelSettings();
129 public void initialize() {
130 logger.debug("initializing handler for thing {}", getThing().getUID());
134 String threadName = "OH-binding-" + getThing().getUID().getAsString();
136 String errorMsg = String.format("@text/offline.config-error-unexpected-thing-type [ \"%s\" ]",
137 getThing().getThingTypeUID().getAsString());
138 if (getThing().getThingTypeUID().equals(BRIDGE_TYPE_SERIAL)) {
139 errorMsg = initializeBridgeSerial(getConfigAs(PowermaxSerialConfiguration.class), threadName);
140 } else if (getThing().getThingTypeUID().equals(BRIDGE_TYPE_IP)) {
141 errorMsg = initializeBridgeIp(getConfigAs(PowermaxIpConfiguration.class), threadName);
144 if (errorMsg == null) {
145 ScheduledFuture<?> job = globalJob;
146 if (job == null || job.isCancelled()) {
147 // Delay the startup in case the handler is restarted immediately
148 globalJob = scheduler.scheduleWithFixedDelay(() -> {
150 logger.trace("Powermax job...");
151 updateMotionSensorState();
152 updateRingingState();
155 retryDownloadSetup();
159 } catch (Exception e) {
160 logger.warn("Exception in scheduled job: {}", e.getMessage(), e);
162 }, 10, JOB_REPEAT, TimeUnit.SECONDS);
165 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg);
169 private @Nullable String initializeBridgeSerial(PowermaxSerialConfiguration config, String threadName) {
170 String errorMsg = null;
171 String serialPort = config.serialPort.trim();
172 if (!serialPort.isEmpty() && !serialPort.startsWith("rfc2217")) {
173 motionOffDelay = config.motionOffDelay * ONE_MINUTE;
174 pinCode = config.pinCode;
175 forceStandardMode = config.forceStandardMode;
176 PowermaxPanelType panelType = getPanelTypeSetting(config.panelType, DEFAULT_PANEL_TYPE);
178 PowermaxArmMode.DISARMED.setAllowedCommand(config.allowDisarming);
179 PowermaxArmMode.ARMED_HOME.setAllowedCommand(config.allowArming);
180 PowermaxArmMode.ARMED_AWAY.setAllowedCommand(config.allowArming);
181 PowermaxArmMode.ARMED_HOME_INSTANT.setAllowedCommand(config.allowArming);
182 PowermaxArmMode.ARMED_AWAY_INSTANT.setAllowedCommand(config.allowArming);
183 PowermaxArmMode.ARMED_NIGHT.setAllowedCommand(config.allowArming);
184 PowermaxArmMode.ARMED_NIGHT_INSTANT.setAllowedCommand(config.allowArming);
186 commManager = new PowermaxCommManager(serialPort, panelType, forceStandardMode, config.autoSyncTime,
187 serialPortManager, threadName, timeZoneProvider);
189 if (serialPort.startsWith("rfc2217")) {
190 errorMsg = "@text/offline.config-error-invalid-thing-type";
192 errorMsg = "@text/offline.config-error-mandatory-serial-port";
198 private @Nullable String initializeBridgeIp(PowermaxIpConfiguration config, String threadName) {
199 String errorMsg = null;
200 String ip = config.ip.trim();
201 if (!ip.isEmpty() && config.tcpPort > 0) {
202 motionOffDelay = config.motionOffDelay * ONE_MINUTE;
203 pinCode = config.pinCode;
204 forceStandardMode = config.forceStandardMode;
205 PowermaxPanelType panelType = getPanelTypeSetting(config.panelType, DEFAULT_PANEL_TYPE);
207 PowermaxArmMode.DISARMED.setAllowedCommand(config.allowDisarming);
208 PowermaxArmMode.ARMED_HOME.setAllowedCommand(config.allowArming);
209 PowermaxArmMode.ARMED_AWAY.setAllowedCommand(config.allowArming);
210 PowermaxArmMode.ARMED_HOME_INSTANT.setAllowedCommand(config.allowArming);
211 PowermaxArmMode.ARMED_AWAY_INSTANT.setAllowedCommand(config.allowArming);
212 PowermaxArmMode.ARMED_NIGHT.setAllowedCommand(config.allowArming);
213 PowermaxArmMode.ARMED_NIGHT_INSTANT.setAllowedCommand(config.allowArming);
215 commManager = new PowermaxCommManager(ip, config.tcpPort, panelType, forceStandardMode, config.autoSyncTime,
216 threadName, timeZoneProvider);
218 errorMsg = "@text/offline.config-error-mandatory-ip-port";
224 public void dispose() {
225 logger.debug("Handler disposed for thing {}", getThing().getUID());
226 ScheduledFuture<?> job = globalJob;
227 if (job != null && !job.isCancelled()) {
237 * Set the state of items linked to motion sensors to OFF when the last trip is older
238 * than the value defined by the variable motionOffDelay
240 private void updateMotionSensorState() {
241 PowermaxCommManager localCommManager = commManager;
242 if (localCommManager != null) {
243 long now = System.currentTimeMillis();
244 boolean update = false;
245 PowermaxState updateState = localCommManager.createNewState();
246 PowermaxPanelSettings panelSettings = localCommManager.getPanelSettings();
247 for (int i = 1; i <= panelSettings.getNbZones(); i++) {
248 PowermaxZoneSettings zoneSettings = panelSettings.getZoneSettings(i);
249 if (zoneSettings != null && zoneSettings.isMotionSensor()
250 && currentState.getZone(i).isLastTripBeforeTime(now - motionOffDelay)) {
252 updateState.getZone(i).tripped.setValue(false);
256 updateChannelsFromAlarmState(TRIPPED, updateState);
257 currentState.merge(updateState);
263 * Turn off the Ringing flag when the bell time expires
265 private void updateRingingState() {
266 PowermaxCommManager localCommManager = commManager;
267 if (localCommManager != null && Boolean.TRUE.equals(currentState.ringing.getValue())) {
268 long now = System.currentTimeMillis();
269 long bellTime = localCommManager.getPanelSettings().getBellTime() * ONE_MINUTE;
271 Long ringingSince = currentState.ringingSince.getValue();
272 if (ringingSince != null && (ringingSince + bellTime) < now) {
273 PowermaxState updateState = localCommManager.createNewState();
274 updateState.ringing.setValue(false);
275 updateChannelsFromAlarmState(RINGING, updateState);
276 currentState.merge(updateState);
282 * Check that we're actively communicating with the panel
284 private void checkKeepAlive() {
285 PowermaxCommManager localCommManager = commManager;
286 if (localCommManager == null) {
289 long now = System.currentTimeMillis();
290 Long lastKeepAlive = currentState.lastKeepAlive.getValue();
291 Long lastMessageTime = currentState.lastMessageTime.getValue();
292 if (Boolean.TRUE.equals(currentState.powerlinkMode.getValue()) && (lastKeepAlive != null)
293 && ((now - lastKeepAlive) > ONE_MINUTE)) {
294 // In Powerlink mode: let Powermax know we are alive
295 localCommManager.sendRestoreMessage();
296 currentState.lastKeepAlive.setValue(now);
297 } else if (!Boolean.TRUE.equals(currentState.downloadMode.getValue()) && (lastMessageTime != null)
298 && ((now - lastMessageTime) > FIVE_MINUTES)) {
299 // In Standard mode: ping the panel every so often to detect disconnects
300 localCommManager.sendMessage(PowermaxSendType.STATUS);
304 private void tryReconnect() {
305 PowermaxCommManager localCommManager = commManager;
306 if (localCommManager == null) {
309 logger.info("Trying to connect or reconnect...");
311 currentState = localCommManager.createNewState();
314 logger.debug("openConnection(): connected");
315 updateStatus(ThingStatus.ONLINE);
316 updateChannelsFromAlarmState(currentState);
317 if (forceStandardMode) {
318 currentState.powerlinkMode.setValue(false);
319 updateChannelsFromAlarmState(MODE, currentState);
320 processPanelSettings();
322 localCommManager.startDownload();
324 } catch (Exception e) {
325 logger.debug("openConnection(): {}", e.getMessage(), e);
326 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
327 setAllChannelsOffline();
332 * Open a TCP or Serial connection to the Powermax Alarm Panel
334 * @return true if the connection has been opened
336 private synchronized void openConnection() throws Exception {
337 PowermaxCommManager localCommManager = commManager;
338 if (localCommManager != null) {
339 localCommManager.addEventListener(this);
340 localCommManager.open();
342 remainingDownloadAttempts = MAX_DOWNLOAD_ATTEMPTS;
346 * Close TCP or Serial connection to the Powermax Alarm Panel and remove the Event Listener
348 private synchronized void closeConnection() {
349 PowermaxCommManager localCommManager = commManager;
350 if (localCommManager != null) {
351 localCommManager.close();
352 localCommManager.removeEventListener(this);
354 logger.debug("closeConnection(): disconnected");
357 private boolean isConnected() {
358 PowermaxCommManager localCommManager = commManager;
359 return localCommManager == null ? false : localCommManager.isConnected();
362 private void retryDownloadSetup() {
363 PowermaxCommManager localCommManager = commManager;
364 if (localCommManager != null) {
365 localCommManager.retryDownloadSetup(remainingDownloadAttempts);
370 public void handleCommand(ChannelUID channelUID, Command command) {
371 logger.debug("Received command {} from channel {}", command, channelUID.getId());
373 if (command instanceof RefreshType) {
374 updateChannelsFromAlarmState(channelUID.getId(), getCurrentState());
376 switch (channelUID.getId()) {
379 PowermaxArmMode armMode = PowermaxArmMode.fromShortName(command.toString());
381 } catch (IllegalArgumentException e) {
382 logger.debug("Powermax alarm binding: invalid command {}", command);
386 if (command instanceof OnOffType) {
388 command.equals(OnOffType.ON) ? PowermaxArmMode.ARMED_AWAY : PowermaxArmMode.DISARMED);
390 logger.debug("Command of type {} while OnOffType is expected. Command is ignored.",
391 command.getClass().getSimpleName());
397 case UPDATE_EVENT_LOGS:
404 logger.debug("No available command for channel {}. Command is ignored.", channelUID.getId());
410 private void armCommand(PowermaxArmMode armMode) {
411 PowermaxCommManager localCommManager = commManager;
412 if (localCommManager == null) {
413 logger.debug("Powermax alarm binding not correctly initialized. Arm command is ignored.");
414 } else if (!isConnected()) {
415 logger.debug("Powermax alarm binding not connected. Arm command is ignored.");
417 localCommManager.requestArmMode(armMode,
418 Boolean.TRUE.equals(currentState.powerlinkMode.getValue())
419 ? localCommManager.getPanelSettings().getFirstPinCode()
424 private void pgmCommand(Command command) {
425 PowermaxCommManager localCommManager = commManager;
426 if (localCommManager == null) {
427 logger.debug("Powermax alarm binding not correctly initialized. PGM command is ignored.");
428 } else if (!isConnected()) {
429 logger.debug("Powermax alarm binding not connected. PGM command is ignored.");
431 localCommManager.sendPGMX10(command, null);
435 public void x10Command(Byte deviceNr, Command command) {
436 PowermaxCommManager localCommManager = commManager;
437 if (localCommManager == null) {
438 logger.debug("Powermax alarm binding not correctly initialized. X10 command is ignored.");
439 } else if (!isConnected()) {
440 logger.debug("Powermax alarm binding not connected. X10 command is ignored.");
442 localCommManager.sendPGMX10(command, deviceNr);
446 public void zoneBypassed(byte zoneNr, boolean bypassed) {
447 PowermaxCommManager localCommManager = commManager;
448 if (localCommManager == null) {
449 logger.debug("Powermax alarm binding not correctly initialized. Zone bypass command is ignored.");
450 } else if (!isConnected()) {
451 logger.debug("Powermax alarm binding not connected. Zone bypass command is ignored.");
452 } else if (!Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) {
453 logger.debug("Powermax alarm binding: Bypass option only supported in Powerlink mode");
454 } else if (!localCommManager.getPanelSettings().isBypassEnabled()) {
455 logger.debug("Powermax alarm binding: Bypass option not enabled in panel settings");
457 localCommManager.sendZoneBypass(bypassed, zoneNr, localCommManager.getPanelSettings().getFirstPinCode());
461 private void downloadEventLog() {
462 PowermaxCommManager localCommManager = commManager;
463 if (localCommManager == null) {
464 logger.debug("Powermax alarm binding not correctly initialized. Event logs command is ignored.");
465 } else if (!isConnected()) {
466 logger.debug("Powermax alarm binding not connected. Event logs command is ignored.");
468 localCommManager.requestEventLog(Boolean.TRUE.equals(currentState.powerlinkMode.getValue())
469 ? localCommManager.getPanelSettings().getFirstPinCode()
474 public void downloadSetup() {
475 PowermaxCommManager localCommManager = commManager;
476 if (localCommManager == null) {
477 logger.debug("Powermax alarm binding not correctly initialized. Download setup command is ignored.");
478 } else if (!isConnected()) {
479 logger.debug("Powermax alarm binding not connected. Download setup command is ignored.");
480 } else if (!Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) {
481 logger.debug("Powermax alarm binding: download setup only supported in Powerlink mode");
482 } else if (localCommManager.isDownloadRunning()) {
483 logger.debug("Powermax alarm binding: download setup not started as one is in progress");
485 localCommManager.startDownload();
486 if (currentState.lastKeepAlive.getValue() != null) {
487 currentState.lastKeepAlive.setValue(System.currentTimeMillis());
492 public String getInfoSetup() {
493 PowermaxPanelSettings panelSettings = getPanelSettings();
494 return (panelSettings == null) ? "" : panelSettings.getInfo();
498 public void onNewStateEvent(EventObject event) {
499 PowermaxCommManager localCommManager = commManager;
500 if (localCommManager == null) {
504 PowermaxStateEvent stateEvent = (PowermaxStateEvent) event;
505 PowermaxState updateState = stateEvent.getState();
507 if (Boolean.TRUE.equals(currentState.powerlinkMode.getValue())
508 && Boolean.TRUE.equals(updateState.downloadSetupRequired.getValue())) {
509 // After Enrolling Powerlink or if a reset is required
510 logger.debug("Powermax alarm binding: Reset");
511 localCommManager.startDownload();
512 updateState.downloadSetupRequired.setValue(false);
513 if (currentState.lastKeepAlive.getValue() != null) {
514 currentState.lastKeepAlive.setValue(System.currentTimeMillis());
516 } else if (Boolean.FALSE.equals(currentState.powerlinkMode.getValue())
517 && updateState.lastKeepAlive.getValue() != null) {
518 // Were are in standard mode but received a keep alive message
519 // so we switch in PowerLink mode
520 logger.debug("Powermax alarm binding: Switching to Powerlink mode");
521 localCommManager.startDownload();
524 boolean doProcessSettings = (updateState.powerlinkMode.getValue() != null);
526 PowermaxPanelSettings panelSettings = localCommManager.getPanelSettings();
527 panelSettings.getZoneRange().forEach(i -> {
528 if (Boolean.TRUE.equals(updateState.getZone(i).armed.getValue())
529 && Boolean.TRUE.equals(currentState.getZone(i).bypassed.getValue())) {
530 updateState.getZone(i).armed.setValue(false);
534 updateState.keepOnlyDifferencesWith(currentState);
535 updateChannelsFromAlarmState(updateState);
536 currentState.merge(updateState);
538 if (!updateState.getUpdatedZoneNames().isEmpty()) {
539 for (Integer zoneIdx : updateState.getUpdatedZoneNames().keySet()) {
540 if (panelSettings.getZoneSettings(zoneIdx) != null) {
541 for (PowermaxPanelSettingsListener listener : listeners) {
542 listener.onZoneSettingsUpdated(zoneIdx, panelSettings);
548 if (doProcessSettings) {
549 // There is a change of mode (standard or Powerlink)
550 processPanelSettings();
551 localCommManager.exitDownload();
556 public void onCommunicationFailure(String message) {
557 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
558 setAllChannelsOffline();
561 private void processPanelSettings() {
562 PowermaxCommManager localCommManager = commManager;
563 if (localCommManager == null) {
567 if (localCommManager.processPanelSettings(Boolean.TRUE.equals(currentState.powerlinkMode.getValue()))) {
568 for (PowermaxPanelSettingsListener listener : listeners) {
569 listener.onPanelSettingsUpdated(localCommManager.getPanelSettings());
571 remainingDownloadAttempts = 0;
573 logger.info("Powermax alarm binding: setup download failed!");
574 for (PowermaxPanelSettingsListener listener : listeners) {
575 listener.onPanelSettingsUpdated(null);
577 remainingDownloadAttempts--;
579 updatePropertiesFromPanelSettings();
580 if (Boolean.TRUE.equals(currentState.powerlinkMode.getValue())) {
581 logger.info("Powermax alarm binding: running in Powerlink mode");
582 localCommManager.sendRestoreMessage();
584 logger.info("Powermax alarm binding: running in Standard mode");
585 localCommManager.getInfosWhenInStandardMode();
590 * Update channels to match a new alarm system state
592 * @param state: the alarm system state
594 private void updateChannelsFromAlarmState(@Nullable PowermaxState state) {
595 updateChannelsFromAlarmState(null, state);
599 * Update channels to match a new alarm system state
601 * @param channel: filter on a particular channel; if null, consider all channels
602 * @param state: the alarm system state
604 private synchronized void updateChannelsFromAlarmState(@Nullable String channel, @Nullable PowermaxState state) {
605 if (state == null || !isConnected()) {
609 for (Value<?> value : state.getValues()) {
610 String vChannel = value.getChannel();
612 if (((channel == null) || channel.equals(vChannel)) && isLinked(vChannel) && (value.getValue() != null)) {
613 updateState(vChannel, value.getState());
617 for (int i = 1; i <= NB_EVENT_LOG; i++) {
618 String channel2 = String.format(EVENT_LOG, i);
619 String log = state.getEventLog(i);
620 if (((channel == null) || channel.equals(channel2)) && isLinked(channel2) && (log != null)) {
621 updateState(channel2, new StringType(log));
625 for (Thing thing : getThing().getThings()) {
626 if (thing.getHandler() != null) {
627 PowermaxThingHandler handler = (PowermaxThingHandler) thing.getHandler();
628 if (handler != null) {
629 if (thing.getThingTypeUID().equals(THING_TYPE_ZONE)) {
630 // All of the zone state objects will have the same list of values.
631 // The use of getZone(1) here is just to get any PowermaxZoneState
632 // and use it to get the list of zone channels.
634 for (Value<?> value : state.getZone(1).getValues()) {
635 String channelId = value.getChannel();
636 if ((channel == null) || channel.equals(channelId)) {
637 handler.updateChannelFromAlarmState(channelId, state);
640 } else if (thing.getThingTypeUID().equals(THING_TYPE_X10)) {
641 if ((channel == null) || channel.equals(X10_STATUS)) {
642 handler.updateChannelFromAlarmState(X10_STATUS, state);
651 * Update all channels to an UNDEF state to indicate that communication with the panel is offline
653 private synchronized void setAllChannelsOffline() {
654 getThing().getChannels().forEach(c -> updateState(c.getUID(), UnDefType.UNDEF));
658 * Update properties to match the alarm panel settings
660 private void updatePropertiesFromPanelSettings() {
661 PowermaxPanelSettings panelSettings = getPanelSettings();
662 if (panelSettings == null) {
666 Map<String, String> properties = editProperties();
667 value = panelSettings.getPanelType().getLabel();
668 if (!value.isEmpty()) {
669 properties.put(Thing.PROPERTY_MODEL_ID, value);
671 value = panelSettings.getPanelSerial();
672 if (value != null && !value.isEmpty()) {
673 properties.put(Thing.PROPERTY_SERIAL_NUMBER, value);
675 value = panelSettings.getPanelEprom();
676 if (value != null && !value.isEmpty()) {
677 properties.put(Thing.PROPERTY_HARDWARE_VERSION, value);
679 value = panelSettings.getPanelSoftware();
680 if (value != null && !value.isEmpty()) {
681 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, value);
683 updateProperties(properties);
686 public boolean registerPanelSettingsListener(PowermaxPanelSettingsListener listener) {
687 boolean inList = true;
688 if (!listeners.contains(listener)) {
689 inList = listeners.add(listener);
694 public boolean unregisterPanelSettingsListener(PowermaxPanelSettingsListener listener) {
695 return listeners.remove(listener);
698 private PowermaxPanelType getPanelTypeSetting(String value, PowermaxPanelType defaultValue) {
699 PowermaxPanelType result;
701 result = PowermaxPanelType.fromLabel(value);
702 } catch (IllegalArgumentException e) {
703 result = defaultValue;
704 logger.debug("Powermax alarm binding: panel type not configured correctly");