2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.digitalstrom.internal.lib.manager.impl;
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;
21 import java.util.Map.Entry;
22 import java.util.concurrent.CopyOnWriteArrayList;
23 import java.util.concurrent.ScheduledExecutorService;
24 import java.util.concurrent.ScheduledFuture;
25 import java.util.concurrent.TimeUnit;
27 import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance;
28 import org.openhab.binding.digitalstrom.internal.lib.config.Config;
29 import org.openhab.binding.digitalstrom.internal.lib.event.EventListener;
30 import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventNames;
31 import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventResponseEnum;
32 import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem;
33 import org.openhab.binding.digitalstrom.internal.lib.listener.ConnectionListener;
34 import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
35 import org.openhab.binding.digitalstrom.internal.lib.listener.ManagerStatusListener;
36 import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
37 import org.openhab.binding.digitalstrom.internal.lib.listener.TotalPowerConsumptionListener;
38 import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerStates;
39 import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerTypes;
40 import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
41 import org.openhab.binding.digitalstrom.internal.lib.manager.DeviceStatusManager;
42 import org.openhab.binding.digitalstrom.internal.lib.manager.SceneManager;
43 import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
44 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.SceneReadingJobExecutor;
45 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.SensorJobExecutor;
46 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
47 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.DeviceConsumptionSensorJob;
48 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.DeviceOutputValueSensorJob;
49 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.SceneConfigReadingJob;
50 import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl.SceneOutputValueReadingJob;
51 import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
52 import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
53 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit;
54 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
55 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.CachedMeteringValue;
56 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceConstants;
57 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
58 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
59 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringTypeEnum;
60 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringUnitsEnum;
61 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
62 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
63 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
64 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceStateUpdateImpl;
65 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.impl.DeviceImpl;
66 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
67 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.ApartmentSceneEnum;
68 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
69 import org.openhab.core.common.ThreadPoolManager;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
73 import com.google.gson.JsonElement;
74 import com.google.gson.JsonObject;
77 * The {@link DeviceStatusManagerImpl} is the implementation of the {@link DeviceStatusManager}.
79 * @author Michael Ochel - Initial contribution
80 * @author Matthias Siegele - Initial contribution
82 public class DeviceStatusManagerImpl implements DeviceStatusManager {
84 private final Logger logger = LoggerFactory.getLogger(DeviceStatusManagerImpl.class);
87 * Contains all supported event-types.
89 public static final List<String> SUPPORTED_EVENTS = Arrays.asList(EventNames.DEVICE_SENSOR_VALUE,
90 EventNames.DEVICE_BINARY_INPUT_EVENT);
92 private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(Config.THREADPOOL_NAME);
93 private ScheduledFuture<?> pollingScheduler;
96 * Query to get all {@link Device}'s with more informations than {@link DsAPI#getApartmentDevices(String)}. Can be
97 * executed with {@link DsAPI#query(String, String)} or {@link DsAPI#query2(String, String)}.
99 public static final String GET_DETAILD_DEVICES = "/apartment/zones/zone0(*)/devices/*(*)/*(*)/*(*)";
101 * Query to get the last called scenes of all groups in digitalSTROM. Can be executed with
102 * {@link DsAPI#query(String, String)} or
103 * {@link DsAPI#query2(String, String)}.
105 public static final String LAST_CALL_SCENE_QUERY = "/apartment/zones/*(*)/groups/*(*)/*(*)";
107 private ConnectionManager connMan;
108 private StructureManager strucMan;
109 private SceneManager sceneMan;
110 private DsAPI digitalSTROMClient;
111 private Config config;
113 private SensorJobExecutor sensorJobExecutor;
114 private SceneReadingJobExecutor sceneJobExecutor;
115 private EventListener eventListener;
117 private final List<TrashDevice> trashDevices = new CopyOnWriteArrayList<>();
119 private long lastBinCheck = 0;
120 private ManagerStates state = ManagerStates.STOPPED;
122 private int tempConsumption = 0;
123 private int tempEnergyMeter = 0;
124 private int tempEnergyMeterWs = 0;
126 private DeviceStatusListener deviceDiscovery;
127 private TotalPowerConsumptionListener totalPowerConsumptionListener;
128 private ManagerStatusListener statusListener;
131 * Creates a new {@link DeviceStatusManagerImpl} through the given {@link Config} object, which has to be contains
132 * all needed parameters like host address, authentication data and so on. This constructor the
133 * {@link DeviceStatusManagerImpl} will be create all needed managers itself.
135 * @param config (must not be null)
137 public DeviceStatusManagerImpl(Config config) {
138 init(new ConnectionManagerImpl(config), null, null, null, null);
142 * Creates a new {@link DeviceStatusManagerImpl}. The given fields needed to create {@link ConnectionManager}
143 * through the constructor {@link ConnectionManagerImpl#ConnectionManagerImpl(String, String, String, String)}. All
144 * other needed manager will be automatically created, too.
146 * @param hostAddress (must not be null)
147 * @param user (can be null, if appToken is set)
148 * @param password (can be null, if appToken is set)
149 * @param appToken (can be null, if user and password is set)
151 public DeviceStatusManagerImpl(String hostAddress, String user, String password, String appToken) {
152 init(new ConnectionManagerImpl(hostAddress, user, password, false), null, null, null, null);
156 * Creates a new {@link DeviceStatusManagerImpl} with the given managers. If the {@link StructureManager} or
157 * {@link SceneManager} is null, they will be automatically created.
159 * @param connMan (must not be null)
160 * @param strucMan (can be null)
161 * @param sceneMan (can be null)
163 public DeviceStatusManagerImpl(ConnectionManager connMan, StructureManager strucMan, SceneManager sceneMan) {
164 init(connMan, strucMan, sceneMan, null, null);
168 * Same constructor like {@link #DeviceStatusManagerImpl(ConnectionManager, StructureManager, SceneManager)}, but a
169 * {@link ManagerStatusListener} can be set, too.
171 * @param connMan (must not be null)
172 * @param strucMan (can be null)
173 * @param sceneMan (can be null)
174 * @param statusListener (can be null)
175 * @see #DeviceStatusManagerImpl(ConnectionManager, StructureManager, SceneManager)
177 public DeviceStatusManagerImpl(ConnectionManager connMan, StructureManager strucMan, SceneManager sceneMan,
178 ManagerStatusListener statusListener) {
179 init(connMan, strucMan, sceneMan, statusListener, null);
183 * Same constructor like
184 * {@link #DeviceStatusManagerImpl(ConnectionManager, StructureManager, SceneManager, ManagerStatusListener)}, but a
185 * {@link EventListener} can be set, too.
187 * @param connMan (must not be null)
188 * @param strucMan (can be null)
189 * @param sceneMan (can be null)
190 * @param statusListener (can be null)
191 * @param eventListener (can be null)
192 * @see #DeviceStatusManagerImpl(ConnectionManager, StructureManager, SceneManager, ManagerStatusListener)
194 public DeviceStatusManagerImpl(ConnectionManager connMan, StructureManager strucMan, SceneManager sceneMan,
195 ManagerStatusListener statusListener, EventListener eventListener) {
196 init(connMan, strucMan, sceneMan, statusListener, eventListener);
200 * Creates a new {@link DeviceStatusManagerImpl} with the given {@link ConnectionManager}. The
201 * {@link StructureManager} and
202 * {@link SceneManager} will be automatically created.
204 * @param connMan (must not be null)
206 public DeviceStatusManagerImpl(ConnectionManager connMan) {
207 init(connMan, null, null, null, null);
210 private void init(ConnectionManager connMan, StructureManager strucMan, SceneManager sceneMan,
211 ManagerStatusListener statusListener, EventListener eventListener) {
212 this.connMan = connMan;
213 this.digitalSTROMClient = connMan.getDigitalSTROMAPI();
214 this.config = connMan.getConfig();
215 if (strucMan != null) {
216 this.strucMan = strucMan;
218 this.strucMan = new StructureManagerImpl();
220 if (sceneMan != null) {
221 this.sceneMan = sceneMan;
223 this.sceneMan = new SceneManagerImpl(connMan, strucMan, statusListener);
225 this.statusListener = statusListener;
226 this.eventListener = eventListener;
230 * Check and updates the {@link Device} structure, configurations and status.
232 * @author Michael Ochel - initial contributer
233 * @author Matthias Siegele - initial contributer
235 private class PollingRunnable implements Runnable {
236 private boolean devicesLoaded = false;
237 private long nextSensorUpdate = 0;
241 if (!getManagerState().equals(ManagerStates.RUNNING)) {
242 logger.debug("Thread started");
244 stateChanged(ManagerStates.RUNNING);
246 stateChanged(ManagerStates.INITIALIZING);
249 Map<DSID, Device> tempDeviceMap;
250 if (strucMan.getDeviceMap() != null) {
251 tempDeviceMap = strucMan.getDeviceMap();
253 tempDeviceMap = new HashMap<>();
256 List<Device> currentDeviceList = getDetailedDevices();
258 // update the current total power consumption
259 if (nextSensorUpdate <= System.currentTimeMillis()) {
261 List<Circuit> circuits = digitalSTROMClient.getApartmentCircuits(connMan.getSessionToken());
262 for (Circuit circuit : circuits) {
263 if (strucMan.getCircuitByDSID(circuit.getDSID()) != null) {
264 if (!circuit.equals(strucMan.getCircuitByDSID(circuit.getDSID()))) {
265 strucMan.updateCircuitConfig(circuit);
268 strucMan.addCircuit(circuit);
269 if (deviceDiscovery != null) {
270 deviceDiscovery.onDeviceAdded(circuit);
275 nextSensorUpdate = System.currentTimeMillis() + config.getTotalPowerUpdateInterval();
278 while (!currentDeviceList.isEmpty()) {
279 Device currentDevice = currentDeviceList.remove(0);
280 DSID currentDeviceDSID = currentDevice.getDSID();
281 Device device = tempDeviceMap.remove(currentDeviceDSID);
283 if (device != null) {
284 checkDeviceConfig(currentDevice, device);
286 if (device.isPresent()) {
287 // check device state updates
288 while (!device.isDeviceUpToDate()) {
289 DeviceStateUpdate deviceStateUpdate = device.getNextDeviceUpdateState();
290 if (deviceStateUpdate != null) {
291 switch (deviceStateUpdate.getType()) {
292 case DeviceStateUpdate.OUTPUT:
293 case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
294 case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
295 filterCommand(deviceStateUpdate, device);
297 case DeviceStateUpdate.UPDATE_SCENE_CONFIG:
298 case DeviceStateUpdate.UPDATE_SCENE_OUTPUT:
299 updateSceneData(device, deviceStateUpdate);
301 case DeviceStateUpdate.UPDATE_OUTPUT_VALUE:
302 if (deviceStateUpdate.getValueAsInteger() > -1) {
303 readOutputValue(device);
305 removeSensorJob(device, deviceStateUpdate);
309 sendComandsToDSS(device, deviceStateUpdate);
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());
322 logger.debug("Search device in trashDevices.");
323 boolean found = trashDevices.removeIf(trashDevice -> {
324 if (trashDevice.getDevice().equals(currentDevice)) {
326 "Found device in trashDevices, add TrashDevice with dSID {} to the StructureManager!",
328 strucMan.addDeviceToStructure(trashDevice.getDevice());
335 strucMan.addDeviceToStructure(currentDevice);
337 "Can't find device in trashDevices, add Device with dSID: {} to the StructureManager!",
341 if (deviceDiscovery != null) {
342 // only informs discovery, if the device is a output or a sensor device
343 deviceDiscovery.onDeviceAdded(currentDevice);
344 logger.debug("inform DeviceStatusListener: {} about added device with dSID {}",
345 DeviceStatusListener.DEVICE_DISCOVERY, currentDevice.getDSID().getValue());
348 "The device discovery is not registrated, can't inform device discovery about found device.");
353 if (!devicesLoaded && strucMan.getDeviceMap() != null) {
354 if (!strucMan.getDeviceMap().values().isEmpty()) {
355 logger.debug("Devices loaded");
356 devicesLoaded = true;
357 setInizialStateWithLastCallScenes();
358 stateChanged(ManagerStates.RUNNING);
360 logger.debug("No devices found");
364 if (!sceneMan.scenesGenerated() && devicesLoaded
365 && !sceneMan.getManagerState().equals(ManagerStates.GENERATING_SCENES)) {
366 logger.debug("{}", sceneMan.getManagerState());
367 sceneMan.generateScenes();
370 for (Device device : tempDeviceMap.values()) {
371 logger.debug("Found removed devices.");
373 trashDevices.add(new TrashDevice(device));
374 DeviceStatusListener listener = device.unregisterDeviceStatusListener();
375 if (listener != null) {
376 listener.onDeviceRemoved(null);
378 strucMan.deleteDevice(device);
379 logger.debug("Add device with dSID {} to trashDevices", device.getDSID().getValue());
381 if (deviceDiscovery != null) {
382 deviceDiscovery.onDeviceRemoved(device);
383 logger.debug("inform DeviceStatusListener: {} about removed device with dSID {}",
384 DeviceStatusListener.DEVICE_DISCOVERY, device.getDSID().getValue());
387 "The device-Discovery is not registered, can't inform device discovery about removed device.");
391 if (!trashDevices.isEmpty() && (lastBinCheck + config.getBinCheckTime() < System.currentTimeMillis())) {
392 trashDevices.removeIf(trashDevice -> {
393 if (trashDevice.isTimeToDelete(Calendar.getInstance().get(Calendar.DAY_OF_YEAR))) {
394 logger.debug("Deleted trashDevice: {}", trashDevice.getDevice().getDSID().getValue());
400 lastBinCheck = System.currentTimeMillis();
404 private List<Device> getDetailedDevices() {
405 List<Device> deviceList = new LinkedList<>();
406 JsonObject result = connMan.getDigitalSTROMAPI().query2(connMan.getSessionToken(), GET_DETAILD_DEVICES);
407 if (result != null && result.isJsonObject()) {
408 if (result.getAsJsonObject().get(GeneralLibConstance.QUERY_BROADCAST_ZONE_STRING).isJsonObject()) {
409 result = result.getAsJsonObject().get(GeneralLibConstance.QUERY_BROADCAST_ZONE_STRING)
411 for (Entry<String, JsonElement> entry : result.entrySet()) {
412 if (!(entry.getKey().equals(JSONApiResponseKeysEnum.ZONE_ID.getKey())
413 && entry.getKey().equals(JSONApiResponseKeysEnum.NAME.getKey()))
414 && entry.getValue().isJsonObject()) {
415 deviceList.add(new DeviceImpl(entry.getValue().getAsJsonObject()));
423 private void filterCommand(DeviceStateUpdate deviceStateUpdate, Device device) {
424 DeviceStateUpdate intDeviceStateUpdate = deviceStateUpdate;
425 String stateUpdateType = intDeviceStateUpdate.getType();
427 if (stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_INCREASE)
428 || stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_DECREASE)) {
429 newAngle = device.getAnglePosition();
431 DeviceStateUpdate nextDeviceStateUpdate = device.getNextDeviceUpdateState();
432 while (nextDeviceStateUpdate != null && nextDeviceStateUpdate.getType().equals(stateUpdateType)) {
433 switch (stateUpdateType) {
434 case DeviceStateUpdate.OUTPUT:
435 intDeviceStateUpdate = nextDeviceStateUpdate;
436 nextDeviceStateUpdate = device.getNextDeviceUpdateState();
438 case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
439 if (intDeviceStateUpdate.getValueAsInteger() == 1) {
440 newAngle = (short) (newAngle + DeviceConstants.ANGLE_STEP_SLAT);
443 case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
444 if (intDeviceStateUpdate.getValueAsInteger() == 1) {
445 newAngle = (short) (newAngle - DeviceConstants.ANGLE_STEP_SLAT);
450 if (stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_INCREASE)
451 || stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_DECREASE)) {
452 if (newAngle > device.getMaxSlatAngle()) {
453 newAngle = (short) device.getMaxSlatAngle();
455 if (newAngle < device.getMinSlatAngle()) {
456 newAngle = (short) device.getMinSlatAngle();
458 if (!(stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_INCREASE) && checkAngleIsMinMax(device) == 1)
459 || !(stateUpdateType.equals(DeviceStateUpdate.SLAT_ANGLE_DECREASE)
460 && checkAngleIsMinMax(device) == 0)) {
461 intDeviceStateUpdate = new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, newAngle);
464 sendComandsToDSS(device, intDeviceStateUpdate);
465 if (nextDeviceStateUpdate != null) {
466 if (intDeviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_CONFIG
467 || intDeviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_OUTPUT) {
468 updateSceneData(device, intDeviceStateUpdate);
470 sendComandsToDSS(device, intDeviceStateUpdate);
476 private void removeSensorJob(Device device, DeviceStateUpdate deviceStateUpdate) {
477 switch (deviceStateUpdate.getType()) {
478 case DeviceStateUpdate.UPDATE_SCENE_CONFIG:
479 if (sceneJobExecutor != null) {
480 sceneJobExecutor.removeSensorJob(device,
481 SceneConfigReadingJob.getID(device, deviceStateUpdate.getSceneId()));
484 case DeviceStateUpdate.UPDATE_SCENE_OUTPUT:
485 if (sceneJobExecutor != null) {
486 sceneJobExecutor.removeSensorJob(device,
487 SceneOutputValueReadingJob.getID(device, deviceStateUpdate.getSceneId()));
490 case DeviceStateUpdate.UPDATE_OUTPUT_VALUE:
491 if (sensorJobExecutor != null) {
492 sensorJobExecutor.removeSensorJob(device, DeviceOutputValueSensorJob.getID(device));
496 if (deviceStateUpdate.isSensorUpdateType()) {
497 if (sensorJobExecutor != null) {
498 logger.debug("remove SensorJob with ID: {}",
499 DeviceConsumptionSensorJob.getID(device, deviceStateUpdate.getTypeAsSensorEnum()));
500 sensorJobExecutor.removeSensorJob(device,
501 DeviceConsumptionSensorJob.getID(device, deviceStateUpdate.getTypeAsSensorEnum()));
507 public ManagerTypes getManagerType() {
508 return ManagerTypes.DEVICE_STATUS_MANAGER;
512 public synchronized ManagerStates getManagerState() {
516 private synchronized void stateChanged(ManagerStates state) {
517 if (statusListener != null) {
519 statusListener.onStatusChanged(ManagerTypes.DEVICE_STATUS_MANAGER, state);
524 public synchronized void start() {
525 logger.debug("start DeviceStatusManager");
526 if (pollingScheduler == null || pollingScheduler.isCancelled()) {
527 pollingScheduler = scheduler.scheduleWithFixedDelay(new PollingRunnable(), 0, config.getPollingFrequency(),
528 TimeUnit.MILLISECONDS);
529 logger.debug("start pollingScheduler");
532 if (sceneJobExecutor != null) {
533 this.sceneJobExecutor.startExecutor();
536 if (sensorJobExecutor != null) {
537 this.sensorJobExecutor.startExecutor();
539 if (eventListener != null) {
540 eventListener.addEventHandler(this);
542 eventListener = new EventListener(connMan, this);
543 eventListener.start();
548 public synchronized void stop() {
549 logger.debug("stop DeviceStatusManager");
550 stateChanged(ManagerStates.STOPPED);
551 if (sceneMan != null) {
554 if (pollingScheduler != null && !pollingScheduler.isCancelled()) {
555 pollingScheduler.cancel(true);
556 pollingScheduler = null;
557 logger.debug("stop pollingScheduler");
559 if (sceneJobExecutor != null) {
560 this.sceneJobExecutor.shutdown();
562 if (sensorJobExecutor != null) {
563 this.sensorJobExecutor.shutdown();
565 if (eventListener != null) {
566 eventListener.removeEventHandler(this);
571 * The {@link TrashDevice} saves not present {@link Device}'s, but at this point not deleted from the
572 * digitalSTROM-System, temporary to get back the configuration of the {@link Device}'s faster.
574 * @author Michael Ochel - Initial contribution
575 * @author Matthias Siegele - Initial contribution
577 private class TrashDevice {
578 private final Device device;
579 private final int timestamp;
582 * Creates a new {@link TrashDevice}.
584 * @param device to put in {@link TrashDevice}
586 public TrashDevice(Device device) {
587 this.device = device;
588 this.timestamp = Calendar.getInstance().get(Calendar.DAY_OF_YEAR);
592 * Returns the saved {@link Device}.
596 public Device getDevice() {
601 * Returns true if the time for the {@link TrashDevice} is over and it can be deleted.
603 * @param dayOfYear day of the current year
604 * @return true = time to delete | false = not time to delete
606 public boolean isTimeToDelete(int dayOfYear) {
607 return this.timestamp + config.getTrashDeviceDeleteTime() <= dayOfYear;
611 public boolean equals(Object object) {
612 return object instanceof TrashDevice
613 ? this.device.getDSID().equals(((TrashDevice) object).getDevice().getDSID())
618 private void checkDeviceConfig(Device newDevice, Device internalDevice) {
619 if (newDevice == null || internalDevice == null) {
622 // check device availability has changed and informs the deviceStatusListener about the change.
624 // The device is not availability for the digitalSTROM-Server, it has not been deleted and therefore it is set
627 // An alternate algorithm is responsible for deletion.
628 if (newDevice.isPresent() != internalDevice.isPresent()) {
629 internalDevice.setIsPresent(newDevice.isPresent());
631 if (newDevice.getMeterDSID() != null && !newDevice.getMeterDSID().equals(internalDevice.getMeterDSID())) {
632 internalDevice.setMeterDSID(newDevice.getMeterDSID().getValue());
634 if (newDevice.getFunctionalColorGroup() != null
635 && !newDevice.getFunctionalColorGroup().equals(internalDevice.getFunctionalColorGroup())) {
636 internalDevice.setFunctionalColorGroup(newDevice.getFunctionalColorGroup());
638 if (newDevice.getName() != null && !newDevice.getName().equals(internalDevice.getName())) {
639 internalDevice.setName(newDevice.getName());
641 if (newDevice.getOutputMode() != null && !newDevice.getOutputMode().equals(internalDevice.getOutputMode())) {
642 if (deviceDiscovery != null) {
643 if (OutputModeEnum.DISABLED.equals(internalDevice.getOutputMode())
644 || OutputModeEnum.outputModeIsTemperationControlled(internalDevice.getOutputMode())) {
645 deviceDiscovery.onDeviceAdded(newDevice);
647 if (OutputModeEnum.DISABLED.equals(newDevice.getOutputMode())
648 || OutputModeEnum.outputModeIsTemperationControlled(newDevice.getOutputMode())) {
649 deviceDiscovery.onDeviceRemoved(newDevice);
652 internalDevice.setOutputMode(newDevice.getOutputMode());
654 if (!newDevice.getBinaryInputs().equals(internalDevice.getBinaryInputs())) {
655 internalDevice.setBinaryInputs(newDevice.getBinaryInputs());
657 strucMan.updateDevice(newDevice);
660 private long lastSceneCall = 0;
661 private long sleepTime = 0;
664 public synchronized void sendSceneComandsToDSS(InternalScene scene, boolean call_undo) {
666 if (lastSceneCall + 1000 > System.currentTimeMillis()) {
667 sleepTime = System.currentTimeMillis() - lastSceneCall;
669 Thread.sleep(sleepTime);
670 } catch (InterruptedException e) {
671 logger.debug("An InterruptedException occurred", e);
674 lastSceneCall = System.currentTimeMillis();
675 boolean requestSuccessful = false;
676 if (scene.getZoneID() == 0) {
678 logger.debug("{} {} {}", scene.getGroupID(), scene.getSceneID(),
679 ApartmentSceneEnum.getApartmentScene(scene.getSceneID()));
680 requestSuccessful = this.digitalSTROMClient.callApartmentScene(connMan.getSessionToken(),
681 scene.getGroupID(), null, ApartmentSceneEnum.getApartmentScene(scene.getSceneID()), false);
683 requestSuccessful = this.digitalSTROMClient.undoApartmentScene(connMan.getSessionToken(),
684 scene.getGroupID(), null, ApartmentSceneEnum.getApartmentScene(scene.getSceneID()));
688 requestSuccessful = this.digitalSTROMClient.callZoneScene(connMan.getSessionToken(),
689 scene.getZoneID(), null, scene.getGroupID(), null, SceneEnum.getScene(scene.getSceneID()),
692 requestSuccessful = this.digitalSTROMClient.undoZoneScene(connMan.getSessionToken(),
693 scene.getZoneID(), null, scene.getGroupID(), null, SceneEnum.getScene(scene.getSceneID()));
697 logger.debug("Was the scene call succsessful?: {}", requestSuccessful);
698 if (requestSuccessful) {
699 this.sceneMan.addEcho(scene.getID());
701 scene.activateScene();
703 scene.deactivateScene();
710 public synchronized void sendStopComandsToDSS(final Device device) {
711 scheduler.execute(new Runnable() {
715 if (digitalSTROMClient.callDeviceScene(connMan.getSessionToken(), device.getDSID(), null, null,
716 SceneEnum.STOP, true)) {
717 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.STOP.getSceneNumber());
718 readOutputValue(device);
724 private void readOutputValue(Device device) {
725 short outputIndex = DeviceConstants.DEVICE_SENSOR_OUTPUT;
726 if (device.isShade()) {
727 outputIndex = DeviceConstants.DEVICE_SENSOR_SLAT_POSITION_OUTPUT;
730 int outputValue = this.digitalSTROMClient.getDeviceOutputValue(connMan.getSessionToken(), device.getDSID(),
731 null, null, outputIndex);
732 if (outputValue != -1) {
733 if (!device.isShade()) {
734 device.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, outputValue));
736 device.updateInternalDeviceState(
737 new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, outputValue));
738 if (device.isBlind()) {
739 outputValue = this.digitalSTROMClient.getDeviceOutputValue(connMan.getSessionToken(),
740 device.getDSID(), null, null, DeviceConstants.DEVICE_SENSOR_SLAT_ANGLE_OUTPUT);
741 device.updateInternalDeviceState(
742 new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, outputValue));
749 * Updates the {@link Device} status of the given {@link Device} with handling outstanding commands, which are saved
750 * as {@link DeviceStateUpdate}'s.
752 * @param device to update
754 public synchronized void updateDevice(Device device) {
755 logger.debug("Check device updates");
756 // check device state updates
757 while (!device.isDeviceUpToDate()) {
758 DeviceStateUpdate deviceStateUpdate = device.getNextDeviceUpdateState();
759 if (deviceStateUpdate != null) {
760 if (deviceStateUpdate.getType() != DeviceStateUpdate.OUTPUT) {
761 if (deviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_CONFIG
762 || deviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_OUTPUT) {
763 updateSceneData(device, deviceStateUpdate);
765 sendComandsToDSS(device, deviceStateUpdate);
768 DeviceStateUpdate nextDeviceStateUpdate = device.getNextDeviceUpdateState();
769 while (nextDeviceStateUpdate != null
770 && nextDeviceStateUpdate.getType() == DeviceStateUpdate.OUTPUT) {
771 deviceStateUpdate = nextDeviceStateUpdate;
772 nextDeviceStateUpdate = device.getNextDeviceUpdateState();
774 sendComandsToDSS(device, deviceStateUpdate);
775 if (nextDeviceStateUpdate != null) {
776 if (deviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_CONFIG
777 || deviceStateUpdate.getType() == DeviceStateUpdate.UPDATE_SCENE_OUTPUT) {
778 updateSceneData(device, deviceStateUpdate);
780 sendComandsToDSS(device, deviceStateUpdate);
789 * Checks the output value of a {@link Device} and return 0, if the output value or slat position is min and 1, if
790 * the output value or slat position is max, otherwise it returns -1.
793 * @return 0 = output value is min, 1 device value is min, otherwise -1
795 private short checkIsAllreadyMinMax(Device device) {
796 if (device.isShade()) {
797 if (device.getSlatPosition() == device.getMaxSlatPosition()) {
798 if (device.isBlind()) {
799 if (device.getAnglePosition() == device.getMaxSlatAngle()) {
807 if (device.getSlatPosition() == device.getMinSlatPosition()) {
808 if (device.isBlind()) {
809 if (device.getAnglePosition() == device.getMinSlatAngle()) {
818 if (device.getOutputValue() == device.getMaxOutputValue()) {
821 if (device.getOutputValue() == device.getMinOutputValue() || device.getOutputValue() <= 0) {
829 * Checks the angle value of a {@link Device} and return 0, if the angle value is min and 1, if the angle value is
830 * max, otherwise it returns -1.
833 * @return 0 = angle value is min, 1 angle value is min, otherwise -1
835 private short checkAngleIsMinMax(Device device) {
836 if (device.getAnglePosition() == device.getMaxSlatAngle()) {
839 if (device.getAnglePosition() == device.getMinSlatAngle()) {
846 public synchronized void sendComandsToDSS(Device device, DeviceStateUpdate deviceStateUpdate) {
847 boolean requestSuccsessful = false;
848 boolean commandHasNoEffect = false;
849 if (deviceStateUpdate != null) {
850 if (deviceStateUpdate.isSensorUpdateType()) {
851 SensorEnum sensorType = deviceStateUpdate.getTypeAsSensorEnum();
852 if (deviceStateUpdate.getValueAsInteger() == 0) {
853 updateSensorData(new DeviceConsumptionSensorJob(device, sensorType),
854 device.getPowerSensorRefreshPriority(sensorType));
856 } else if (deviceStateUpdate.getValueAsInteger() < 0) {
857 removeSensorJob(device, deviceStateUpdate);
860 int consumption = this.digitalSTROMClient.getDeviceSensorValue(connMan.getSessionToken(),
861 device.getDSID(), null, null, device.getSensorIndex(sensorType));
862 if (consumption >= 0) {
863 device.setDeviceSensorDsValueBySensorJob(sensorType, consumption);
864 requestSuccsessful = true;
868 switch (deviceStateUpdate.getType()) {
869 case DeviceStateUpdate.OUTPUT_DECREASE:
870 case DeviceStateUpdate.SLAT_DECREASE:
871 if (checkIsAllreadyMinMax(device) != 0) {
872 requestSuccsessful = digitalSTROMClient.decreaseValue(connMan.getSessionToken(),
873 device.getDSID(), null, null);
874 if (requestSuccsessful) {
875 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.DECREMENT.getSceneNumber());
878 commandHasNoEffect = true;
881 case DeviceStateUpdate.OUTPUT_INCREASE:
882 case DeviceStateUpdate.SLAT_INCREASE:
883 if (checkIsAllreadyMinMax(device) != 1) {
884 requestSuccsessful = digitalSTROMClient.increaseValue(connMan.getSessionToken(),
885 device.getDSID(), null, null);
886 if (requestSuccsessful) {
887 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.INCREMENT.getSceneNumber());
890 commandHasNoEffect = true;
893 case DeviceStateUpdate.OUTPUT:
894 if (device.getOutputValue() != deviceStateUpdate.getValueAsInteger()) {
895 requestSuccsessful = digitalSTROMClient.setDeviceValue(connMan.getSessionToken(),
896 device.getDSID(), null, null, deviceStateUpdate.getValueAsInteger());
898 commandHasNoEffect = true;
901 case DeviceStateUpdate.OPEN_CLOSE:
902 case DeviceStateUpdate.ON_OFF:
903 if (deviceStateUpdate.getValueAsInteger() > 0) {
904 if (checkIsAllreadyMinMax(device) != 1) {
905 requestSuccsessful = digitalSTROMClient.turnDeviceOn(connMan.getSessionToken(),
906 device.getDSID(), null, null);
907 if (requestSuccsessful) {
908 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.MAXIMUM.getSceneNumber());
911 commandHasNoEffect = true;
914 if (checkIsAllreadyMinMax(device) != 0) {
915 requestSuccsessful = digitalSTROMClient.turnDeviceOff(connMan.getSessionToken(),
916 device.getDSID(), null, null);
917 if (requestSuccsessful) {
918 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.MINIMUM.getSceneNumber());
921 commandHasNoEffect = true;
925 case DeviceStateUpdate.SLATPOSITION:
926 if (device.getSlatPosition() != deviceStateUpdate.getValueAsInteger()) {
927 requestSuccsessful = digitalSTROMClient.setDeviceOutputValue(connMan.getSessionToken(),
928 device.getDSID(), null, null, DeviceConstants.DEVICE_SENSOR_SLAT_POSITION_OUTPUT,
929 deviceStateUpdate.getValueAsInteger());
931 commandHasNoEffect = true;
934 case DeviceStateUpdate.SLAT_STOP:
935 this.sendStopComandsToDSS(device);
937 case DeviceStateUpdate.SLAT_MOVE:
938 if (deviceStateUpdate.getValueAsInteger() > 0) {
939 requestSuccsessful = digitalSTROMClient.turnDeviceOn(connMan.getSessionToken(),
940 device.getDSID(), null, null);
941 if (requestSuccsessful) {
942 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.MAXIMUM.getSceneNumber());
945 requestSuccsessful = digitalSTROMClient.turnDeviceOff(connMan.getSessionToken(),
946 device.getDSID(), null, null);
947 if (requestSuccsessful) {
948 sceneMan.addEcho(device.getDSID().getValue(), SceneEnum.MINIMUM.getSceneNumber());
950 if (sensorJobExecutor != null) {
951 sensorJobExecutor.removeSensorJobs(device);
955 case DeviceStateUpdate.UPDATE_CALL_SCENE:
956 if (SceneEnum.getScene((short) deviceStateUpdate.getValue()) != null) {
957 requestSuccsessful = digitalSTROMClient.callDeviceScene(connMan.getSessionToken(),
958 device.getDSID(), null, null,
959 SceneEnum.getScene((short) deviceStateUpdate.getValue()), true);
962 case DeviceStateUpdate.UPDATE_UNDO_SCENE:
963 if (SceneEnum.getScene((short) deviceStateUpdate.getValue()) != null) {
964 requestSuccsessful = digitalSTROMClient.undoDeviceScene(connMan.getSessionToken(),
965 device.getDSID(), null, null,
966 SceneEnum.getScene((short) deviceStateUpdate.getValue()));
969 case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
970 // By UPDATE_SLAT_ANGLE_DECREASE, UPDATE_SLAT_ANGLE_INCREASE with value unequal 1 which will
971 // handled in the pollingRunnable and UPDATE_OPEN_CLOSE_ANGLE the value will be set without
972 // checking, because it was triggered by setting the slat position.
973 requestSuccsessful = true;
975 case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
976 requestSuccsessful = true;
978 case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
979 requestSuccsessful = true;
981 case DeviceStateUpdate.SLAT_ANGLE:
982 if (device.getAnglePosition() != deviceStateUpdate.getValueAsInteger()) {
983 requestSuccsessful = digitalSTROMClient.setDeviceOutputValue(connMan.getSessionToken(),
984 device.getDSID(), null, null, DeviceConstants.DEVICE_SENSOR_SLAT_ANGLE_OUTPUT,
985 deviceStateUpdate.getValueAsInteger());
987 commandHasNoEffect = true;
990 case DeviceStateUpdate.REFRESH_OUTPUT:
991 readOutputValue(device);
992 logger.debug("Inizalize output value reading for device with dSID {}.",
993 device.getDSID().getValue());
999 if (commandHasNoEffect) {
1000 logger.debug("Command {} for device with dSID {} not send to dSS, because it has no effect!",
1001 deviceStateUpdate.getType(), device.getDSID().getValue());
1004 if (requestSuccsessful) {
1005 logger.debug("Send {} command to dSS and updateInternalDeviceState for device with dSID {}.",
1006 deviceStateUpdate.getType(), device.getDSID().getValue());
1007 device.updateInternalDeviceState(deviceStateUpdate);
1009 logger.debug("Can't send {} command for device with dSID {} to dSS!", deviceStateUpdate.getType(),
1010 device.getDSID().getValue());
1016 public void updateSensorData(SensorJob sensorJob, String priority) {
1017 if (sensorJobExecutor == null) {
1018 sensorJobExecutor = new SensorJobExecutor(connMan);
1019 this.sensorJobExecutor.startExecutor();
1021 if (sensorJob != null && priority != null) {
1023 case Config.REFRESH_PRIORITY_HIGH:
1024 sensorJobExecutor.addHighPriorityJob(sensorJob);
1026 case Config.REFRESH_PRIORITY_MEDIUM:
1027 sensorJobExecutor.addMediumPriorityJob(sensorJob);
1029 case Config.REFRESH_PRIORITY_LOW:
1030 sensorJobExecutor.addLowPriorityJob(sensorJob);
1034 long prio = Long.parseLong(priority);
1035 sensorJobExecutor.addPriorityJob(sensorJob, prio);
1036 } catch (NumberFormatException e) {
1037 logger.debug("Sensor data update priority do not exist! Please check the input!");
1041 logger.debug("Add new sensorJob {} with priority: {} to sensorJobExecuter", sensorJob.toString(), priority);
1046 public void updateSceneData(Device device, DeviceStateUpdate deviceStateUpdate) {
1047 if (sceneJobExecutor == null) {
1048 sceneJobExecutor = new SceneReadingJobExecutor(connMan);
1049 this.sceneJobExecutor.startExecutor();
1052 if (deviceStateUpdate != null) {
1053 if (deviceStateUpdate.getScenePriority() > -1) {
1054 if (deviceStateUpdate.getType().equals(DeviceStateUpdate.UPDATE_SCENE_OUTPUT)) {
1055 sceneJobExecutor.addPriorityJob(
1056 new SceneOutputValueReadingJob(device, deviceStateUpdate.getSceneId()),
1057 deviceStateUpdate.getScenePriority().longValue());
1059 sceneJobExecutor.addPriorityJob(new SceneConfigReadingJob(device, deviceStateUpdate.getSceneId()),
1060 deviceStateUpdate.getScenePriority().longValue());
1062 if (deviceStateUpdate.getScenePriority() == 0) {
1063 updateSensorData(new DeviceOutputValueSensorJob(device), "0");
1065 logger.debug("Add new sceneReadingJob with priority: {} to SceneReadingJobExecuter",
1066 deviceStateUpdate.getScenePriority());
1068 removeSensorJob(device, deviceStateUpdate);
1074 public void registerDeviceListener(DeviceStatusListener deviceListener) {
1075 if (deviceListener != null) {
1076 String id = deviceListener.getDeviceStatusListenerID();
1077 if (id.equals(DeviceStatusListener.DEVICE_DISCOVERY)) {
1078 this.deviceDiscovery = deviceListener;
1079 logger.debug("register Device-Discovery ");
1080 for (Device device : strucMan.getDeviceMap().values()) {
1081 deviceDiscovery.onDeviceAdded(device);
1083 for (Circuit circuit : strucMan.getCircuitMap().values()) {
1084 deviceDiscovery.onDeviceAdded(circuit);
1087 Device intDevice = strucMan.getDeviceByDSID(deviceListener.getDeviceStatusListenerID());
1088 if (intDevice != null) {
1089 logger.debug("register DeviceListener with id: {} to Device ", id);
1090 intDevice.registerDeviceStatusListener(deviceListener);
1092 Circuit intCircuit = strucMan
1093 .getCircuitByDSID(new DSID(deviceListener.getDeviceStatusListenerID()));
1094 if (intCircuit != null) {
1095 logger.debug("register DeviceListener with id: {} to Circuit ", id);
1096 intCircuit.registerDeviceStatusListener(deviceListener);
1098 deviceListener.onDeviceRemoved(null);
1106 public void unregisterDeviceListener(DeviceStatusListener deviceListener) {
1107 if (deviceListener != null) {
1108 String id = deviceListener.getDeviceStatusListenerID();
1109 logger.debug("unregister DeviceListener with id: {}", id);
1110 if (id.equals(DeviceStatusListener.DEVICE_DISCOVERY)) {
1111 this.deviceDiscovery = null;
1113 Device intDevice = strucMan.getDeviceByDSID(deviceListener.getDeviceStatusListenerID());
1114 if (intDevice != null) {
1115 intDevice.unregisterDeviceStatusListener();
1117 Circuit intCircuit = strucMan
1118 .getCircuitByDSID(new DSID(deviceListener.getDeviceStatusListenerID()));
1119 if (intCircuit != null) {
1120 intCircuit.unregisterDeviceStatusListener();
1121 if (deviceDiscovery != null) {
1122 deviceDiscovery.onDeviceAdded(intCircuit);
1131 public void removeDevice(String dSID) {
1132 Device intDevice = strucMan.getDeviceByDSID(dSID);
1133 if (intDevice != null) {
1134 strucMan.deleteDevice(intDevice);
1135 trashDevices.add(new TrashDevice(intDevice));
1140 public void registerTotalPowerConsumptionListener(TotalPowerConsumptionListener totalPowerConsumptionListener) {
1141 this.totalPowerConsumptionListener = totalPowerConsumptionListener;
1145 public void unregisterTotalPowerConsumptionListener() {
1146 this.totalPowerConsumptionListener = null;
1150 public void registerSceneListener(SceneStatusListener sceneListener) {
1151 this.sceneMan.registerSceneListener(sceneListener);
1155 public void unregisterSceneListener(SceneStatusListener sceneListener) {
1156 this.sceneMan.unregisterSceneListener(sceneListener);
1160 public void registerStatusListener(ManagerStatusListener statusListener) {
1161 this.statusListener = statusListener;
1162 this.sceneMan.registerStatusListener(statusListener);
1166 public void unregisterStatusListener() {
1167 this.statusListener = null;
1168 this.sceneMan.unregisterStatusListener();
1172 public void registerConnectionListener(ConnectionListener connectionListener) {
1173 this.connMan.registerConnectionListener(connectionListener);
1177 public void unregisterConnectionListener() {
1178 this.connMan.unregisterConnectionListener();
1182 public int getTotalPowerConsumption() {
1183 List<CachedMeteringValue> cachedConsumptionMeteringValues = digitalSTROMClient
1184 .getLatest(connMan.getSessionToken(), MeteringTypeEnum.CONSUMPTION, DsAPI.ALL_METERS, null);
1185 if (cachedConsumptionMeteringValues != null) {
1186 tempConsumption = 0;
1187 for (CachedMeteringValue value : cachedConsumptionMeteringValues) {
1188 tempConsumption += value.getValue();
1189 if (strucMan.getCircuitByDSID(value.getDsid()) != null) {
1190 strucMan.getCircuitByDSID(value.getDsid()).addMeteringValue(value);
1194 return tempConsumption;
1197 private void getMeterData() {
1198 int val = getTotalPowerConsumption();
1199 if (totalPowerConsumptionListener != null) {
1200 totalPowerConsumptionListener.onTotalPowerConsumptionChanged(val);
1202 val = getTotalEnergyMeterValue();
1203 if (totalPowerConsumptionListener != null) {
1204 totalPowerConsumptionListener.onEnergyMeterValueChanged(val);
1209 public int getTotalEnergyMeterValue() {
1210 List<CachedMeteringValue> cachedEnergyMeteringValues = digitalSTROMClient.getLatest(connMan.getSessionToken(),
1211 MeteringTypeEnum.ENERGY, DsAPI.ALL_METERS, null);
1212 if (cachedEnergyMeteringValues != null) {
1213 tempEnergyMeter = 0;
1214 for (CachedMeteringValue value : cachedEnergyMeteringValues) {
1215 tempEnergyMeter += value.getValue();
1216 if (strucMan.getCircuitByDSID(value.getDsid()) != null) {
1217 strucMan.getCircuitByDSID(value.getDsid()).addMeteringValue(value);
1221 return tempEnergyMeter;
1225 public int getTotalEnergyMeterWsValue() {
1226 List<CachedMeteringValue> cachedEnergyMeteringValues = digitalSTROMClient.getLatest(connMan.getSessionToken(),
1227 MeteringTypeEnum.ENERGY, DsAPI.ALL_METERS, MeteringUnitsEnum.WS);
1228 if (cachedEnergyMeteringValues != null) {
1229 tempEnergyMeterWs = 0;
1230 for (CachedMeteringValue value : cachedEnergyMeteringValues) {
1231 tempEnergyMeterWs += value.getValue();
1232 if (strucMan.getCircuitByDSID(value.getDsid()) != null) {
1233 strucMan.getCircuitByDSID(value.getDsid()).addMeteringValue(value);
1237 return tempEnergyMeterWs;
1240 private void setInizialStateWithLastCallScenes() {
1241 if (sceneMan == null) {
1244 JsonObject response = connMan.getDigitalSTROMAPI().query2(connMan.getSessionToken(), LAST_CALL_SCENE_QUERY);
1245 if (!response.isJsonObject()) {
1248 for (Entry<String, JsonElement> entry : response.entrySet()) {
1249 if (!entry.getValue().isJsonObject()) {
1252 JsonObject zone = entry.getValue().getAsJsonObject();
1256 if (zone.get(JSONApiResponseKeysEnum.ZONE_ID.getKey()) != null) {
1257 zoneID = zone.get(JSONApiResponseKeysEnum.ZONE_ID.getKey()).getAsInt();
1259 for (Entry<String, JsonElement> groupEntry : zone.entrySet()) {
1260 if (groupEntry.getKey().startsWith("group") && groupEntry.getValue().isJsonObject()) {
1261 JsonObject group = groupEntry.getValue().getAsJsonObject();
1262 if (group.get(JSONApiResponseKeysEnum.DEVICES.getKey()) != null) {
1263 if (group.get(JSONApiResponseKeysEnum.GROUP.getKey()) != null) {
1264 groupID = group.get(JSONApiResponseKeysEnum.GROUP.getKey()).getAsShort();
1266 if (group.get(JSONApiResponseKeysEnum.LAST_CALL_SCENE.getKey()) != null) {
1267 sceneID = group.get(JSONApiResponseKeysEnum.LAST_CALL_SCENE.getKey()).getAsShort();
1269 if (zoneID > -1 && groupID > -1 && sceneID > -1) {
1270 logger.debug("initial state, call scene {}-{}-{}", zoneID, groupID, sceneID);
1271 sceneMan.callInternalSceneWithoutDiscovery(zoneID, groupID, sceneID);
1280 public void handleEvent(EventItem eventItem) {
1282 if (EventNames.DEVICE_SENSOR_VALUE.equals(eventItem.getName())
1283 || EventNames.DEVICE_BINARY_INPUT_EVENT.equals(eventItem.getName())) {
1284 logger.debug("Detect {} eventItem = {}", eventItem.getName(), eventItem.toString());
1285 Device dev = getDeviceOfEvent(eventItem);
1287 if (EventNames.DEVICE_SENSOR_VALUE.equals(eventItem.getName())) {
1288 dev.setDeviceSensorByEvent(eventItem);
1290 DeviceBinarayInputEnum binaryInputType = DeviceBinarayInputEnum.getdeviceBinarayInput(Short
1291 .parseShort(eventItem.getProperties().getOrDefault(EventResponseEnum.INPUT_TYPE, "")));
1292 Short newState = Short
1293 .parseShort(eventItem.getProperties().getOrDefault(EventResponseEnum.INPUT_STATE, ""));
1294 if (binaryInputType != null) {
1295 dev.setBinaryInputState(binaryInputType, newState);
1300 } catch (NumberFormatException e) {
1301 logger.debug("Unexpected missing or invalid number while handling event", e);
1305 private Device getDeviceOfEvent(EventItem eventItem) {
1306 if (eventItem.getSource().get(EventResponseEnum.DSID) != null) {
1307 String dSID = eventItem.getSource().get(EventResponseEnum.DSID);
1308 Device dev = strucMan.getDeviceByDSID(dSID);
1310 dev = strucMan.getDeviceByDSUID(dSID);
1318 public List<String> getSupportedEvents() {
1319 return SUPPORTED_EVENTS;
1323 public boolean supportsEvent(String eventName) {
1324 return SUPPORTED_EVENTS.contains(eventName);
1328 public String getUID() {
1329 return getClass().getName();
1333 public void setEventListener(EventListener eventListener) {
1334 if (this.eventListener != null) {
1335 this.eventListener.removeEventHandler(this);
1337 this.eventListener = eventListener;
1341 public void unsetEventListener(EventListener eventListener) {
1342 if (this.eventListener != null) {
1343 this.eventListener.removeEventHandler(this);
1345 this.eventListener = null;