2 * Copyright (c) 2010-2020 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.nikohomecontrol.internal.protocol.nhc2;
15 import static org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.*;
17 import java.lang.reflect.Type;
18 import java.net.InetAddress;
19 import java.security.cert.CertificateException;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.NoSuchElementException;
23 import java.util.Objects;
24 import java.util.Optional;
25 import java.util.concurrent.CompletableFuture;
26 import java.util.concurrent.CopyOnWriteArrayList;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.ScheduledExecutorService;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.TimeoutException;
31 import java.util.stream.Collectors;
32 import java.util.stream.IntStream;
34 import org.eclipse.jdt.annotation.NonNullByDefault;
35 import org.eclipse.jdt.annotation.Nullable;
36 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcControllerEvent;
37 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
38 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType;
39 import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcDevice2.NhcProperty;
40 import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcMessage2.NhcMessageParam;
41 import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
42 import org.openhab.core.io.transport.mqtt.MqttConnectionState;
43 import org.openhab.core.io.transport.mqtt.MqttException;
44 import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import com.google.gson.FieldNamingPolicy;
49 import com.google.gson.Gson;
50 import com.google.gson.GsonBuilder;
51 import com.google.gson.JsonSyntaxException;
52 import com.google.gson.reflect.TypeToken;
55 * The {@link NikoHomeControlCommunication2} class is able to do the following tasks with Niko Home Control II
58 * <li>Start and stop MQTT connection with Niko Home Control II Connected Controller.
59 * <li>Read all setup and status information from the Niko Home Control Controller.
60 * <li>Execute Niko Home Control commands.
61 * <li>Listen for events from Niko Home Control.
64 * @author Mark Herwege - Initial Contribution
67 public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
68 implements MqttMessageSubscriber, MqttConnectionObserver {
70 private final Logger logger = LoggerFactory.getLogger(NikoHomeControlCommunication2.class);
72 private final NhcMqttConnection2 mqttConnection;
74 private final List<NhcService2> services = new CopyOnWriteArrayList<>();
76 private volatile String profile = "";
78 private volatile @Nullable NhcSystemInfo2 nhcSystemInfo;
79 private volatile @Nullable NhcTimeInfo2 nhcTimeInfo;
81 private volatile @Nullable CompletableFuture<Boolean> communicationStarted;
83 private ScheduledExecutorService scheduler;
85 private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
88 * Constructor for Niko Home Control communication object, manages communication with
89 * Niko Home Control II Connected Controller.
91 * @throws CertificateException when the SSL context for MQTT communication cannot be created
92 * @throws UnknownHostException when the IP address is not provided
95 public NikoHomeControlCommunication2(NhcControllerEvent handler, String clientId,
96 ScheduledExecutorService scheduler) throws CertificateException {
98 mqttConnection = new NhcMqttConnection2(clientId, this, this);
99 this.scheduler = scheduler;
103 public synchronized void startCommunication() {
104 communicationStarted = new CompletableFuture<>();
106 InetAddress addr = handler.getAddr();
108 logger.warn("Niko Home Control: IP address cannot be empty");
112 String addrString = addr.getHostAddress();
113 int port = handler.getPort();
114 logger.debug("Niko Home Control: initializing for mqtt connection to CoCo on {}:{}", addrString, port);
116 profile = handler.getProfile();
118 String token = handler.getToken();
119 if (token.isEmpty()) {
120 logger.warn("Niko Home Control: JWT token cannot be empty");
126 mqttConnection.startConnection(addrString, port, profile, token);
128 } catch (MqttException e) {
129 logger.warn("Niko Home Control: error in mqtt communication");
135 public synchronized void stopCommunication() {
136 CompletableFuture<Boolean> started = communicationStarted;
137 if (started != null) {
138 started.complete(false);
140 communicationStarted = null;
141 mqttConnection.stopConnection();
145 public boolean communicationActive() {
146 CompletableFuture<Boolean> started = communicationStarted;
147 if (started == null) {
151 // Wait until we received all devices info to confirm we are active.
152 return started.get(5000, TimeUnit.MILLISECONDS);
153 } catch (InterruptedException | ExecutionException | TimeoutException e) {
154 logger.debug("Niko Home Control: exception waiting for connection start");
160 * After setting up the communication with the Niko Home Control Connected Controller, send all initialization
164 private void initialize() throws MqttException {
165 NhcMessage2 message = new NhcMessage2();
167 message.method = "systeminfo.publish";
168 mqttConnection.connectionPublish(profile + "/system/cmd", gson.toJson(message));
170 message.method = "services.list";
171 mqttConnection.connectionPublish(profile + "/authentication/cmd", gson.toJson(message));
173 message.method = "devices.list";
174 mqttConnection.connectionPublish(profile + "/control/devices/cmd", gson.toJson(message));
176 message.method = "notifications.list";
177 mqttConnection.connectionPublish(profile + "/notification/cmd", gson.toJson(message));
180 private void connectionLost() {
181 logger.debug("Niko Home Control: connection lost");
183 handler.controllerOffline();
186 private void systemEvt(String response) {
187 Type messageType = new TypeToken<NhcMessage2>() {
189 List<NhcTimeInfo2> timeInfo = null;
190 List<NhcSystemInfo2> systemInfo = null;
192 NhcMessage2 message = gson.fromJson(response, messageType);
193 List<NhcMessageParam> messageParams = message.params;
194 if (messageParams != null) {
195 timeInfo = messageParams.stream().filter(p -> (p.timeInfo != null)).findFirst().get().timeInfo;
196 systemInfo = messageParams.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo;
198 } catch (JsonSyntaxException e) {
199 logger.debug("Niko Home Control: unexpected json {}", response);
200 } catch (NoSuchElementException ignore) {
201 // Ignore if timeInfo not present in response, this should not happen in a timeInfo response
203 if (timeInfo != null) {
204 nhcTimeInfo = timeInfo.get(0);
206 if (systemInfo != null) {
207 nhcSystemInfo = systemInfo.get(0);
208 handler.updatePropertiesEvent();
212 private void systeminfoPublishRsp(String response) {
213 Type messageType = new TypeToken<NhcMessage2>() {
215 List<NhcSystemInfo2> systemInfo = null;
217 NhcMessage2 message = gson.fromJson(response, messageType);
218 List<NhcMessageParam> messageParams = message.params;
219 if (messageParams != null) {
220 systemInfo = messageParams.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo;
222 } catch (JsonSyntaxException e) {
223 logger.debug("Niko Home Control: unexpected json {}", response);
224 } catch (NoSuchElementException ignore) {
225 // Ignore if systemInfo not present in response, this should not happen in a systemInfo response
227 if (systemInfo != null) {
228 nhcSystemInfo = systemInfo.get(0);
232 private void servicesListRsp(String response) {
233 Type messageType = new TypeToken<NhcMessage2>() {
235 List<NhcService2> serviceList = null;
237 NhcMessage2 message = gson.fromJson(response, messageType);
238 List<NhcMessageParam> messageParams = message.params;
239 if (messageParams != null) {
240 serviceList = messageParams.stream().filter(p -> (p.services != null)).findFirst().get().services;
242 } catch (JsonSyntaxException e) {
243 logger.debug("Niko Home Control: unexpected json {}", response);
244 } catch (NoSuchElementException ignore) {
245 // Ignore if services not present in response, this should not happen in a services response
248 if (serviceList != null) {
249 services.addAll(serviceList);
253 private void devicesListRsp(String response) {
254 Type messageType = new TypeToken<NhcMessage2>() {
256 List<NhcDevice2> deviceList = null;
258 NhcMessage2 message = gson.fromJson(response, messageType);
259 List<NhcMessageParam> messageParams = message.params;
260 if (messageParams != null) {
261 deviceList = messageParams.stream().filter(p -> (p.devices != null)).findFirst().get().devices;
263 } catch (JsonSyntaxException e) {
264 logger.debug("Niko Home Control: unexpected json {}", response);
265 } catch (NoSuchElementException ignore) {
266 // Ignore if devices not present in response, this should not happen in a devices response
268 if (deviceList == null) {
272 for (NhcDevice2 device : deviceList) {
277 // Once a devices list response is received, we know the communication is fully started.
278 logger.debug("Niko Home Control: Communication start complete.");
279 handler.controllerOnline();
280 CompletableFuture<Boolean> future = communicationStarted;
281 if (future != null) {
282 future.complete(true);
286 private void devicesEvt(String response) {
287 Type messageType = new TypeToken<NhcMessage2>() {
289 List<NhcDevice2> deviceList = null;
290 String method = null;
292 NhcMessage2 message = gson.fromJson(response, messageType);
293 method = message.method;
294 List<NhcMessageParam> messageParams = message.params;
295 if (messageParams != null) {
296 deviceList = messageParams.stream().filter(p -> (p.devices != null)).findFirst().get().devices;
298 } catch (JsonSyntaxException e) {
299 logger.debug("Niko Home Control: unexpected json {}", response);
300 } catch (NoSuchElementException ignore) {
301 // Ignore if devices not present in response, this should not happen in a devices event
303 if (deviceList == null) {
307 if ("devices.removed".equals(method)) {
308 deviceList.forEach(this::removeDevice);
310 } else if ("devices.added".equals(method)) {
311 deviceList.forEach(this::addDevice);
312 } else if ("devices.changed".contentEquals(method)) {
313 deviceList.forEach(this::removeDevice);
314 deviceList.forEach(this::addDevice);
317 deviceList.forEach(this::updateState);
320 private void notificationEvt(String response) {
321 Type messageType = new TypeToken<NhcMessage2>() {
323 List<NhcNotification2> notificationList = null;
325 NhcMessage2 message = gson.fromJson(response, messageType);
326 List<NhcMessageParam> messageParams = message.params;
327 if (messageParams != null) {
328 notificationList = messageParams.stream().filter(p -> (p.notifications != null)).findFirst()
329 .get().notifications;
331 } catch (JsonSyntaxException e) {
332 logger.debug("Niko Home Control: unexpected json {}", response);
333 } catch (NoSuchElementException ignore) {
334 // Ignore if notifications not present in response, this should not happen in a notifications event
336 logger.debug("Niko Home Control: notifications {}", notificationList);
337 if (notificationList == null) {
341 for (NhcNotification2 notification : notificationList) {
342 if ("new".equals(notification.status)) {
343 String alarmText = notification.text;
344 switch (notification.type) {
346 handler.alarmEvent(alarmText);
349 handler.noticeEvent(alarmText);
352 logger.debug("Niko Home Control: unexpected message type {}", notification.type);
358 private void addDevice(NhcDevice2 device) {
359 String location = null;
360 if (device.parameters != null) {
361 location = device.parameters.stream().map(p -> p.locationName).filter(Objects::nonNull).findFirst()
365 if ("action".equals(device.type)) {
366 if (!actions.containsKey(device.uuid)) {
367 logger.debug("Niko Home Control: adding action device {}, {}", device.uuid, device.name);
369 ActionType actionType;
370 switch (device.model) {
377 case "overallcomfort":
379 actionType = ActionType.TRIGGER;
383 case "switched-generic":
385 actionType = ActionType.RELAY;
388 actionType = ActionType.DIMMER;
390 case "rolldownshutter":
392 case "venetianblind":
394 actionType = ActionType.ROLLERSHUTTER;
397 actionType = ActionType.GENERIC;
398 logger.debug("Niko Home Control: device type {} not recognised, default to GENERIC action",
402 NhcAction2 nhcAction = new NhcAction2(device.uuid, device.name, device.model, device.technology,
403 actionType, location, this);
404 actions.put(device.uuid, nhcAction);
406 } else if ("thermostat".equals(device.type)) {
407 if (!thermostats.containsKey(device.uuid)) {
408 logger.debug("Niko Home Control: adding thermostat device {}, {}", device.uuid, device.name);
410 NhcThermostat2 nhcThermostat = new NhcThermostat2(device.uuid, device.name, device.model,
411 device.technology, location, this);
412 thermostats.put(device.uuid, nhcThermostat);
414 } else if ("centralmeter".equals(device.type)) {
415 if (!energyMeters.containsKey(device.uuid)) {
416 logger.debug("Niko Home Control: adding centralmeter device {}, {}", device.uuid, device.name);
417 NhcEnergyMeter2 nhcEnergyMeter = new NhcEnergyMeter2(device.uuid, device.name, device.model,
418 device.technology, this, scheduler);
419 energyMeters.put(device.uuid, nhcEnergyMeter);
422 logger.debug("Niko Home Control: device type {} not supported for {}, {}", device.type, device.uuid,
427 private void removeDevice(NhcDevice2 device) {
428 if (actions.containsKey(device.uuid)) {
429 actions.get(device.uuid).actionRemoved();
430 actions.remove(device.uuid);
431 } else if (thermostats.containsKey(device.uuid)) {
432 thermostats.get(device.uuid).thermostatRemoved();
433 thermostats.remove(device.uuid);
434 } else if (energyMeters.containsKey(device.uuid)) {
435 energyMeters.get(device.uuid).energyMeterRemoved();
436 energyMeters.remove(device.uuid);
440 private void updateState(NhcDevice2 device) {
441 List<NhcProperty> deviceProperties = device.properties;
442 if (deviceProperties == null) {
443 logger.debug("Cannot Update state for {} as no properties defined in device message", device.uuid);
447 if (actions.containsKey(device.uuid)) {
448 updateActionState((NhcAction2) actions.get(device.uuid), deviceProperties);
449 } else if (thermostats.containsKey(device.uuid)) {
450 updateThermostatState((NhcThermostat2) thermostats.get(device.uuid), deviceProperties);
451 } else if (energyMeters.containsKey(device.uuid)) {
452 updateEnergyMeterState((NhcEnergyMeter2) energyMeters.get(device.uuid), deviceProperties);
456 private void updateActionState(NhcAction2 action, List<NhcProperty> deviceProperties) {
457 if (action.getType() == ActionType.ROLLERSHUTTER) {
458 updateRollershutterState(action, deviceProperties);
460 updateLightState(action, deviceProperties);
464 private void updateLightState(NhcAction2 action, List<NhcProperty> deviceProperties) {
465 Optional<NhcProperty> statusProperty = deviceProperties.stream().filter(p -> (p.status != null)).findFirst();
466 Optional<NhcProperty> dimmerProperty = deviceProperties.stream().filter(p -> (p.brightness != null))
468 Optional<NhcProperty> basicStateProperty = deviceProperties.stream().filter(p -> (p.basicState != null))
471 String booleanState = null;
472 if (statusProperty.isPresent()) {
473 booleanState = statusProperty.get().status;
474 } else if (basicStateProperty.isPresent()) {
475 booleanState = basicStateProperty.get().basicState;
478 if (booleanState != null) {
479 if (NHCON.equals(booleanState)) {
480 action.setBooleanState(true);
481 logger.debug("Niko Home Control: setting action {} internally to ON", action.getId());
482 } else if (NHCOFF.equals(booleanState)) {
483 action.setBooleanState(false);
484 logger.debug("Niko Home Control: setting action {} internally to OFF", action.getId());
488 if (dimmerProperty.isPresent()) {
489 String brightness = dimmerProperty.get().brightness;
490 if (brightness != null) {
491 action.setState(Integer.parseInt(brightness));
492 logger.debug("Niko Home Control: setting action {} internally to {}", action.getId(),
493 dimmerProperty.get().brightness);
498 private void updateRollershutterState(NhcAction2 action, List<NhcProperty> deviceProperties) {
499 deviceProperties.stream().map(p -> p.position).filter(Objects::nonNull).findFirst().ifPresent(position -> {
501 action.setState(Integer.parseInt(position));
502 logger.debug("Niko Home Control: setting action {} internally to {}", action.getId(), position);
503 } catch (NumberFormatException e) {
504 logger.trace("Niko Home Control: received empty rollershutter {} position info", action.getId());
509 private void updateThermostatState(NhcThermostat2 thermostat, List<NhcProperty> deviceProperties) {
510 Optional<Boolean> overruleActiveProperty = deviceProperties.stream().map(p -> p.overruleActive)
511 .filter(Objects::nonNull).map(t -> Boolean.parseBoolean(t)).findFirst();
512 Optional<Integer> overruleSetpointProperty = deviceProperties.stream().map(p -> p.overruleSetpoint)
513 .map(s -> (!((s == null) || s.isEmpty())) ? Math.round(Float.parseFloat(s) * 10) : null)
514 .filter(Objects::nonNull).findFirst();
515 Optional<Integer> overruleTimeProperty = deviceProperties.stream().map(p -> p.overruleTime)
516 .map(s -> (!((s == null) || s.isEmpty())) ? Math.round(Float.parseFloat(s)) : null)
517 .filter(Objects::nonNull).findFirst();
518 Optional<Integer> setpointTemperatureProperty = deviceProperties.stream().map(p -> p.setpointTemperature)
519 .map(s -> (!((s == null) || s.isEmpty())) ? Math.round(Float.parseFloat(s) * 10) : null)
520 .filter(Objects::nonNull).findFirst();
521 Optional<Boolean> ecoSaveProperty = deviceProperties.stream().map(p -> p.ecoSave)
522 .map(s -> s != null ? Boolean.parseBoolean(s) : null).filter(Objects::nonNull).findFirst();
523 Optional<Integer> ambientTemperatureProperty = deviceProperties.stream().map(p -> p.ambientTemperature)
524 .map(s -> (!((s == null) || s.isEmpty())) ? Math.round(Float.parseFloat(s) * 10) : null)
525 .filter(Objects::nonNull).findFirst();
526 Optional<@Nullable String> demandProperty = deviceProperties.stream().map(p -> p.demand)
527 .filter(Objects::nonNull).findFirst();
528 Optional<@Nullable String> operationModeProperty = deviceProperties.stream().map(p -> p.operationMode)
529 .filter(Objects::nonNull).findFirst();
531 String modeString = deviceProperties.stream().map(p -> p.program).filter(Objects::nonNull).findFirst()
533 int mode = IntStream.range(0, THERMOSTATMODES.length).filter(i -> THERMOSTATMODES[i].equals(modeString))
534 .findFirst().orElse(thermostat.getMode());
536 int measured = ambientTemperatureProperty.orElse(thermostat.getMeasured());
537 int setpoint = setpointTemperatureProperty.orElse(thermostat.getSetpoint());
539 int overrule = thermostat.getOverrule();
540 int overruletime = thermostat.getRemainingOverruletime();
541 if (overruleActiveProperty.orElse(false)) {
542 overrule = overruleSetpointProperty.orElse(0);
543 overruletime = overruleTimeProperty.orElse(0);
546 int ecosave = thermostat.getEcosave();
547 if (ecoSaveProperty.orElse(false)) {
551 int demand = thermostat.getDemand();
552 String demandString = demandProperty.orElse(operationModeProperty.orElse(""));
553 demandString = demandString == null ? "" : demandString;
554 switch (demandString) {
567 "Niko Home Control: setting thermostat {} with measured {}, setpoint {}, mode {}, overrule {}, overruletime {}, ecosave {}, demand {}",
568 thermostat.getId(), measured, setpoint, mode, overrule, overruletime, ecosave, demand);
569 thermostat.updateState(measured, setpoint, mode, overrule, overruletime, ecosave, demand);
572 private void updateEnergyMeterState(NhcEnergyMeter2 energyMeter, List<NhcProperty> deviceProperties) {
573 deviceProperties.stream().map(p -> p.electricalPower).filter(Objects::nonNull).findFirst()
574 .ifPresent(electricalPower -> {
576 energyMeter.setPower(Integer.parseInt(electricalPower));
577 logger.trace("Niko Home Control: setting energy meter {} power to {}", energyMeter.getId(),
579 } catch (NumberFormatException e) {
580 energyMeter.setPower(null);
581 logger.trace("Niko Home Control: received empty energy meter {} power reading",
582 energyMeter.getId());
588 public void executeAction(String actionId, String value) {
589 NhcMessage2 message = new NhcMessage2();
591 message.method = "devices.control";
592 ArrayList<NhcMessageParam> params = new ArrayList<>();
593 NhcMessageParam param = new NhcMessageParam();
595 message.params = params;
596 ArrayList<NhcDevice2> devices = new ArrayList<>();
597 NhcDevice2 device = new NhcDevice2();
599 param.devices = devices;
600 device.uuid = actionId;
601 ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
602 NhcProperty property = new NhcProperty();
603 deviceProperties.add(property);
604 device.properties = deviceProperties;
606 NhcAction2 action = (NhcAction2) actions.get(actionId);
608 switch (action.getType()) {
611 property.basicState = NHCTRIGGERED;
614 property.status = value;
617 if (NHCON.equals(value)) {
618 action.setBooleanState(true); // this will trigger sending the stored brightness value event out
619 property.status = value;
620 } else if (NHCOFF.equals(value)) {
621 property.status = value;
623 // If the light is off, turn the light on before sending the brightness value, needs to happen
624 // in 2 separate messages.
625 if (!action.booleanState()) {
626 executeAction(actionId, NHCON);
628 property.brightness = value;
632 if (NHCSTOP.equals(value)) {
633 property.action = value;
634 } else if (NHCUP.equals(value)) {
635 property.position = "100";
636 } else if (NHCDOWN.equals(value)) {
637 property.position = "0";
639 int position = 100 - Integer.parseInt(value);
640 property.position = String.valueOf(position);
645 String topic = profile + "/control/devices/cmd";
646 String gsonMessage = gson.toJson(message);
647 sendDeviceMessage(topic, gsonMessage);
651 public void executeThermostat(String thermostatId, String mode) {
652 NhcMessage2 message = new NhcMessage2();
654 message.method = "devices.control";
655 ArrayList<NhcMessageParam> params = new ArrayList<>();
656 NhcMessageParam param = new NhcMessageParam();
658 message.params = params;
659 ArrayList<NhcDevice2> devices = new ArrayList<>();
660 NhcDevice2 device = new NhcDevice2();
662 param.devices = devices;
663 device.uuid = thermostatId;
664 ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
666 NhcProperty overruleActiveProp = new NhcProperty();
667 deviceProperties.add(overruleActiveProp);
668 overruleActiveProp.overruleActive = "False";
670 NhcProperty program = new NhcProperty();
671 deviceProperties.add(program);
672 program.program = mode;
674 device.properties = deviceProperties;
676 String topic = profile + "/control/devices/cmd";
677 String gsonMessage = gson.toJson(message);
678 sendDeviceMessage(topic, gsonMessage);
682 public void executeThermostat(String thermostatId, int overruleTemp, int overruleTime) {
683 NhcMessage2 message = new NhcMessage2();
685 message.method = "devices.control";
686 ArrayList<NhcMessageParam> params = new ArrayList<>();
687 NhcMessageParam param = new NhcMessageParam();
689 message.params = params;
690 ArrayList<NhcDevice2> devices = new ArrayList<>();
691 NhcDevice2 device = new NhcDevice2();
693 param.devices = devices;
694 device.uuid = thermostatId;
695 ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
697 if (overruleTime > 0) {
698 NhcProperty overruleActiveProp = new NhcProperty();
699 overruleActiveProp.overruleActive = "True";
700 deviceProperties.add(overruleActiveProp);
702 NhcProperty overruleSetpointProp = new NhcProperty();
703 overruleSetpointProp.overruleSetpoint = String.valueOf(overruleTemp / 10.0);
704 deviceProperties.add(overruleSetpointProp);
706 NhcProperty overruleTimeProp = new NhcProperty();
707 overruleTimeProp.overruleTime = String.valueOf(overruleTime);
708 deviceProperties.add(overruleTimeProp);
710 NhcProperty overruleActiveProp = new NhcProperty();
711 overruleActiveProp.overruleActive = "False";
712 deviceProperties.add(overruleActiveProp);
714 device.properties = deviceProperties;
716 String topic = profile + "/control/devices/cmd";
717 String gsonMessage = gson.toJson(message);
718 sendDeviceMessage(topic, gsonMessage);
722 public void startEnergyMeter(String energyMeterId) {
723 NhcMessage2 message = new NhcMessage2();
725 message.method = "devices.control";
726 ArrayList<NhcMessageParam> params = new ArrayList<>();
727 NhcMessageParam param = new NhcMessageParam();
729 message.params = params;
730 ArrayList<NhcDevice2> devices = new ArrayList<>();
731 NhcDevice2 device = new NhcDevice2();
733 param.devices = devices;
734 device.uuid = energyMeterId;
735 ArrayList<NhcProperty> deviceProperties = new ArrayList<>();
737 NhcProperty reportInstantUsageProp = new NhcProperty();
738 deviceProperties.add(reportInstantUsageProp);
739 reportInstantUsageProp.reportInstantUsage = "True";
740 device.properties = deviceProperties;
742 String topic = profile + "/control/devices/cmd";
743 String gsonMessage = gson.toJson(message);
745 ((NhcEnergyMeter2) energyMeters.get(energyMeterId)).startEnergyMeter(topic, gsonMessage);
749 public void stopEnergyMeter(String energyMeterId) {
750 ((NhcEnergyMeter2) energyMeters.get(energyMeterId)).stopEnergyMeter();
754 * Method called from the {@link NhcEnergyMeter2} object to send message to Niko Home Control.
759 public void executeEnergyMeter(String topic, String gsonMessage) {
760 sendDeviceMessage(topic, gsonMessage);
763 private void sendDeviceMessage(String topic, String gsonMessage) {
765 mqttConnection.connectionPublish(topic, gsonMessage);
767 } catch (MqttException e) {
768 logger.warn("Niko Home Control: sending command failed, trying to restart communication");
769 restartCommunication();
770 // retry sending after restart
772 if (communicationActive()) {
773 mqttConnection.connectionPublish(topic, gsonMessage);
775 logger.warn("Niko Home Control: failed to restart communication");
778 } catch (MqttException e1) {
779 logger.warn("Niko Home Control: error resending device command");
786 public void processMessage(String topic, byte[] payload) {
787 String message = new String(payload);
788 if ((profile + "/system/evt").equals(topic)) {
790 } else if ((profile + "/system/rsp").equals(topic)) {
791 logger.debug("Niko Home Control: received topic {}, payload {}", topic, message);
792 systeminfoPublishRsp(message);
793 } else if ((profile + "/notification/evt").equals(topic)) {
794 logger.debug("Niko Home Control: received topic {}, payload {}", topic, message);
795 notificationEvt(message);
796 } else if ((profile + "/control/devices/evt").equals(topic)) {
797 logger.trace("Niko Home Control: received topic {}, payload {}", topic, message);
799 } else if ((profile + "/control/devices/rsp").equals(topic)) {
800 logger.debug("Niko Home Control: received topic {}, payload {}", topic, message);
801 devicesListRsp(message);
802 } else if ((profile + "/authentication/rsp").equals(topic)) {
803 logger.debug("Niko Home Control: received topic {}, payload {}", topic, message);
804 servicesListRsp(message);
805 } else if ((profile + "/control/devices.error").equals(topic)) {
806 logger.warn("Niko Home Control: received error {}", message);
808 logger.trace("Niko Home Control: not acted on received message topic {}, payload {}", topic, message);
813 * @return system info retrieved from Connected Controller
815 public NhcSystemInfo2 getSystemInfo() {
816 NhcSystemInfo2 systemInfo = nhcSystemInfo;
817 if (systemInfo == null) {
818 systemInfo = new NhcSystemInfo2();
824 * @return time info retrieved from Connected Controller
826 public NhcTimeInfo2 getTimeInfo() {
827 NhcTimeInfo2 timeInfo = nhcTimeInfo;
828 if (timeInfo == null) {
829 timeInfo = new NhcTimeInfo2();
835 * @return comma separated list of services retrieved from Connected Controller
837 public String getServices() {
838 return services.stream().map(NhcService2::name).collect(Collectors.joining(", "));
842 public void connectionStateChanged(MqttConnectionState state, @Nullable Throwable error) {
844 logger.debug("Connection state: {}", state, error);
845 restartCommunication();
846 if (!communicationActive()) {
847 logger.warn("Niko Home Control: failed to restart communication");
851 logger.trace("Connection state: {}", state);