]> git.basschouten.com Git - openhab-addons.git/blob
8cadbcc720f7ff6b4f797d92440ce904d37bf2a0
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.max.internal.handler;
14
15 import static org.openhab.binding.max.internal.MaxBindingConstants.*;
16 import static org.openhab.core.library.unit.SIUnits.CELSIUS;
17
18 import java.math.BigDecimal;
19 import java.text.DateFormat;
20 import java.text.SimpleDateFormat;
21 import java.util.Calendar;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Set;
27 import java.util.concurrent.ScheduledFuture;
28 import java.util.concurrent.TimeUnit;
29
30 import org.openhab.binding.max.internal.actions.MaxDevicesActions;
31 import org.openhab.binding.max.internal.command.CCommand;
32 import org.openhab.binding.max.internal.command.QCommand;
33 import org.openhab.binding.max.internal.command.SConfigCommand;
34 import org.openhab.binding.max.internal.command.SConfigCommand.ConfigCommandType;
35 import org.openhab.binding.max.internal.command.ZCommand;
36 import org.openhab.binding.max.internal.device.Device;
37 import org.openhab.binding.max.internal.device.DeviceType;
38 import org.openhab.binding.max.internal.device.EcoSwitch;
39 import org.openhab.binding.max.internal.device.HeatingThermostat;
40 import org.openhab.binding.max.internal.device.ShutterContact;
41 import org.openhab.binding.max.internal.device.ThermostatModeType;
42 import org.openhab.core.config.core.Configuration;
43 import org.openhab.core.library.types.DecimalType;
44 import org.openhab.core.library.types.OpenClosedType;
45 import org.openhab.core.library.types.QuantityType;
46 import org.openhab.core.library.types.StringType;
47 import org.openhab.core.thing.Bridge;
48 import org.openhab.core.thing.ChannelUID;
49 import org.openhab.core.thing.Thing;
50 import org.openhab.core.thing.ThingStatus;
51 import org.openhab.core.thing.ThingStatusDetail;
52 import org.openhab.core.thing.ThingStatusInfo;
53 import org.openhab.core.thing.ThingUID;
54 import org.openhab.core.thing.binding.BaseThingHandler;
55 import org.openhab.core.thing.binding.ThingHandler;
56 import org.openhab.core.thing.binding.ThingHandlerService;
57 import org.openhab.core.types.Command;
58 import org.openhab.core.types.RefreshType;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 /**
63  * The {@link MaxDevicesHandler} is responsible for handling commands, which are
64  * sent to one of the channels.
65  *
66  * @author Marcel Verpaalen - Initial contribution
67  */
68 public class MaxDevicesHandler extends BaseThingHandler implements DeviceStatusListener {
69
70     private final Logger logger = LoggerFactory.getLogger(MaxDevicesHandler.class);
71     private MaxCubeBridgeHandler bridgeHandler;
72
73     private String maxDeviceSerial;
74     private String rfAddress;
75     private boolean propertiesSet;
76     private boolean configSet;
77
78     // actual refresh variables
79     public static final int REFRESH_ACTUAL_MIN_RATE = 10; // minutes
80     public static final int REFRESH_ACTUAL_DURATION = 120; // seconds
81     private static final long COMMUNICATION_DELAY_TIME = 120;
82     private int refreshActualRate;
83     private boolean refreshingActuals;
84     private ScheduledFuture<?> refreshActualsJob;
85     private double originalSetTemp;
86     private ThermostatModeType originalMode;
87
88     public MaxDevicesHandler(Thing thing) {
89         super(thing);
90     }
91
92     @Override
93     public void initialize() {
94         try {
95             final Configuration config = getThing().getConfiguration();
96             final String configDeviceId = (String) config.get(Thing.PROPERTY_SERIAL_NUMBER);
97
98             try {
99                 refreshActualRate = ((BigDecimal) config.get(PROPERTY_REFRESH_ACTUAL_RATE)).intValueExact();
100             } catch (Exception e) {
101                 refreshActualRate = 0;
102             }
103
104             if (configDeviceId != null) {
105                 maxDeviceSerial = configDeviceId;
106             }
107             if (maxDeviceSerial != null) {
108                 logger.debug("Initialized MAX! device handler for {}.", maxDeviceSerial);
109             } else {
110                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
111                         "Initialized MAX! device missing serialNumber configuration");
112             }
113             propertiesSet = false;
114             configSet = false;
115             getMaxCubeBridgeHandler();
116         } catch (Exception e) {
117             logger.debug("Exception occurred during initialize : {}", e.getMessage(), e);
118             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
119         }
120     }
121
122     @Override
123     public void dispose() {
124         logger.debug("Disposing MAX! device {} {}.", getThing().getUID(), maxDeviceSerial);
125         if (refreshingActuals) {
126             refreshActualsRestore();
127         }
128         if (refreshActualsJob != null && !refreshActualsJob.isCancelled()) {
129             refreshActualsJob.cancel(true);
130             refreshActualsJob = null;
131         }
132         if (bridgeHandler != null) {
133             logger.trace("Clear MAX! device {} {} from bridge.", getThing().getUID(), maxDeviceSerial);
134             bridgeHandler.clearDeviceList();
135             bridgeHandler.unregisterDeviceStatusListener(this);
136             bridgeHandler = null;
137         }
138         logger.debug("Disposed MAX! device {} {}.", getThing().getUID(), maxDeviceSerial);
139         super.dispose();
140     }
141
142     @Override
143     public void thingUpdated(Thing thing) {
144         configSet = false;
145         super.thingUpdated(thing);
146     }
147
148     @Override
149     public void handleConfigurationUpdate(Map<String, Object> configurationParameters) {
150         logger.debug("MAX! Device {}: Configuration update received", getThing().getUID());
151         boolean temperaturePropertyUpdateNeeded = false;
152         final Device device = getMaxCubeBridgeHandler().getDevice(maxDeviceSerial);
153
154         final Map<String, Object> deviceProperties = device == null ? new HashMap<>() : device.getProperties();
155         final Configuration configuration = editConfiguration();
156         for (final Entry<String, Object> configurationParameter : configurationParameters.entrySet()) {
157             logger.debug("MAX! Device {}: Configuration update {} to {}", getThing().getUID(),
158                     configurationParameter.getKey(), configurationParameter.getValue());
159
160             // Test if it is a part of the configuration properties.
161             // With the update all parameters are sends, so we need to determine which ones really changed.
162             if (deviceProperties.containsKey(configurationParameter.getKey())) {
163                 if (deviceProperties.get(configurationParameter.getKey()).equals(configurationParameter.getValue())) {
164                     logger.trace("Device {} Property {} value {} unchanged.", getThing().getUID(),
165                             configurationParameter.getKey(), configurationParameter.getValue());
166                 } else if (configurationParameter.getValue().getClass() == BigDecimal.class
167                         && ((BigDecimal) deviceProperties.get(configurationParameter.getKey()))
168                                 .compareTo((BigDecimal) configurationParameter.getValue()) == 0) {
169                     logger.trace("Device {} Property {} value {} unchanged.", getThing().getUID(),
170                             configurationParameter.getKey(), configurationParameter.getValue());
171                 } else {
172                     logger.debug("Device {} Property {} value {} -> {} changed.", getThing().getUID(),
173                             configurationParameter.getKey(), deviceProperties.get(configurationParameter.getKey()),
174                             configurationParameter.getValue());
175                     temperaturePropertyUpdateNeeded = true;
176                 }
177             }
178             if (configurationParameter.getKey().equals(PROPERTY_DEVICENAME)
179                     || configurationParameter.getKey().equals(PROPERTY_ROOMID)) {
180                 updateDeviceName(configurationParameter);
181             }
182             if (configurationParameter.getKey().startsWith("action-")) {
183                 if (configurationParameter.getValue().toString().equals(BUTTON_ACTION_VALUE)) {
184                     configurationParameter.setValue(BigDecimal.valueOf(BUTTON_NOACTION_VALUE));
185                     if (configurationParameter.getKey().equals(ACTION_DEVICE_DELETE)) {
186                         deviceDelete();
187                     }
188                 }
189             }
190             configuration.put(configurationParameter.getKey(), configurationParameter.getValue());
191         }
192         // Persist changes and restart with new parameters
193         updateConfiguration(configuration);
194         if (temperaturePropertyUpdateNeeded) {
195             sendPropertyUpdate(configurationParameters, deviceProperties);
196         }
197     }
198
199     @Override
200     public Collection<Class<? extends ThingHandlerService>> getServices() {
201         return Set.of(MaxDevicesActions.class);
202     }
203
204     private void sendPropertyUpdate(Map<String, Object> configurationParameters, Map<String, Object> deviceProperties) {
205         if (getMaxCubeBridgeHandler() == null) {
206             logger.debug("MAX! Cube LAN gateway bridge handler not found. Cannot handle update without bridge.");
207             return;
208         }
209
210         try {
211             Device device = getMaxCubeBridgeHandler().getDevice(maxDeviceSerial);
212             rfAddress = device.getRFAddress();
213             int roomId = device.getRoomId();
214             BigDecimal tempComfort = (BigDecimal) configurationParameters.getOrDefault(PROPERTY_THERMO_COMFORT_TEMP,
215                     deviceProperties.get(PROPERTY_THERMO_COMFORT_TEMP));
216             BigDecimal tempEco = (BigDecimal) configurationParameters.getOrDefault(PROPERTY_THERMO_ECO_TEMP,
217                     deviceProperties.get(PROPERTY_THERMO_ECO_TEMP));
218             BigDecimal tempSetpointMax = (BigDecimal) configurationParameters.getOrDefault(
219                     PROPERTY_THERMO_MAX_TEMP_SETPOINT, deviceProperties.get(PROPERTY_THERMO_MAX_TEMP_SETPOINT));
220             BigDecimal tempSetpointMin = (BigDecimal) configurationParameters.getOrDefault(
221                     PROPERTY_THERMO_MIN_TEMP_SETPOINT, deviceProperties.get(PROPERTY_THERMO_MIN_TEMP_SETPOINT));
222             BigDecimal tempOffset = (BigDecimal) configurationParameters.getOrDefault(PROPERTY_THERMO_OFFSET_TEMP,
223                     deviceProperties.get(PROPERTY_THERMO_OFFSET_TEMP));
224             BigDecimal tempOpenWindow = (BigDecimal) configurationParameters.getOrDefault(
225                     PROPERTY_THERMO_WINDOW_OPEN_TEMP, deviceProperties.get(PROPERTY_THERMO_WINDOW_OPEN_TEMP));
226             BigDecimal durationOpenWindow = (BigDecimal) configurationParameters.getOrDefault(
227                     PROPERTY_THERMO_WINDOW_OPEN_DURATION, deviceProperties.get(PROPERTY_THERMO_WINDOW_OPEN_DURATION));
228             SConfigCommand cmd = new SConfigCommand(rfAddress, roomId, tempComfort.doubleValue(), tempEco.doubleValue(),
229                     tempSetpointMax.doubleValue(), tempSetpointMin.doubleValue(), tempOffset.doubleValue(),
230                     tempOpenWindow.doubleValue(), durationOpenWindow.intValue());
231             bridgeHandler.queueCommand(new SendCommand(maxDeviceSerial, cmd, "Update Thermostat Properties"));
232             sendCCommand();
233         } catch (Exception e) {
234             logger.debug("Exception occurred during execution: {}", e.getMessage(), e);
235         }
236     }
237
238     /**
239      * Trigger update by sending C command.
240      * This command is delayed as it takes time to have the updates back from the thermostat
241      */
242     private void sendCCommand() {
243         scheduler.schedule(() -> {
244             CCommand cmd = new CCommand(rfAddress);
245             bridgeHandler.queueCommand(new SendCommand(maxDeviceSerial, cmd, "Refresh Thermostat Properties"));
246             configSet = false;
247         }, COMMUNICATION_DELAY_TIME, TimeUnit.SECONDS);
248     }
249
250     /**
251      * sends the T command to the Cube to disassociate the device from the MAX! Cube.
252      */
253     public void deviceDelete() {
254         MaxCubeBridgeHandler maxCubeBridge = getMaxCubeBridgeHandler();
255         if (maxCubeBridge != null) {
256             maxCubeBridge.sendDeviceDelete(maxDeviceSerial);
257             dispose();
258         }
259     }
260
261     /**
262      * Updates the device & roomname
263      */
264     private void updateDeviceName(Entry<String, Object> configurationParameter) {
265         try {
266             final Device device = getMaxCubeBridgeHandler().getDevice(maxDeviceSerial);
267             if (device == null) {
268                 logger.debug("MAX! Cube LAN gateway bridge handler not found. Cannot handle update without bridge.");
269                 return;
270             }
271             switch (configurationParameter.getKey()) {
272                 case PROPERTY_DEVICENAME:
273                     final String name = configurationParameter.getValue().toString();
274                     if (!name.equals(device.getName())) {
275                         logger.debug("Updating device name for {} to {}", getThing().getUID(), name);
276                         device.setName(name);
277                         bridgeHandler.sendDeviceAndRoomNameUpdate(name);
278                         bridgeHandler.queueCommand(new SendCommand(maxDeviceSerial, new QCommand(), "Reload Data"));
279                     }
280                     break;
281
282                 case PROPERTY_ROOMID: // fall-through
283                 case PROPERTY_ROOMNAME:
284                     final int roomId = ((BigDecimal) configurationParameter.getValue()).intValue();
285                     if (roomId != device.getRoomId()) {
286                         logger.debug("Updating room for {} to {}", getThing().getUID().getAsString(), roomId);
287                         device.setRoomId(roomId);
288                         // TODO: handle if a room has no more devices, probably should be deleted. Also handle if room
289                         // rfId
290                         // is no longer valid as the related device is movd to another room
291                         bridgeHandler.sendDeviceAndRoomNameUpdate(Integer.toString(roomId));
292                         SendCommand sendCommand = new SendCommand(maxDeviceSerial,
293                                 ZCommand.wakeupDevice(device.getRFAddress()),
294                                 "WakeUp device" + getThing().getUID().getAsString());
295                         bridgeHandler.queueCommand(sendCommand);
296                         sendCommand = new SendCommand(maxDeviceSerial,
297                                 new SConfigCommand(device.getRFAddress(), roomId, ConfigCommandType.SetRoom),
298                                 "Set Room");
299                         bridgeHandler.queueCommand(sendCommand);
300
301                         sendCommand = new SendCommand(maxDeviceSerial, new QCommand(), "Reload Data");
302                         bridgeHandler.queueCommand(sendCommand);
303                         sendCCommand();
304                     }
305             }
306         } catch (Exception e) {
307             logger.debug("Exception occurred during execution: {}", e.getMessage(), e);
308         }
309     }
310
311     private synchronized MaxCubeBridgeHandler getMaxCubeBridgeHandler() {
312         if (this.bridgeHandler == null) {
313             final Bridge bridge = getBridge();
314             if (bridge == null) {
315                 logger.debug("Required bridge not defined for device {}.", maxDeviceSerial);
316                 return null;
317             }
318             final ThingHandler handler = bridge.getHandler();
319             if (!(handler instanceof MaxCubeBridgeHandler)) {
320                 logger.debug("No available bridge handler found for {} bridge {} .", maxDeviceSerial, bridge.getUID());
321                 return null;
322             }
323             this.bridgeHandler = (MaxCubeBridgeHandler) handler;
324             this.bridgeHandler.registerDeviceStatusListener(this);
325         }
326         return this.bridgeHandler;
327     }
328
329     @Override
330     public void handleCommand(ChannelUID channelUID, Command command) {
331         final MaxCubeBridgeHandler maxCubeBridge = getMaxCubeBridgeHandler();
332         if (maxCubeBridge == null) {
333             logger.debug("MAX! Cube LAN gateway bridge handler not found. Cannot handle command without bridge.");
334             return;
335         }
336         if (command instanceof RefreshType) {
337             maxCubeBridge.handleCommand(channelUID, command);
338             return;
339         }
340         if (maxDeviceSerial == null) {
341             logger.warn("Serial number missing. Can't send command to device '{}'", getThing());
342             return;
343         }
344         switch (channelUID.getId()) {
345             case CHANNEL_SETTEMP:
346                 if (refreshingActuals) {
347                     refreshActualsRestore();
348                 }
349                 maxCubeBridge.queueCommand(new SendCommand(maxDeviceSerial, channelUID, command));
350                 break;
351             case CHANNEL_MODE:
352                 if (refreshingActuals) {
353                     refreshActualsRestore();
354                 }
355                 maxCubeBridge.queueCommand(new SendCommand(maxDeviceSerial, channelUID, command));
356                 break;
357             default:
358                 logger.warn("Setting of channel '{}' not possible, channel is read-only.", channelUID);
359                 break;
360         }
361     }
362
363     @Override
364     public void onDeviceStateChanged(ThingUID bridge, Device device) {
365         if (!device.getSerialNumber().equals(maxDeviceSerial)) {
366             return;
367         }
368         if (device.isError() || device.isLinkStatusError()) {
369             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR);
370         } else if (!refreshingActuals) {
371             updateStatus(ThingStatus.ONLINE);
372         } else {
373             updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "Updating Actual Temperature");
374         }
375         if (!propertiesSet) {
376             setProperties(device);
377         }
378         if (!configSet) {
379             setDeviceConfiguration(device);
380         }
381         if (refreshActualRate >= REFRESH_ACTUAL_MIN_RATE && (device.getType() == DeviceType.HeatingThermostat
382                 || device.getType() == DeviceType.HeatingThermostatPlus)) {
383             refreshActualCheck((HeatingThermostat) device);
384         }
385         logger.debug("Updating states of {} {} ({}) id: {}", device.getType(), device.getName(),
386                 device.getSerialNumber(), getThing().getUID());
387         switch (device.getType()) {
388             case WallMountedThermostat: // fall-through
389             case HeatingThermostat: // fall-through
390             case HeatingThermostatPlus:
391                 updateState(new ChannelUID(getThing().getUID(), CHANNEL_LOCKED),
392                         ((HeatingThermostat) device).isPanelLocked() ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
393                 updateState(new ChannelUID(getThing().getUID(), CHANNEL_SETTEMP),
394                         new QuantityType<>(((HeatingThermostat) device).getTemperatureSetpoint(), CELSIUS));
395                 updateState(new ChannelUID(getThing().getUID(), CHANNEL_MODE),
396                         new StringType(((HeatingThermostat) device).getModeString()));
397                 updateState(new ChannelUID(getThing().getUID(), CHANNEL_BATTERY),
398                         ((HeatingThermostat) device).getBatteryLow());
399                 updateState(new ChannelUID(getThing().getUID(), CHANNEL_VALVE),
400                         new DecimalType(((HeatingThermostat) device).getValvePosition()));
401                 double actualTemp = ((HeatingThermostat) device).getTemperatureActual();
402                 if (actualTemp != 0) {
403                     updateState(new ChannelUID(getThing().getUID(), CHANNEL_ACTUALTEMP),
404                             new QuantityType<>(actualTemp, CELSIUS));
405                 }
406                 break;
407             case ShutterContact:
408                 updateState(new ChannelUID(getThing().getUID(), CHANNEL_CONTACT_STATE),
409                         ((ShutterContact) device).getShutterState());
410                 updateState(new ChannelUID(getThing().getUID(), CHANNEL_BATTERY),
411                         ((ShutterContact) device).getBatteryLow());
412                 break;
413             case EcoSwitch:
414                 updateState(new ChannelUID(getThing().getUID(), CHANNEL_BATTERY), ((EcoSwitch) device).getBatteryLow());
415                 break;
416             default:
417                 logger.debug("Unhandled Device {}.", device.getType());
418                 break;
419         }
420         device.setUpdated(false);
421     }
422
423     private void refreshActualCheck(HeatingThermostat device) {
424         if (device.getActualTempLastUpdated() == null) {
425             Calendar t = Calendar.getInstance();
426             t.add(Calendar.MINUTE, REFRESH_ACTUAL_MIN_RATE * -1);
427             device.setActualTempLastUpdated(t.getTime());
428             logger.debug("Actual date reset for {} {} ({}) id: {}", device.getType(), device.getName(),
429                     device.getSerialNumber(), getThing().getUID());
430         }
431         long timediff = Calendar.getInstance().getTime().getTime() - device.getActualTempLastUpdated().getTime();
432         if (timediff > ((long) refreshActualRate) * 1000 * 60) {
433             if (!refreshingActuals) {
434                 logger.debug("Actual needs updating for {} {} ({}) id: {}", device.getType(), device.getName(),
435                         device.getSerialNumber(), getThing().getUID());
436
437                 originalSetTemp = device.getTemperatureSetpoint();
438                 originalMode = device.getMode();
439
440                 if (originalMode == ThermostatModeType.MANUAL || originalMode == ThermostatModeType.AUTOMATIC) {
441                     double tempSetTemp = originalSetTemp + 0.5;
442                     logger.debug("Actuals Refresh: Setting Temp {}", tempSetTemp);
443                     handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SETTEMP),
444                             new QuantityType<>(tempSetTemp, CELSIUS));
445                     refreshingActuals = true;
446                 } else {
447                     logger.debug("Defer Actuals refresh. Only manual refresh for mode AUTOMATIC & MANUAL");
448                     device.setActualTempLastUpdated(Calendar.getInstance().getTime());
449                 }
450
451                 if (refreshingActuals) {
452                     updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "Updating Actual Temperature");
453
454                     if (refreshActualsJob == null || refreshActualsJob.isCancelled()) {
455                         refreshActualsJob = scheduler.schedule(this::refreshActualsRestore, REFRESH_ACTUAL_DURATION,
456                                 TimeUnit.SECONDS);
457                     }
458
459                     device.setActualTempLastUpdated(Calendar.getInstance().getTime());
460                 }
461             }
462             logger.debug("Actual Refresh in progress for {} {} ({}) id: {}", device.getType(), device.getName(),
463                     device.getSerialNumber(), getThing().getUID());
464         } else {
465             if (logger.isTraceEnabled()) {
466                 final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
467                 logger.trace("Actual date for {} {} ({}) : {}", device.getType(), device.getName(),
468                         device.getSerialNumber(), dateFormat.format(device.getActualTempLastUpdated().getTime()));
469             }
470         }
471     }
472
473     /**
474      * Send the commands to restore the original settings for mode & temperature
475      * to end the automatic update cycle
476      */
477     private synchronized void refreshActualsRestore() {
478         try {
479             refreshingActuals = false;
480             if (originalMode == ThermostatModeType.AUTOMATIC || originalMode == ThermostatModeType.MANUAL) {
481                 logger.debug("Finished Actuals Refresh: Restoring Temp {}", originalSetTemp);
482                 handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SETTEMP),
483                         new QuantityType<>(originalSetTemp, CELSIUS));
484             }
485
486             if (refreshActualsJob != null && !refreshActualsJob.isCancelled()) {
487                 refreshActualsJob.cancel(true);
488                 refreshActualsJob = null;
489             }
490         } catch (Exception e) {
491             logger.debug("Exception occurred during Actuals Refresh : {}", e.getMessage(), e);
492         }
493     }
494
495     @Override
496     public void onDeviceRemoved(MaxCubeBridgeHandler bridge, Device device) {
497         if (device.getSerialNumber().equals(maxDeviceSerial)) {
498             bridgeHandler.unregisterDeviceStatusListener(this);
499             bridgeHandler = null;
500             updateStatus(ThingStatus.OFFLINE);
501         }
502     }
503
504     @Override
505     public void onDeviceAdded(Bridge bridge, Device device) {
506     }
507
508     /**
509      * Set the properties for this device
510      */
511     private void setProperties(Device device) {
512         try {
513             logger.debug("MAX! {} {} properties update", device.getType(), device.getSerialNumber());
514             Map<String, String> properties = editProperties();
515             properties.put(Thing.PROPERTY_MODEL_ID, device.getType().toString());
516             properties.put(Thing.PROPERTY_SERIAL_NUMBER, device.getSerialNumber());
517             properties.put(Thing.PROPERTY_VENDOR, PROPERTY_VENDOR_NAME);
518             updateProperties(properties);
519             logger.debug("properties updated");
520             propertiesSet = true;
521         } catch (Exception e) {
522             logger.debug("Exception occurred during property edit: {}", e.getMessage(), e);
523         }
524     }
525
526     @Override
527     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
528         logger.debug("Bridge Status updated to {} for device: {}", bridgeStatusInfo.getStatus(), getThing().getUID());
529         if (!bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
530             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
531         }
532     }
533
534     /**
535      * Set the Configurable properties for this device
536      */
537     private void setDeviceConfiguration(Device device) {
538         try {
539             boolean config_changed = false;
540             logger.debug("MAX! {} {} configuration update", device.getType(), device.getSerialNumber());
541             Configuration configuration = editConfiguration();
542             if (!device.getRoomName().equalsIgnoreCase((String) getConfig().get(PROPERTY_ROOMNAME))) {
543                 configuration.put(PROPERTY_ROOMNAME, device.getRoomName());
544                 config_changed = true;
545             }
546             if (getConfig().get(PROPERTY_ROOMID) == null || new BigDecimal(device.getRoomId())
547                     .compareTo((BigDecimal) getConfig().get(PROPERTY_ROOMID)) != 0) {
548                 configuration.put(PROPERTY_ROOMID, new BigDecimal(device.getRoomId()));
549                 config_changed = true;
550             }
551             if (!device.getName().equalsIgnoreCase((String) getConfig().get(PROPERTY_DEVICENAME))) {
552                 configuration.put(PROPERTY_DEVICENAME, device.getName());
553                 config_changed = true;
554             }
555             if (!device.getRFAddress().equalsIgnoreCase((String) getConfig().get(PROPERTY_RFADDRESS))) {
556                 configuration.put(PROPERTY_RFADDRESS, device.getRFAddress());
557                 config_changed = true;
558             }
559             for (Map.Entry<String, Object> entry : device.getProperties().entrySet()) {
560                 configuration.put(entry.getKey(), entry.getValue());
561             }
562             if (config_changed) {
563                 updateConfiguration(configuration);
564                 logger.debug("Config updated: {}", configuration.getProperties());
565             } else {
566                 logger.debug("MAX! {} {} no updated required.", device.getType(), device.getSerialNumber());
567             }
568             configSet = true;
569         } catch (Exception e) {
570             logger.debug("Exception occurred during configuration edit: {}", e.getMessage(), e);
571         }
572     }
573
574     @Override
575     public void onDeviceConfigUpdate(Bridge bridge, Device device) {
576         if (device.getSerialNumber().equals(maxDeviceSerial)) {
577             setDeviceConfiguration(device);
578         }
579     }
580 }