]> git.basschouten.com Git - openhab-addons.git/blob
8e888742b9bec3bfe379d3b4efbba0c0e229746e
[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.digitalstrom.internal.lib.manager.impl;
14
15 import java.util.Arrays;
16 import java.util.Calendar;
17 import java.util.HashMap;
18 import java.util.LinkedList;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.concurrent.ScheduledExecutorService;
23 import java.util.concurrent.ScheduledFuture;
24 import java.util.concurrent.TimeUnit;
25
26 import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance;
27 import org.openhab.binding.digitalstrom.internal.lib.config.Config;
28 import org.openhab.binding.digitalstrom.internal.lib.event.EventListener;
29 import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventNames;
30 import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventResponseEnum;
31 import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem;
32 import org.openhab.binding.digitalstrom.internal.lib.listener.ConnectionListener;
33 import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
34 import org.openhab.binding.digitalstrom.internal.lib.listener.ManagerStatusListener;
35 import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
36 import org.openhab.binding.digitalstrom.internal.lib.listener.TotalPowerConsumptionListener;
37 import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerStates;
38 import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerTypes;
39 import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
40 import org.openhab.binding.digitalstrom.internal.lib.manager.DeviceStatusManager;
41 import org.openhab.binding.digitalstrom.internal.lib.manager.SceneManager;
42 import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
43 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.SceneReadingJobExecutor;
44 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.SensorJobExecutor;
45 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
46 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.DeviceConsumptionSensorJob;
47 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.DeviceOutputValueSensorJob;
48 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.SceneConfigReadingJob;
49 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.SceneOutputValueReadingJob;
50 import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
51 import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
52 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit;
53 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
54 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.CachedMeteringValue;
55 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceConstants;
56 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
57 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
58 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringTypeEnum;
59 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringUnitsEnum;
60 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
61 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
62 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
63 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceStateUpdateImpl;
64 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.impl.DeviceImpl;
65 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
66 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.ApartmentSceneEnum;
67 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
68 import org.openhab.core.common.ThreadPoolManager;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 import com.google.gson.JsonElement;
73 import com.google.gson.JsonObject;
74
75 /**
76  * The {@link DeviceStatusManagerImpl} is the implementation of the {@link DeviceStatusManager}.
77  *
78  * @author Michael Ochel - Initial contribution
79  * @author Matthias Siegele - Initial contribution
80  */
81 public class DeviceStatusManagerImpl implements DeviceStatusManager {
82
83     private final Logger logger = LoggerFactory.getLogger(DeviceStatusManagerImpl.class);
84
85     /**
86      * Contains all supported event-types.
87      */
88     public static final List<String> SUPPORTED_EVENTS = Arrays.asList(EventNames.DEVICE_SENSOR_VALUE,
89             EventNames.DEVICE_BINARY_INPUT_EVENT);
90
91     private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(Config.THREADPOOL_NAME);
92     private ScheduledFuture<?> pollingScheduler;
93
94     /**
95      * Query to get all {@link Device}'s with more informations than {@link DsAPI#getApartmentDevices(String)}. Can be
96      * executed with {@link DsAPI#query(String, String)} or {@link DsAPI#query2(String, String)}.
97      */
98     public static final String GET_DETAILD_DEVICES = "/apartment/zones/zone0(*)/devices/*(*)/*(*)/*(*)";
99     /**
100      * Query to get the last called scenes of all groups in digitalSTROM. Can be executed with
101      * {@link DsAPI#query(String, String)} or
102      * {@link DsAPI#query2(String, String)}.
103      */
104     public static final String LAST_CALL_SCENE_QUERY = "/apartment/zones/*(*)/groups/*(*)/*(*)";
105
106     private ConnectionManager connMan;
107     private StructureManager strucMan;
108     private SceneManager sceneMan;
109     private DsAPI digitalSTROMClient;
110     private Config config;
111
112     private SensorJobExecutor sensorJobExecutor;
113     private SceneReadingJobExecutor sceneJobExecutor;
114     private EventListener eventListener;
115
116     private final List<TrashDevice> trashDevices = new LinkedList<>();
117
118     private long lastBinCheck = 0;
119     private ManagerStates state = ManagerStates.STOPPED;
120
121     private int tempConsumption = 0;
122     private int tempEnergyMeter = 0;
123     private int tempEnergyMeterWs = 0;
124
125     private DeviceStatusListener deviceDiscovery;
126     private TotalPowerConsumptionListener totalPowerConsumptionListener;
127     private ManagerStatusListener statusListener;
128
129     /**
130      * Creates a new {@link DeviceStatusManagerImpl} through the given {@link Config} object, which has to be contains
131      * all needed parameters like host address, authentication data and so on. This constructor the
132      * {@link DeviceStatusManagerImpl} will be create all needed managers itself.
133      *
134      * @param config (must not be null)
135      */
136     public DeviceStatusManagerImpl(Config config) {
137         init(new ConnectionManagerImpl(config), null, null, null, null);
138     }
139
140     /**
141      * Creates a new {@link DeviceStatusManagerImpl}. The given fields needed to create {@link ConnectionManager}
142      * through the constructor {@link ConnectionManagerImpl#ConnectionManagerImpl(String, String, String, String)}. All
143      * other needed manager will be automatically created, too.
144      *
145      * @param hostAddress (must not be null)
146      * @param user (can be null, if appToken is set)
147      * @param password (can be null, if appToken is set)
148      * @param appToken (can be null, if user and password is set)
149      */
150     public DeviceStatusManagerImpl(String hostAddress, String user, String password, String appToken) {
151         init(new ConnectionManagerImpl(hostAddress, user, password, false), null, null, null, null);
152     }
153
154     /**
155      * Creates a new {@link DeviceStatusManagerImpl} with the given managers. If the {@link StructureManager} or
156      * {@link SceneManager} is null, they will be automatically created.
157      *
158      * @param connMan (must not be null)
159      * @param strucMan (can be null)
160      * @param sceneMan (can be null)
161      */
162     public DeviceStatusManagerImpl(ConnectionManager connMan, StructureManager strucMan, SceneManager sceneMan) {
163         init(connMan, strucMan, sceneMan, null, null);
164     }
165
166     /**
167      * Same constructor like {@link #DeviceStatusManagerImpl(ConnectionManager, StructureManager, SceneManager)}, but a
168      * {@link ManagerStatusListener} can be set, too.
169      *
170      * @param connMan (must not be null)
171      * @param strucMan (can be null)
172      * @param sceneMan (can be null)
173      * @param statusListener (can be null)
174      * @see #DeviceStatusManagerImpl(ConnectionManager, StructureManager, SceneManager)
175      */
176     public DeviceStatusManagerImpl(ConnectionManager connMan, StructureManager strucMan, SceneManager sceneMan,
177             ManagerStatusListener statusListener) {
178         init(connMan, strucMan, sceneMan, statusListener, null);
179     }
180
181     /**
182      * Same constructor like
183      * {@link #DeviceStatusManagerImpl(ConnectionManager, StructureManager, SceneManager, ManagerStatusListener)}, but a
184      * {@link EventListener} can be set, too.
185      *
186      * @param connMan (must not be null)
187      * @param strucMan (can be null)
188      * @param sceneMan (can be null)
189      * @param statusListener (can be null)
190      * @param eventListener (can be null)
191      * @see #DeviceStatusManagerImpl(ConnectionManager, StructureManager, SceneManager, ManagerStatusListener)
192      */
193     public DeviceStatusManagerImpl(ConnectionManager connMan, StructureManager strucMan, SceneManager sceneMan,
194             ManagerStatusListener statusListener, EventListener eventListener) {
195         init(connMan, strucMan, sceneMan, statusListener, eventListener);
196     }
197
198     /**
199      * Creates a new {@link DeviceStatusManagerImpl} with the given {@link ConnectionManager}. The
200      * {@link StructureManager} and
201      * {@link SceneManager} will be automatically created.
202      *
203      * @param connMan (must not be null)
204      */
205     public DeviceStatusManagerImpl(ConnectionManager connMan) {
206         init(connMan, null, null, null, null);
207     }
208
209     private void init(ConnectionManager connMan, StructureManager strucMan, SceneManager sceneMan,
210             ManagerStatusListener statusListener, EventListener eventListener) {
211         this.connMan = connMan;
212         this.digitalSTROMClient = connMan.getDigitalSTROMAPI();
213         this.config = connMan.getConfig();
214         if (strucMan != null) {
215             this.strucMan = strucMan;
216         } else {
217             this.strucMan = new StructureManagerImpl();
218         }
219         if (sceneMan != null) {
220             this.sceneMan = sceneMan;
221         } else {
222             this.sceneMan = new SceneManagerImpl(connMan, strucMan, statusListener);
223         }
224         this.statusListener = statusListener;
225         this.eventListener = eventListener;
226     }
227
228     /**
229      * Check and updates the {@link Device} structure, configurations and status.
230      *
231      * @author Michael Ochel - initial contributer
232      * @author Matthias Siegele - initial contributer
233      */
234     private class PollingRunnable implements Runnable {
235         private boolean devicesLoaded = false;
236         private long nextSensorUpdate = 0;
237
238         @Override
239         public void run() {
240             if (!getManagerState().equals(ManagerStates.RUNNING)) {
241                 logger.debug("Thread started");
242                 if (devicesLoaded) {
243                     stateChanged(ManagerStates.RUNNING);
244                 } else {
245                     stateChanged(ManagerStates.INITIALIZING);
246                 }
247             }
248             Map<DSID, Device> tempDeviceMap;
249             if (strucMan.getDeviceMap() != null) {
250                 tempDeviceMap = strucMan.getDeviceMap();
251             } else {
252                 tempDeviceMap = new HashMap<>();
253             }
254
255             List<Device> currentDeviceList = getDetailedDevices();
256
257             // update the current total power consumption
258             if (nextSensorUpdate <= System.currentTimeMillis()) {
259                 // check circuits
260                 List<Circuit> circuits = digitalSTROMClient.getApartmentCircuits(connMan.getSessionToken());
261                 for (Circuit circuit : circuits) {
262                     if (strucMan.getCircuitByDSID(circuit.getDSID()) != null) {
263                         if (!circuit.equals(strucMan.getCircuitByDSID(circuit.getDSID()))) {
264                             strucMan.updateCircuitConfig(circuit);
265                         }
266                     } else {
267                         strucMan.addCircuit(circuit);
268                         if (deviceDiscovery != null) {
269                             deviceDiscovery.onDeviceAdded(circuit);
270                         }
271                     }
272                 }
273                 getMeterData();
274                 nextSensorUpdate = System.currentTimeMillis() + config.getTotalPowerUpdateInterval();
275             }
276
277             while (!currentDeviceList.isEmpty()) {
278                 Device currentDevice = currentDeviceList.remove(0);
279                 DSID currentDeviceDSID = currentDevice.getDSID();
280                 Device device = tempDeviceMap.remove(currentDeviceDSID);
281
282                 if (device != null) {
283                     checkDeviceConfig(currentDevice, device);
284
285                     if (device.isPresent()) {
286                         // check device state updates
287                         while (!device.isDeviceUpToDate()) {
288                             DeviceStateUpdate deviceStateUpdate = device.getNextDeviceUpdateState();
289                             if (deviceStateUpdate != null) {
290                                 switch (deviceStateUpdate.getType()) {
291                                     case DeviceStateUpdate.OUTPUT:
292                                     case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
293                                     case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
294                                         filterCommand(deviceStateUpdate, device);
295                                         break;
296                                     case DeviceStateUpdate.UPDATE_SCENE_CONFIG:
297                                     case DeviceStateUpdate.UPDATE_SCENE_OUTPUT:
298                                         updateSceneData(device, deviceStateUpdate);
299                                         break;
300                                     case DeviceStateUpdate.UPDATE_OUTPUT_VALUE:
301                                         if (deviceStateUpdate.getValueAsInteger() > -1) {
302                                             readOutputValue(device);
303                                         } else {
304                                             removeSensorJob(device, deviceStateUpdate);
305                                         }
306                                         break;
307                                     default:
308                                         sendComandsToDSS(device, deviceStateUpdate);
309                                 }
310                             }
311                         }
312                     }
313
314                 } else {
315                     logger.debug("Found new device!");
316                     if (trashDevices.isEmpty()) {
317                         currentDevice.setConfig(config);
318                         strucMan.addDeviceToStructure(currentDevice);
319                         logger.debug("trashDevices are empty, add Device with dSID {} to the deviceMap!",
320                                 currentDevice.getDSID());
321                     } else {
322                         logger.debug("Search device in trashDevices.");
323                         TrashDevice foundTrashDevice = null;
324                         for (TrashDevice trashDevice : trashDevices) {
325                             if (trashDevice != null) {
326                                 if (trashDevice.getDevice().equals(currentDevice)) {
327                                     foundTrashDevice = trashDevice;
328                                     logger.debug(
329                                             "Found device in trashDevices, add TrashDevice with dSID {} to the StructureManager!",
330                                             currentDeviceDSID);
331                                 }
332                             }
333                         }
334                         if (foundTrashDevice != null) {
335                             trashDevices.remove(foundTrashDevice);
336                             strucMan.addDeviceToStructure(foundTrashDevice.getDevice());
337                         } else {
338                             strucMan.addDeviceToStructure(currentDevice);
339                             logger.debug(
340                                     "Can't find device in trashDevices, add Device with dSID: {} to the StructureManager!",
341                                     currentDeviceDSID);
342                         }
343                     }
344                     if (deviceDiscovery != null) {
345                         // only informs discovery, if the device is a output or a sensor device
346                         deviceDiscovery.onDeviceAdded(currentDevice);
347                         logger.debug("inform DeviceStatusListener: {} about added device with dSID {}",
348                                 DeviceStatusListener.DEVICE_DISCOVERY, currentDevice.getDSID().getValue());
349                     } else {
350                         logger.debug(
351                                 "The device discovery is not registrated, can't inform device discovery about found device.");
352                     }
353                 }
354             }
355
356             if (!devicesLoaded && strucMan.getDeviceMap() != null) {
357                 if (!strucMan.getDeviceMap().values().isEmpty()) {
358                     logger.debug("Devices loaded");
359                     devicesLoaded = true;
360                     setInizialStateWithLastCallScenes();
361                     stateChanged(ManagerStates.RUNNING);
362                 } else {
363                     logger.debug("No devices found");
364                 }
365             }
366
367             if (!sceneMan.scenesGenerated() && devicesLoaded
368                     && !sceneMan.getManagerState().equals(ManagerStates.GENERATING_SCENES)) {
369                 logger.debug("{}", sceneMan.getManagerState());
370                 sceneMan.generateScenes();
371             }
372
373             for (Device device : tempDeviceMap.values()) {
374                 logger.debug("Found removed devices.");
375
376                 trashDevices.add(new TrashDevice(device));
377                 DeviceStatusListener listener = device.unregisterDeviceStatusListener();
378                 if (listener != null) {
379                     listener.onDeviceRemoved(null);
380                 }
381                 strucMan.deleteDevice(device);
382                 logger.debug("Add device with dSID {} to trashDevices", device.getDSID().getValue());
383
384                 if (deviceDiscovery != null) {
385                     deviceDiscovery.onDeviceRemoved(device);
386                     logger.debug("inform DeviceStatusListener: {} about removed device with dSID {}",
387                             DeviceStatusListener.DEVICE_DISCOVERY, device.getDSID().getValue());
388                 } else {
389                     logger.debug(
390                             "The device-Discovery is not registrated, can't inform device discovery about removed device.");
391                 }
392             }
393
394             if (!trashDevices.isEmpty() && (lastBinCheck + config.getBinCheckTime() < System.currentTimeMillis())) {
395                 for (TrashDevice trashDevice : trashDevices) {
396                     if (trashDevice.isTimeToDelete(Calendar.getInstance().get(Calendar.DAY_OF_YEAR))) {
397                         logger.debug("Found trashDevice that have to delete!");
398                         trashDevices.remove(trashDevice);
399                         logger.debug("Delete trashDevice: {}", trashDevice.getDevice().getDSID().getValue());
400                     }
401                 }
402                 lastBinCheck = System.currentTimeMillis();
403             }
404         }
405
406         private List<Device> getDetailedDevices() {
407             List<Device> deviceList = new LinkedList<>();
408             JsonObject result = connMan.getDigitalSTROMAPI().query2(connMan.getSessionToken(), GET_DETAILD_DEVICES);
409             if (result != null && result.isJsonObject()) {
410                 if (result.getAsJsonObject().get(GeneralLibConstance.QUERY_BROADCAST_ZONE_STRING).isJsonObject()) {
411                     result = result.getAsJsonObject().get(GeneralLibConstance.QUERY_BROADCAST_ZONE_STRING)
412                             .getAsJsonObject();
413                     for (Entry<String, JsonElement> entry : result.entrySet()) {
414                         if (!(entry.getKey().equals(JSONApiResponseKeysEnum.ZONE_ID.getKey())
415                                 && entry.getKey().equals(JSONApiResponseKeysEnum.NAME.getKey()))
416                                 && entry.getValue().isJsonObject()) {
417                             deviceList.add(new DeviceImpl(entry.getValue().getAsJsonObject()));
418                         }
419                     }
420                 }
421             }
422             return deviceList;
423         }
424
425         private void filterCommand(DeviceStateUpdate deviceStateUpdate, Device device) {
426             DeviceStateUpdate intDeviceStateUpdate = deviceStateUpdate;
427             String stateUpdateType = intDeviceStateUpdate.getType();
428             short newAngle = 0;
429             if (stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_INCREASE)
430                     || stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_DECREASE)) {
431                 newAngle = device.getAnglePosition();
432             }
433             DeviceStateUpdate nextDeviceStateUpdate = device.getNextDeviceUpdateState();
434             while (nextDeviceStateUpdate != null && nextDeviceStateUpdate.getType().equals(stateUpdateType)) {
435                 switch (stateUpdateType) {
436                     case DeviceStateUpdate.OUTPUT:
437                         intDeviceStateUpdate = nextDeviceStateUpdate;
438                         nextDeviceStateUpdate = device.getNextDeviceUpdateState();
439                         break;
440                     case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
441                         if (intDeviceStateUpdate.getValueAsInteger() == 1) {
442                             newAngle = (short) (newAngle + DeviceConstants.ANGLE_STEP_SLAT);
443                         }
444                         break;
445                     case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
446                         if (intDeviceStateUpdate.getValueAsInteger() == 1) {
447                             newAngle = (short) (newAngle - DeviceConstants.ANGLE_STEP_SLAT);
448                         }
449                         break;
450                 }
451             }
452             if (stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_INCREASE)
453                     || stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_DECREASE)) {
454                 if (newAngle > device.getMaxSlatAngle()) {
455                     newAngle = (short) device.getMaxSlatAngle();
456                 }
457                 if (newAngle < device.getMinSlatAngle()) {
458                     newAngle = (short) device.getMinSlatAngle();
459                 }
460                 if (!(stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_INCREASE) && checkAngleIsMinMax(device) == 1)
461                         || !(stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_DECREASE)
462                                 && checkAngleIsMinMax(device) == 0)) {
463                     intDeviceStateUpdate = new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, newAngle);
464                 }
465             }
466             sendComandsToDSS(device, intDeviceStateUpdate);
467             if (nextDeviceStateUpdate != null) {
468                 if (intDeviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_CONFIG
469                         || intDeviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_OUTPUT) {
470                     updateSceneData(device, intDeviceStateUpdate);
471                 } else {
472                     sendComandsToDSS(device, intDeviceStateUpdate);
473                 }
474             }
475         }
476     }
477
478     private void removeSensorJob(Device device, DeviceStateUpdate deviceStateUpdate) {
479         switch (deviceStateUpdate.getType()) {
480             case DeviceStateUpdate.UPDATE_SCENE_CONFIG:
481                 if (sceneJobExecutor != null) {
482                     sceneJobExecutor.removeSensorJob(device,
483                             SceneConfigReadingJob.getID(device, deviceStateUpdate.getSceneId()));
484                 }
485                 break;
486             case DeviceStateUpdate.UPDATE_SCENE_OUTPUT:
487                 if (sceneJobExecutor != null) {
488                     sceneJobExecutor.removeSensorJob(device,
489                             SceneOutputValueReadingJob.getID(device, deviceStateUpdate.getSceneId()));
490                 }
491                 break;
492             case DeviceStateUpdate.UPDATE_OUTPUT_VALUE:
493                 if (sensorJobExecutor != null) {
494                     sensorJobExecutor.removeSensorJob(device, DeviceOutputValueSensorJob.getID(device));
495                 }
496                 break;
497         }
498         if (deviceStateUpdate.isSensorUpdateType()) {
499             if (sensorJobExecutor != null) {
500                 logger.debug("remove SensorJob with ID: {}",
501                         DeviceConsumptionSensorJob.getID(device, deviceStateUpdate.getTypeAsSensorEnum()));
502                 sensorJobExecutor.removeSensorJob(device,
503                         DeviceConsumptionSensorJob.getID(device, deviceStateUpdate.getTypeAsSensorEnum()));
504             }
505         }
506     }
507
508     @Override
509     public ManagerTypes getManagerType() {
510         return ManagerTypes.DEVICE_STATUS_MANAGER;
511     }
512
513     @Override
514     public synchronized ManagerStates getManagerState() {
515         return state;
516     }
517
518     private synchronized void stateChanged(ManagerStates state) {
519         if (statusListener != null) {
520             this.state = state;
521             statusListener.onStatusChanged(ManagerTypes.DEVICE_STATUS_MANAGER, state);
522         }
523     }
524
525     @Override
526     public synchronized void start() {
527         logger.debug("start DeviceStatusManager");
528         if (pollingScheduler == null || pollingScheduler.isCancelled()) {
529             pollingScheduler = scheduler.scheduleWithFixedDelay(new PollingRunnable(), 0, config.getPollingFrequency(),
530                     TimeUnit.MILLISECONDS);
531             logger.debug("start pollingScheduler");
532         }
533         sceneMan.start();
534         if (sceneJobExecutor != null) {
535             this.sceneJobExecutor.startExecutor();
536         }
537
538         if (sensorJobExecutor != null) {
539             this.sensorJobExecutor.startExecutor();
540         }
541         if (eventListener != null) {
542             eventListener.addEventHandler(this);
543         } else {
544             eventListener = new EventListener(connMan, this);
545             eventListener.start();
546         }
547     }
548
549     @Override
550     public synchronized void stop() {
551         logger.debug("stop DeviceStatusManager");
552         stateChanged(ManagerStates.STOPPED);
553         if (sceneMan != null) {
554             sceneMan.stop();
555         }
556         if (pollingScheduler != null && !pollingScheduler.isCancelled()) {
557             pollingScheduler.cancel(true);
558             pollingScheduler = null;
559             logger.debug("stop pollingScheduler");
560         }
561         if (sceneJobExecutor != null) {
562             this.sceneJobExecutor.shutdown();
563         }
564         if (sensorJobExecutor != null) {
565             this.sensorJobExecutor.shutdown();
566         }
567         if (eventListener != null) {
568             eventListener.removeEventHandler(this);
569         }
570     }
571
572     /**
573      * The {@link TrashDevice} saves not present {@link Device}'s, but at this point not deleted from the
574      * digitalSTROM-System, temporary to get back the configuration of the {@link Device}'s faster.
575      *
576      * @author Michael Ochel - Initial contribution
577      * @author Matthias Siegele - Initial contribution
578      */
579     private class TrashDevice {
580         private final Device device;
581         private final int timestamp;
582
583         /**
584          * Creates a new {@link TrashDevice}.
585          *
586          * @param device to put in {@link TrashDevice}
587          */
588         public TrashDevice(Device device) {
589             this.device = device;
590             this.timestamp = Calendar.getInstance().get(Calendar.DAY_OF_YEAR);
591         }
592
593         /**
594          * Returns the saved {@link Device}.
595          *
596          * @return device
597          */
598         public Device getDevice() {
599             return device;
600         }
601
602         /**
603          * Returns true if the time for the {@link TrashDevice} is over and it can be deleted.
604          *
605          * @param dayOfYear day of the current year
606          * @return true = time to delete | false = not time to delete
607          */
608         public boolean isTimeToDelete(int dayOfYear) {
609             return this.timestamp + config.getTrashDeviceDeleteTime() <= dayOfYear;
610         }
611
612         @Override
613         public boolean equals(Object object) {
614             return object instanceof TrashDevice
615                     ? this.device.getDSID().equals(((TrashDevice) object).getDevice().getDSID())
616                     : false;
617         }
618     }
619
620     private void checkDeviceConfig(Device newDevice, Device internalDevice) {
621         if (newDevice == null || internalDevice == null) {
622             return;
623         }
624         // check device availability has changed and informs the deviceStatusListener about the change.
625         // NOTE:
626         // The device is not availability for the digitalSTROM-Server, it has not been deleted and therefore it is set
627         // to
628         // OFFLINE.
629         // An alternate algorithm is responsible for deletion.
630         if (newDevice.isPresent() != internalDevice.isPresent()) {
631             internalDevice.setIsPresent(newDevice.isPresent());
632         }
633         if (newDevice.getMeterDSID() != null && !newDevice.getMeterDSID().equals(internalDevice.getMeterDSID())) {
634             internalDevice.setMeterDSID(newDevice.getMeterDSID().getValue());
635         }
636         if (newDevice.getFunctionalColorGroup() != null
637                 && !newDevice.getFunctionalColorGroup().equals(internalDevice.getFunctionalColorGroup())) {
638             internalDevice.setFunctionalColorGroup(newDevice.getFunctionalColorGroup());
639         }
640         if (newDevice.getName() != null && !newDevice.getName().equals(internalDevice.getName())) {
641             internalDevice.setName(newDevice.getName());
642         }
643         if (newDevice.getOutputMode() != null && !newDevice.getOutputMode().equals(internalDevice.getOutputMode())) {
644             if (deviceDiscovery != null) {
645                 if (OutputModeEnum.DISABLED.equals(internalDevice.getOutputMode())
646                         || OutputModeEnum.outputModeIsTemperationControlled(internalDevice.getOutputMode())) {
647                     deviceDiscovery.onDeviceAdded(newDevice);
648                 }
649                 if (OutputModeEnum.DISABLED.equals(newDevice.getOutputMode())
650                         || OutputModeEnum.outputModeIsTemperationControlled(newDevice.getOutputMode())) {
651                     deviceDiscovery.onDeviceRemoved(newDevice);
652                 }
653             }
654             internalDevice.setOutputMode(newDevice.getOutputMode());
655         }
656         if (!newDevice.getBinaryInputs().equals(internalDevice.getBinaryInputs())) {
657             internalDevice.setBinaryInputs(newDevice.getBinaryInputs());
658         }
659         strucMan.updateDevice(newDevice);
660     }
661
662     private long lastSceneCall = 0;
663     private long sleepTime = 0;
664
665     @Override
666     public synchronized void sendSceneComandsToDSS(InternalScene scene, boolean call_undo) {
667         if (scene != null) {
668             if (lastSceneCall + 1000 > System.currentTimeMillis()) {
669                 sleepTime = System.currentTimeMillis() - lastSceneCall;
670                 try {
671                     Thread.sleep(sleepTime);
672                 } catch (InterruptedException e) {
673                     logger.debug("An InterruptedException occurred", e);
674                 }
675             }
676             lastSceneCall = System.currentTimeMillis();
677             boolean requestSuccessful = false;
678             if (scene.getZoneID() == 0) {
679                 if (call_undo) {
680                     logger.debug("{} {} {}", scene.getGroupID(), scene.getSceneID(),
681                             ApartmentSceneEnum.getApartmentScene(scene.getSceneID()));
682                     requestSuccessful = this.digitalSTROMClient.callApartmentScene(connMan.getSessionToken(),
683                             scene.getGroupID(), null, ApartmentSceneEnum.getApartmentScene(scene.getSceneID()), false);
684                 } else {
685                     requestSuccessful = this.digitalSTROMClient.undoApartmentScene(connMan.getSessionToken(),
686                             scene.getGroupID(), null, ApartmentSceneEnum.getApartmentScene(scene.getSceneID()));
687                 }
688             } else {
689                 if (call_undo) {
690                     requestSuccessful = this.digitalSTROMClient.callZoneScene(connMan.getSessionToken(),
691                             scene.getZoneID(), null, scene.getGroupID(), null, SceneEnum.getScene(scene.getSceneID()),
692                             false);
693                 } else {
694                     requestSuccessful = this.digitalSTROMClient.undoZoneScene(connMan.getSessionToken(),
695                             scene.getZoneID(), null, scene.getGroupID(), null, SceneEnum.getScene(scene.getSceneID()));
696                 }
697             }
698
699             logger.debug("Was the scene call succsessful?: {}", requestSuccessful);
700             if (requestSuccessful) {
701                 this.sceneMan.addEcho(scene.getID());
702                 if (call_undo) {
703                     scene.activateScene();
704                 } else {
705                     scene.deactivateScene();
706                 }
707             }
708         }
709     }
710
711     @Override
712     public synchronized void sendStopComandsToDSS(final Device device) {
713         scheduler.execute(new Runnable() {
714
715             @Override
716             public void run() {
717                 if (digitalSTROMClient.callDeviceScene(connMan.getSessionToken(), device.getDSID(), null, null,
718                         SceneEnum.STOP, true)) {
719                     sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.STOP.getSceneNumber());
720                     readOutputValue(device);
721                 }
722             }
723         });
724     }
725
726     private void readOutputValue(Device device) {
727         short outputIndex = DeviceConstants.DEVICE_SENSOR_OUTPUT;
728         if (device.isShade()) {
729             outputIndex = DeviceConstants.DEVICE_SENSOR_SLAT_POSITION_OUTPUT;
730         }
731
732         int outputValue = this.digitalSTROMClient.getDeviceOutputValue(connMan.getSessionToken(), device.getDSID(),
733                 null, null, outputIndex);
734         if (outputValue != -1) {
735             if (!device.isShade()) {
736                 device.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, outputValue));
737             } else {
738                 device.updateInternalDeviceState(
739                         new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, outputValue));
740                 if (device.isBlind()) {
741                     outputValue = this.digitalSTROMClient.getDeviceOutputValue(connMan.getSessionToken(),
742                             device.getDSID(), null, null, DeviceConstants.DEVICE_SENSOR_SLAT_ANGLE_OUTPUT);
743                     device.updateInternalDeviceState(
744                             new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, outputValue));
745                 }
746             }
747         }
748     }
749
750     /**
751      * Updates the {@link Device} status of the given {@link Device} with handling outstanding commands, which are saved
752      * as {@link DeviceStateUpdate}'s.
753      *
754      * @param device to update
755      */
756     public synchronized void updateDevice(Device device) {
757         logger.debug("Check device updates");
758         // check device state updates
759         while (!device.isDeviceUpToDate()) {
760             DeviceStateUpdate deviceStateUpdate = device.getNextDeviceUpdateState();
761             if (deviceStateUpdate != null) {
762                 if (deviceStateUpdate.getType() != DeviceStateUpdate.OUTPUT) {
763                     if (deviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_CONFIG
764                             || deviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_OUTPUT) {
765                         updateSceneData(device, deviceStateUpdate);
766                     } else {
767                         sendComandsToDSS(device, deviceStateUpdate);
768                     }
769                 } else {
770                     DeviceStateUpdate nextDeviceStateUpdate = device.getNextDeviceUpdateState();
771                     while (nextDeviceStateUpdate != null
772                             && nextDeviceStateUpdate.getType() == DeviceStateUpdate.OUTPUT) {
773                         deviceStateUpdate = nextDeviceStateUpdate;
774                         nextDeviceStateUpdate = device.getNextDeviceUpdateState();
775                     }
776                     sendComandsToDSS(device, deviceStateUpdate);
777                     if (nextDeviceStateUpdate != null) {
778                         if (deviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_CONFIG
779                                 || deviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_OUTPUT) {
780                             updateSceneData(device, deviceStateUpdate);
781                         } else {
782                             sendComandsToDSS(device, deviceStateUpdate);
783                         }
784                     }
785                 }
786             }
787         }
788     }
789
790     /**
791      * Checks the output value of a {@link Device} and return 0, if the output value or slat position is min and 1, if
792      * the output value or slat position is max, otherwise it returns -1.
793      *
794      * @param device
795      * @return 0 = output value is min, 1 device value is min, otherwise -1
796      */
797     private short checkIsAllreadyMinMax(Device device) {
798         if (device.isShade()) {
799             if (device.getSlatPosition() == device.getMaxSlatPosition()) {
800                 if (device.isBlind()) {
801                     if (device.getAnglePosition() == device.getMaxSlatAngle()) {
802                         return 1;
803                     } else {
804                         return -1;
805                     }
806                 }
807                 return 1;
808             }
809             if (device.getSlatPosition() == device.getMinSlatPosition()) {
810                 if (device.isBlind()) {
811                     if (device.getAnglePosition() == device.getMinSlatAngle()) {
812                         return 0;
813                     } else {
814                         return -1;
815                     }
816                 }
817                 return 0;
818             }
819         } else {
820             if (device.getOutputValue() == device.getMaxOutputValue()) {
821                 return 1;
822             }
823             if (device.getOutputValue() == device.getMinOutputValue() || device.getOutputValue() <= 0) {
824                 return 0;
825             }
826         }
827         return -1;
828     }
829
830     /**
831      * Checks the angle value of a {@link Device} and return 0, if the angle value is min and 1, if the angle value is
832      * max, otherwise it returns -1.
833      *
834      * @param device
835      * @return 0 = angle value is min, 1 angle value is min, otherwise -1
836      */
837     private short checkAngleIsMinMax(Device device) {
838         if (device.getAnglePosition() == device.getMaxSlatAngle()) {
839             return 1;
840         }
841         if (device.getAnglePosition() == device.getMinSlatAngle()) {
842             return 1;
843         }
844         return -1;
845     }
846
847     @Override
848     public synchronized void sendComandsToDSS(Device device, DeviceStateUpdate deviceStateUpdate) {
849         boolean requestSuccsessful = false;
850         boolean commandHasNoEffect = false;
851         if (deviceStateUpdate != null) {
852             if (deviceStateUpdate.isSensorUpdateType()) {
853                 SensorEnum sensorType = deviceStateUpdate.getTypeAsSensorEnum();
854                 if (deviceStateUpdate.getValueAsInteger() == 0) {
855                     updateSensorData(new DeviceConsumptionSensorJob(device, sensorType),
856                             device.getPowerSensorRefreshPriority(sensorType));
857                     return;
858                 } else if (deviceStateUpdate.getValueAsInteger() < 0) {
859                     removeSensorJob(device, deviceStateUpdate);
860                     return;
861                 } else {
862                     int consumption = this.digitalSTROMClient.getDeviceSensorValue(connMan.getSessionToken(),
863                             device.getDSID(), null, null, device.getSensorIndex(sensorType));
864                     if (consumption >= 0) {
865                         device.setDeviceSensorDsValueBySensorJob(sensorType, consumption);
866                         requestSuccsessful = true;
867                     }
868                 }
869             } else {
870                 switch (deviceStateUpdate.getType()) {
871                     case DeviceStateUpdate.OUTPUT_DECREASE:
872                     case DeviceStateUpdate.SLAT_DECREASE:
873                         if (checkIsAllreadyMinMax(device) != 0) {
874                             requestSuccsessful = digitalSTROMClient.decreaseValue(connMan.getSessionToken(),
875                                     device.getDSID(), null, null);
876                             if (requestSuccsessful) {
877                                 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.DECREMENT.getSceneNumber());
878                             }
879                         } else {
880                             commandHasNoEffect = true;
881                         }
882                         break;
883                     case DeviceStateUpdate.OUTPUT_INCREASE:
884                     case DeviceStateUpdate.SLAT_INCREASE:
885                         if (checkIsAllreadyMinMax(device) != 1) {
886                             requestSuccsessful = digitalSTROMClient.increaseValue(connMan.getSessionToken(),
887                                     device.getDSID(), null, null);
888                             if (requestSuccsessful) {
889                                 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.INCREMENT.getSceneNumber());
890                             }
891                         } else {
892                             commandHasNoEffect = true;
893                         }
894                         break;
895                     case DeviceStateUpdate.OUTPUT:
896                         if (device.getOutputValue() != deviceStateUpdate.getValueAsInteger()) {
897                             requestSuccsessful = digitalSTROMClient.setDeviceValue(connMan.getSessionToken(),
898                                     device.getDSID(), null, null, deviceStateUpdate.getValueAsInteger());
899                         } else {
900                             commandHasNoEffect = true;
901                         }
902                         break;
903                     case DeviceStateUpdate.OPEN_CLOSE:
904                     case DeviceStateUpdate.ON_OFF:
905                         if (deviceStateUpdate.getValueAsInteger() > 0) {
906                             if (checkIsAllreadyMinMax(device) != 1) {
907                                 requestSuccsessful = digitalSTROMClient.turnDeviceOn(connMan.getSessionToken(),
908                                         device.getDSID(), null, null);
909                                 if (requestSuccsessful) {
910                                     sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.MAXIMUM.getSceneNumber());
911                                 }
912                             } else {
913                                 commandHasNoEffect = true;
914                             }
915                         } else {
916                             if (checkIsAllreadyMinMax(device) != 0) {
917                                 requestSuccsessful = digitalSTROMClient.turnDeviceOff(connMan.getSessionToken(),
918                                         device.getDSID(), null, null);
919                                 if (requestSuccsessful) {
920                                     sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.MINIMUM.getSceneNumber());
921                                 }
922                             } else {
923                                 commandHasNoEffect = true;
924                             }
925                         }
926                         break;
927                     case DeviceStateUpdate.SLATPOSITION:
928                         if (device.getSlatPosition() != deviceStateUpdate.getValueAsInteger()) {
929                             requestSuccsessful = digitalSTROMClient.setDeviceOutputValue(connMan.getSessionToken(),
930                                     device.getDSID(), null, null, DeviceConstants.DEVICE_SENSOR_SLAT_POSITION_OUTPUT,
931                                     deviceStateUpdate.getValueAsInteger());
932                         } else {
933                             commandHasNoEffect = true;
934                         }
935                         break;
936                     case DeviceStateUpdate.SLAT_STOP:
937                         this.sendStopComandsToDSS(device);
938                         break;
939                     case DeviceStateUpdate.SLAT_MOVE:
940                         if (deviceStateUpdate.getValueAsInteger() > 0) {
941                             requestSuccsessful = digitalSTROMClient.turnDeviceOn(connMan.getSessionToken(),
942                                     device.getDSID(), null, null);
943                             if (requestSuccsessful) {
944                                 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.MAXIMUM.getSceneNumber());
945                             }
946                         } else {
947                             requestSuccsessful = digitalSTROMClient.turnDeviceOff(connMan.getSessionToken(),
948                                     device.getDSID(), null, null);
949                             if (requestSuccsessful) {
950                                 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.MINIMUM.getSceneNumber());
951                             }
952                             if (sensorJobExecutor != null) {
953                                 sensorJobExecutor.removeSensorJobs(device);
954                             }
955                         }
956                         break;
957                     case DeviceStateUpdate.UPDATE_CALL_SCENE:
958                         if (SceneEnum.getScene((short) deviceStateUpdate.getValue()) != null) {
959                             requestSuccsessful = digitalSTROMClient.callDeviceScene(connMan.getSessionToken(),
960                                     device.getDSID(), null, null,
961                                     SceneEnum.getScene((short) deviceStateUpdate.getValue()), true);
962                         }
963                         break;
964                     case DeviceStateUpdate.UPDATE_UNDO_SCENE:
965                         if (SceneEnum.getScene((short) deviceStateUpdate.getValue()) != null) {
966                             requestSuccsessful = digitalSTROMClient.undoDeviceScene(connMan.getSessionToken(),
967                                     device.getDSID(), null, null,
968                                     SceneEnum.getScene((short) deviceStateUpdate.getValue()));
969                         }
970                         break;
971                     case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
972                         // By UPDATE_SLAT_ANGLE_DECREASE, UPDATE_SLAT_ANGLE_INCREASE with value unequal 1 which will
973                         // handled in the pollingRunnable and UPDATE_OPEN_CLOSE_ANGLE the value will be set without
974                         // checking, because it was triggered by setting the slat position.
975                         requestSuccsessful = true;
976                         break;
977                     case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
978                         requestSuccsessful = true;
979                         break;
980                     case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
981                         requestSuccsessful = true;
982                         break;
983                     case DeviceStateUpdate.SLAT_ANGLE:
984                         if (device.getAnglePosition() != deviceStateUpdate.getValueAsInteger()) {
985                             requestSuccsessful = digitalSTROMClient.setDeviceOutputValue(connMan.getSessionToken(),
986                                     device.getDSID(), null, null, DeviceConstants.DEVICE_SENSOR_SLAT_ANGLE_OUTPUT,
987                                     deviceStateUpdate.getValueAsInteger());
988                         } else {
989                             commandHasNoEffect = true;
990                         }
991                         break;
992                     case DeviceStateUpdate.REFRESH_OUTPUT:
993                         readOutputValue(device);
994                         logger.debug("Inizalize output value reading for device with dSID {}.",
995                                 device.getDSID().getValue());
996                         return;
997                     default:
998                         return;
999                 }
1000             }
1001             if (commandHasNoEffect) {
1002                 logger.debug("Command {} for device with dSID {} not send to dSS, because it has no effect!",
1003                         deviceStateUpdate.getType(), device.getDSID().getValue());
1004                 return;
1005             }
1006             if (requestSuccsessful) {
1007                 logger.debug("Send {} command to dSS and updateInternalDeviceState for device with dSID {}.",
1008                         deviceStateUpdate.getType(), device.getDSID().getValue());
1009                 device.updateInternalDeviceState(deviceStateUpdate);
1010             } else {
1011                 logger.debug("Can't send {} command for device with dSID {} to dSS!", deviceStateUpdate.getType(),
1012                         device.getDSID().getValue());
1013             }
1014         }
1015     }
1016
1017     @Override
1018     public void updateSensorData(SensorJob sensorJob, String priority) {
1019         if (sensorJobExecutor == null) {
1020             sensorJobExecutor = new SensorJobExecutor(connMan);
1021             this.sensorJobExecutor.startExecutor();
1022         }
1023         if (sensorJob != null && priority != null) {
1024             switch (priority) {
1025                 case Config.REFRESH_PRIORITY_HIGH:
1026                     sensorJobExecutor.addHighPriorityJob(sensorJob);
1027                     break;
1028                 case Config.REFRESH_PRIORITY_MEDIUM:
1029                     sensorJobExecutor.addMediumPriorityJob(sensorJob);
1030                     break;
1031                 case Config.REFRESH_PRIORITY_LOW:
1032                     sensorJobExecutor.addLowPriorityJob(sensorJob);
1033                     break;
1034                 default:
1035                     try {
1036                         long prio = Long.parseLong(priority);
1037                         sensorJobExecutor.addPriorityJob(sensorJob, prio);
1038                     } catch (NumberFormatException e) {
1039                         logger.debug("Sensor data update priority do not exist! Please check the input!");
1040                         return;
1041                     }
1042             }
1043             logger.debug("Add new sensorJob {} with priority: {} to sensorJobExecuter", sensorJob.toString(), priority);
1044         }
1045     }
1046
1047     @Override
1048     public void updateSceneData(Device device, DeviceStateUpdate deviceStateUpdate) {
1049         if (sceneJobExecutor == null) {
1050             sceneJobExecutor = new SceneReadingJobExecutor(connMan);
1051             this.sceneJobExecutor.startExecutor();
1052         }
1053
1054         if (deviceStateUpdate != null) {
1055             if (deviceStateUpdate.getScenePriority() > -1) {
1056                 if (deviceStateUpdate.getType().equals(DeviceStateUpdate.UPDATE_SCENE_OUTPUT)) {
1057                     sceneJobExecutor.addPriorityJob(
1058                             new SceneOutputValueReadingJob(device, deviceStateUpdate.getSceneId()),
1059                             deviceStateUpdate.getScenePriority().longValue());
1060                 } else {
1061                     sceneJobExecutor.addPriorityJob(new SceneConfigReadingJob(device, deviceStateUpdate.getSceneId()),
1062                             deviceStateUpdate.getScenePriority().longValue());
1063                 }
1064                 if (deviceStateUpdate.getScenePriority() == 0) {
1065                     updateSensorData(new DeviceOutputValueSensorJob(device), "0");
1066                 }
1067                 logger.debug("Add new sceneReadingJob with priority: {} to SceneReadingJobExecuter",
1068                         deviceStateUpdate.getScenePriority());
1069             } else {
1070                 removeSensorJob(device, deviceStateUpdate);
1071             }
1072         }
1073     }
1074
1075     @Override
1076     public void registerDeviceListener(DeviceStatusListener deviceListener) {
1077         if (deviceListener != null) {
1078             String id = deviceListener.getDeviceStatusListenerID();
1079             if (id.equals(DeviceStatusListener.DEVICE_DISCOVERY)) {
1080                 this.deviceDiscovery = deviceListener;
1081                 logger.debug("register Device-Discovery ");
1082                 for (Device device : strucMan.getDeviceMap().values()) {
1083                     deviceDiscovery.onDeviceAdded(device);
1084                 }
1085                 for (Circuit circuit : strucMan.getCircuitMap().values()) {
1086                     deviceDiscovery.onDeviceAdded(circuit);
1087                 }
1088             } else {
1089                 Device intDevice = strucMan.getDeviceByDSID(deviceListener.getDeviceStatusListenerID());
1090                 if (intDevice != null) {
1091                     logger.debug("register DeviceListener with id: {} to Device ", id);
1092                     intDevice.registerDeviceStatusListener(deviceListener);
1093                 } else {
1094                     Circuit intCircuit = strucMan
1095                             .getCircuitByDSID(new DSID(deviceListener.getDeviceStatusListenerID()));
1096                     if (intCircuit != null) {
1097                         logger.debug("register DeviceListener with id: {} to Circuit ", id);
1098                         intCircuit.registerDeviceStatusListener(deviceListener);
1099                     } else {
1100                         deviceListener.onDeviceRemoved(null);
1101                     }
1102                 }
1103             }
1104         }
1105     }
1106
1107     @Override
1108     public void unregisterDeviceListener(DeviceStatusListener deviceListener) {
1109         if (deviceListener != null) {
1110             String id = deviceListener.getDeviceStatusListenerID();
1111             logger.debug("unregister DeviceListener with id: {}", id);
1112             if (id.equals(DeviceStatusListener.DEVICE_DISCOVERY)) {
1113                 this.deviceDiscovery = null;
1114             } else {
1115                 Device intDevice = strucMan.getDeviceByDSID(deviceListener.getDeviceStatusListenerID());
1116                 if (intDevice != null) {
1117                     intDevice.unregisterDeviceStatusListener();
1118                 } else {
1119                     Circuit intCircuit = strucMan
1120                             .getCircuitByDSID(new DSID(deviceListener.getDeviceStatusListenerID()));
1121                     if (intCircuit != null) {
1122                         intCircuit.unregisterDeviceStatusListener();
1123                         if (deviceDiscovery != null) {
1124                             deviceDiscovery.onDeviceAdded(intCircuit);
1125                         }
1126                     }
1127                 }
1128             }
1129         }
1130     }
1131
1132     @Override
1133     public void removeDevice(String dSID) {
1134         Device intDevice = strucMan.getDeviceByDSID(dSID);
1135         if (intDevice != null) {
1136             strucMan.deleteDevice(intDevice);
1137             trashDevices.add(new TrashDevice(intDevice));
1138         }
1139     }
1140
1141     @Override
1142     public void registerTotalPowerConsumptionListener(TotalPowerConsumptionListener totalPowerConsumptionListener) {
1143         this.totalPowerConsumptionListener = totalPowerConsumptionListener;
1144     }
1145
1146     @Override
1147     public void unregisterTotalPowerConsumptionListener() {
1148         this.totalPowerConsumptionListener = null;
1149     }
1150
1151     @Override
1152     public void registerSceneListener(SceneStatusListener sceneListener) {
1153         this.sceneMan.registerSceneListener(sceneListener);
1154     }
1155
1156     @Override
1157     public void unregisterSceneListener(SceneStatusListener sceneListener) {
1158         this.sceneMan.unregisterSceneListener(sceneListener);
1159     }
1160
1161     @Override
1162     public void registerStatusListener(ManagerStatusListener statusListener) {
1163         this.statusListener = statusListener;
1164         this.sceneMan.registerStatusListener(statusListener);
1165     }
1166
1167     @Override
1168     public void unregisterStatusListener() {
1169         this.statusListener = null;
1170         this.sceneMan.unregisterStatusListener();
1171     }
1172
1173     @Override
1174     public void registerConnectionListener(ConnectionListener connectionListener) {
1175         this.connMan.registerConnectionListener(connectionListener);
1176     }
1177
1178     @Override
1179     public void unregisterConnectionListener() {
1180         this.connMan.unregisterConnectionListener();
1181     }
1182
1183     @Override
1184     public int getTotalPowerConsumption() {
1185         List<CachedMeteringValue> cachedConsumptionMeteringValues = digitalSTROMClient
1186                 .getLatest(connMan.getSessionToken(), MeteringTypeEnum.CONSUMPTION, DsAPI.ALL_METERS, null);
1187         if (cachedConsumptionMeteringValues != null) {
1188             tempConsumption = 0;
1189             for (CachedMeteringValue value : cachedConsumptionMeteringValues) {
1190                 tempConsumption += value.getValue();
1191                 if (strucMan.getCircuitByDSID(value.getDsid()) != null) {
1192                     strucMan.getCircuitByDSID(value.getDsid()).addMeteringValue(value);
1193                 }
1194             }
1195         }
1196         return tempConsumption;
1197     }
1198
1199     private void getMeterData() {
1200         int val = getTotalPowerConsumption();
1201         if (totalPowerConsumptionListener != null) {
1202             totalPowerConsumptionListener.onTotalPowerConsumptionChanged(val);
1203         }
1204         val = getTotalEnergyMeterValue();
1205         if (totalPowerConsumptionListener != null) {
1206             totalPowerConsumptionListener.onEnergyMeterValueChanged(val);
1207         }
1208     }
1209
1210     @Override
1211     public int getTotalEnergyMeterValue() {
1212         List<CachedMeteringValue> cachedEnergyMeteringValues = digitalSTROMClient.getLatest(connMan.getSessionToken(),
1213                 MeteringTypeEnum.ENERGY, DsAPI.ALL_METERS, null);
1214         if (cachedEnergyMeteringValues != null) {
1215             tempEnergyMeter = 0;
1216             for (CachedMeteringValue value : cachedEnergyMeteringValues) {
1217                 tempEnergyMeter += value.getValue();
1218                 if (strucMan.getCircuitByDSID(value.getDsid()) != null) {
1219                     strucMan.getCircuitByDSID(value.getDsid()).addMeteringValue(value);
1220                 }
1221             }
1222         }
1223         return tempEnergyMeter;
1224     }
1225
1226     @Override
1227     public int getTotalEnergyMeterWsValue() {
1228         List<CachedMeteringValue> cachedEnergyMeteringValues = digitalSTROMClient.getLatest(connMan.getSessionToken(),
1229                 MeteringTypeEnum.ENERGY, DsAPI.ALL_METERS, MeteringUnitsEnum.WS);
1230         if (cachedEnergyMeteringValues != null) {
1231             tempEnergyMeterWs = 0;
1232             for (CachedMeteringValue value : cachedEnergyMeteringValues) {
1233                 tempEnergyMeterWs += value.getValue();
1234                 if (strucMan.getCircuitByDSID(value.getDsid()) != null) {
1235                     strucMan.getCircuitByDSID(value.getDsid()).addMeteringValue(value);
1236                 }
1237             }
1238         }
1239         return tempEnergyMeterWs;
1240     }
1241
1242     private void setInizialStateWithLastCallScenes() {
1243         if (sceneMan == null) {
1244             return;
1245         }
1246         JsonObject response = connMan.getDigitalSTROMAPI().query2(connMan.getSessionToken(), LAST_CALL_SCENE_QUERY);
1247         if (!response.isJsonObject()) {
1248             return;
1249         }
1250         for (Entry<String, JsonElement> entry : response.entrySet()) {
1251             if (!entry.getValue().isJsonObject()) {
1252                 continue;
1253             }
1254             JsonObject zone = entry.getValue().getAsJsonObject();
1255             int zoneID = -1;
1256             short groupID = -1;
1257             short sceneID = -1;
1258             if (zone.get(JSONApiResponseKeysEnum.ZONE_ID.getKey()) != null) {
1259                 zoneID = zone.get(JSONApiResponseKeysEnum.ZONE_ID.getKey()).getAsInt();
1260             }
1261             for (Entry<String, JsonElement> groupEntry : zone.entrySet()) {
1262                 if (groupEntry.getKey().startsWith("group") && groupEntry.getValue().isJsonObject()) {
1263                     JsonObject group = groupEntry.getValue().getAsJsonObject();
1264                     if (group.get(JSONApiResponseKeysEnum.DEVICES.getKey()) != null) {
1265                         if (group.get(JSONApiResponseKeysEnum.GROUP.getKey()) != null) {
1266                             groupID = group.get(JSONApiResponseKeysEnum.GROUP.getKey()).getAsShort();
1267                         }
1268                         if (group.get(JSONApiResponseKeysEnum.LAST_CALL_SCENE.getKey()) != null) {
1269                             sceneID = group.get(JSONApiResponseKeysEnum.LAST_CALL_SCENE.getKey()).getAsShort();
1270                         }
1271                         if (zoneID > -1 && groupID > -1 && sceneID > -1) {
1272                             logger.debug("initial state, call scene {}-{}-{}", zoneID, groupID, sceneID);
1273                             sceneMan.callInternalSceneWithoutDiscovery(zoneID, groupID, sceneID);
1274                         }
1275                     }
1276                 }
1277             }
1278         }
1279     }
1280
1281     @Override
1282     public void handleEvent(EventItem eventItem) {
1283         try {
1284             if (EventNames.DEVICE_SENSOR_VALUE.equals(eventItem.getName())
1285                     || EventNames.DEVICE_BINARY_INPUT_EVENT.equals(eventItem.getName())) {
1286                 logger.debug("Detect {} eventItem = {}", eventItem.getName(), eventItem.toString());
1287                 Device dev = getDeviceOfEvent(eventItem);
1288                 if (dev != null) {
1289                     if (EventNames.DEVICE_SENSOR_VALUE.equals(eventItem.getName())) {
1290                         dev.setDeviceSensorByEvent(eventItem);
1291                     } else {
1292                         DeviceBinarayInputEnum binaryInputType = DeviceBinarayInputEnum.getdeviceBinarayInput(Short
1293                                 .parseShort(eventItem.getProperties().getOrDefault(EventResponseEnum.INPUT_TYPE, "")));
1294                         Short newState = Short
1295                                 .parseShort(eventItem.getProperties().getOrDefault(EventResponseEnum.INPUT_STATE, ""));
1296                         if (binaryInputType != null) {
1297                             dev.setBinaryInputState(binaryInputType, newState);
1298                         }
1299                     }
1300                 }
1301             }
1302         } catch (NumberFormatException e) {
1303             logger.debug("Unexpected missing or invalid number while handling event", e);
1304         }
1305     }
1306
1307     private Device getDeviceOfEvent(EventItem eventItem) {
1308         if (eventItem.getSource().get(EventResponseEnum.DSID) != null) {
1309             String dSID = eventItem.getSource().get(EventResponseEnum.DSID);
1310             Device dev = strucMan.getDeviceByDSID(dSID);
1311             if (dev == null) {
1312                 dev = strucMan.getDeviceByDSUID(dSID);
1313             }
1314             return dev;
1315         }
1316         return null;
1317     }
1318
1319     @Override
1320     public List<String> getSupportedEvents() {
1321         return SUPPORTED_EVENTS;
1322     }
1323
1324     @Override
1325     public boolean supportsEvent(String eventName) {
1326         return SUPPORTED_EVENTS.contains(eventName);
1327     }
1328
1329     @Override
1330     public String getUID() {
1331         return getClass().getName();
1332     }
1333
1334     @Override
1335     public void setEventListener(EventListener eventListener) {
1336         if (this.eventListener != null) {
1337             this.eventListener.removeEventHandler(this);
1338         }
1339         this.eventListener = eventListener;
1340     }
1341
1342     @Override
1343     public void unsetEventListener(EventListener eventListener) {
1344         if (this.eventListener != null) {
1345             this.eventListener.removeEventHandler(this);
1346         }
1347         this.eventListener = null;
1348     }
1349 }