2 * Copyright (c) 2010-2024 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.draytonwiser.internal.discovery;
15 import static org.openhab.binding.draytonwiser.internal.DraytonWiserBindingConstants.*;
17 import java.util.HashMap;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.draytonwiser.internal.DraytonWiserRefreshListener;
23 import org.openhab.binding.draytonwiser.internal.handler.DraytonWiserPropertyHelper;
24 import org.openhab.binding.draytonwiser.internal.handler.HeatHubHandler;
25 import org.openhab.binding.draytonwiser.internal.model.DeviceDTO;
26 import org.openhab.binding.draytonwiser.internal.model.DraytonWiserDTO;
27 import org.openhab.binding.draytonwiser.internal.model.HotWaterDTO;
28 import org.openhab.binding.draytonwiser.internal.model.RoomDTO;
29 import org.openhab.binding.draytonwiser.internal.model.RoomStatDTO;
30 import org.openhab.binding.draytonwiser.internal.model.SmartPlugDTO;
31 import org.openhab.binding.draytonwiser.internal.model.SmartValveDTO;
32 import org.openhab.core.config.discovery.AbstractDiscoveryService;
33 import org.openhab.core.config.discovery.DiscoveryResult;
34 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
35 import org.openhab.core.config.discovery.DiscoveryService;
36 import org.openhab.core.thing.ThingTypeUID;
37 import org.openhab.core.thing.ThingUID;
38 import org.openhab.core.thing.binding.ThingHandler;
39 import org.openhab.core.thing.binding.ThingHandlerService;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 * The {@link DraytonWiserDiscoveryService} is used to discover devices that are connected to a Heat Hub.
46 * @author Andrew Schofield - Initial contribution
49 public class DraytonWiserDiscoveryService extends AbstractDiscoveryService
50 implements DiscoveryService, ThingHandlerService, DraytonWiserRefreshListener {
52 private final Logger logger = LoggerFactory.getLogger(DraytonWiserDiscoveryService.class);
54 private @Nullable HeatHubHandler bridgeHandler;
55 private @Nullable ThingUID bridgeUID;
57 public DraytonWiserDiscoveryService() {
58 super(SUPPORTED_THING_TYPES_UIDS, 30, false);
62 public void activate() {
67 public void deactivate() {
72 protected void startScan() {
73 final HeatHubHandler handler = bridgeHandler;
74 if (handler != null) {
75 removeOlderResults(getTimestampOfLastScan());
76 handler.setDiscoveryService(this);
81 public void onRefresh(final DraytonWiserDTO domainDTOProxy) {
82 logger.debug("Received data from Drayton Wiser device. Parsing to discover devices.");
83 onControllerAdded(domainDTOProxy);
84 domainDTOProxy.getRooms().forEach(this::onRoomAdded);
85 domainDTOProxy.getRoomStats().forEach(r -> onRoomStatAdded(domainDTOProxy, r));
86 domainDTOProxy.getSmartValves().forEach(sv -> onSmartValveAdded(domainDTOProxy, sv));
87 domainDTOProxy.getHotWater().forEach(hw -> onHotWaterAdded(domainDTOProxy, hw));
88 domainDTOProxy.getSmartPlugs().forEach(sp -> onSmartPlugAdded(domainDTOProxy, sp));
91 private void onControllerAdded(final DraytonWiserDTO domainDTOProxy) {
92 final DeviceDTO device = domainDTOProxy.getExtendedDeviceProperties(0);
95 logger.debug("Controller discovered, model: {}", device.getModelIdentifier());
96 onThingWithId(THING_TYPE_CONTROLLER, "controller", device, "Controller");
100 private void onHotWaterAdded(final DraytonWiserDTO domainDTOProxy, final HotWaterDTO hotWater) {
101 final Integer hotWaterId = hotWater.getId();
102 final String roomName = getRoomName(domainDTOProxy, hotWaterId);
104 onThingWithId(THING_TYPE_HOTWATER, "hotwater" + hotWaterId, null,
105 (roomName.isEmpty() ? "" : (roomName + " - ")) + "Hot Water");
108 private void onThingWithId(final ThingTypeUID deviceType, final String deviceTypeId,
109 @Nullable final DeviceDTO device, final String name) {
110 logger.debug("{} discovered: {}", deviceTypeId, name);
111 ThingUID localBridgeUID = this.bridgeUID;
112 if (localBridgeUID != null) {
113 final Map<String, Object> properties = new HashMap<>();
115 properties.put(PROP_ID, deviceTypeId);
116 if (device != null) {
117 DraytonWiserPropertyHelper.setGeneralDeviceProperties(device, properties);
119 final DiscoveryResult discoveryResult = DiscoveryResultBuilder
120 .create(new ThingUID(deviceType, localBridgeUID, deviceTypeId)).withBridge(localBridgeUID)
121 .withProperties(properties).withRepresentationProperty(PROP_ID).withLabel(name).build();
123 thingDiscovered(discoveryResult);
127 private void onRoomStatAdded(final DraytonWiserDTO domainDTOProxy, final RoomStatDTO roomStat) {
128 final Integer roomStatId = roomStat.getId();
129 final DeviceDTO device = domainDTOProxy.getExtendedDeviceProperties(roomStatId);
131 if (device != null) {
132 onThingWithSerialNumber(THING_TYPE_ROOMSTAT, "Thermostat", device, getRoomName(domainDTOProxy, roomStatId));
136 private void onRoomAdded(final RoomDTO room) {
137 ThingUID localBridgeUID = this.bridgeUID;
138 if (localBridgeUID != null) {
139 final Map<String, Object> properties = new HashMap<>();
141 logger.debug("Room discovered: {}", room.getName());
142 properties.put(PROP_NAME, room.getName());
143 final DiscoveryResult discoveryResult = DiscoveryResultBuilder
144 .create(new ThingUID(THING_TYPE_ROOM, localBridgeUID,
145 room.getName().replaceAll("[^A-Za-z0-9]", "").toLowerCase()))
146 .withBridge(localBridgeUID).withProperties(properties).withRepresentationProperty(PROP_NAME)
147 .withLabel(room.getName()).build();
149 thingDiscovered(discoveryResult);
153 private void onSmartValveAdded(final DraytonWiserDTO domainDTOProxy, final SmartValveDTO smartValve) {
154 final Integer smartValueId = smartValve.getId();
155 final DeviceDTO device = domainDTOProxy.getExtendedDeviceProperties(smartValueId);
157 if (device != null) {
158 onThingWithSerialNumber(THING_TYPE_ITRV, "TRV", device, getRoomName(domainDTOProxy, smartValueId));
162 private void onSmartPlugAdded(final DraytonWiserDTO domainDTOProxy, final SmartPlugDTO smartPlug) {
163 final DeviceDTO device = domainDTOProxy.getExtendedDeviceProperties(smartPlug.getId());
165 if (device != null) {
166 onThingWithSerialNumber(THING_TYPE_SMARTPLUG, "Smart Plug", device, smartPlug.getName());
170 private String getRoomName(final DraytonWiserDTO domainDTOProxy, final Integer roomId) {
171 final RoomDTO assignedRoom = domainDTOProxy.getRoomForDeviceId(roomId);
172 return assignedRoom == null ? "" : assignedRoom.getName();
175 private void onThingWithSerialNumber(final ThingTypeUID deviceType, final String deviceTypeName,
176 final DeviceDTO device, final String name) {
177 final String serialNumber = device.getSerialNumber();
178 logger.debug("{} discovered, serialnumber: {}", deviceTypeName, serialNumber);
179 ThingUID localBridgeUID = this.bridgeUID;
180 if (localBridgeUID != null) {
181 final Map<String, Object> properties = new HashMap<>();
183 DraytonWiserPropertyHelper.setPropertiesWithSerialNumber(device, properties);
184 final DiscoveryResult discoveryResult = DiscoveryResultBuilder
185 .create(new ThingUID(deviceType, localBridgeUID, serialNumber)).withBridge(localBridgeUID)
186 .withProperties(properties).withRepresentationProperty(PROP_SERIAL_NUMBER)
187 .withLabel((name.isEmpty() ? "" : (name + " - ")) + deviceTypeName).build();
189 thingDiscovered(discoveryResult);
194 public synchronized void stopScan() {
195 final HeatHubHandler handler = bridgeHandler;
197 if (handler != null) {
198 handler.unsetDiscoveryService();
204 public void setThingHandler(@Nullable final ThingHandler handler) {
205 if (handler instanceof HeatHubHandler hubHandler) {
206 bridgeHandler = hubHandler;
207 bridgeUID = handler.getThing().getUID();
209 bridgeHandler = null;
215 public @Nullable ThingHandler getThingHandler() {
216 return bridgeHandler;