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.haywardomnilogic.internal.discovery;
15 import static org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants.THING_TYPES_UIDS;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
22 import java.util.function.BiConsumer;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants;
27 import org.openhab.binding.haywardomnilogic.internal.HaywardException;
28 import org.openhab.binding.haywardomnilogic.internal.HaywardTypeToRequest;
29 import org.openhab.binding.haywardomnilogic.internal.handler.HaywardBridgeHandler;
30 import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
31 import org.openhab.core.config.discovery.DiscoveryResult;
32 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
33 import org.openhab.core.thing.ThingTypeUID;
34 import org.openhab.core.thing.ThingUID;
35 import org.osgi.service.component.annotations.Component;
36 import org.osgi.service.component.annotations.ServiceScope;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * Sets up the discovery results and details
43 * @author Matt Myers - Initial contribution
45 @Component(scope = ServiceScope.PROTOTYPE, service = HaywardDiscoveryService.class)
47 public class HaywardDiscoveryService extends AbstractThingHandlerDiscoveryService<HaywardBridgeHandler> {
48 private final Logger logger = LoggerFactory.getLogger(HaywardDiscoveryService.class);
50 public HaywardDiscoveryService() {
51 super(HaywardBridgeHandler.class, THING_TYPES_UIDS, 0, false);
55 protected void startScan() {
57 String xmlResults = thingHandler.getMspConfig();
58 mspConfigDiscovery(xmlResults);
59 } catch (HaywardException e) {
60 logger.warn("Exception during discovery scan: {}", e.getMessage());
61 } catch (InterruptedException e) {
66 public synchronized void mspConfigDiscovery(String xmlResponse) {
67 List<String> systemIDs = new ArrayList<>();
68 List<String> names = new ArrayList<>();
69 Map<String, Object> backyardProperties = new HashMap<>();
70 Map<String, Object> bowProperties = new HashMap<>();
73 names = thingHandler.evaluateXPath("//Backyard/Name/text()", xmlResponse);
75 for (int i = 0; i < names.size(); i++) {
76 backyardProperties.put(HaywardBindingConstants.PROPERTY_TYPE, HaywardTypeToRequest.BACKYARD);
77 backyardProperties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, thingHandler.account.mspSystemID);
79 onDeviceDiscovered(HaywardBindingConstants.THING_TYPE_BACKYARD, names.get(i), backyardProperties);
82 // Find Bodies of Water
83 systemIDs = thingHandler.evaluateXPath("//Body-of-water/System-Id/text()", xmlResponse);
84 names = thingHandler.evaluateXPath("//Body-of-water/Name/text()", xmlResponse);
86 final List<String> bowProperty1 = thingHandler.evaluateXPath("//Body-of-water/Type/text()", xmlResponse);
87 final List<String> bowProperty2 = thingHandler.evaluateXPath("//Body-of-water/Shared-Type/text()", xmlResponse);
88 final List<String> bowProperty3 = thingHandler.evaluateXPath("//Body-of-water/Shared-Priority/text()",
90 final List<String> bowProperty4 = thingHandler
91 .evaluateXPath("//Body-of-water/Shared-Equipment-System-ID/text()", xmlResponse);
92 final List<String> bowProperty5 = thingHandler.evaluateXPath("//Body-of-water/Supports-Spillover/text()",
94 final List<String> bowProperty6 = thingHandler.evaluateXPath("//Body-of-water/Size-In-Gallons/text()",
97 for (int i = 0; i < systemIDs.size(); i++) {
98 bowProperties.put(HaywardBindingConstants.PROPERTY_TYPE, HaywardTypeToRequest.BOW);
99 bowProperties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, systemIDs.get(i));
100 bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_TYPE, bowProperty1.get(i));
101 bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SHAREDTYPE, bowProperty2.get(i));
102 bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SHAREDPRIORITY, bowProperty3.get(i));
103 bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SHAREDEQUIPID, bowProperty4.get(i));
104 bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SUPPORTSSPILLOVER, bowProperty5.get(i));
105 bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SIZEINGALLONS, bowProperty6.get(i));
107 onDeviceDiscovered(HaywardBindingConstants.THING_TYPE_BOW, names.get(i), bowProperties);
111 final List<String> chlorinatorProperty1 = thingHandler
112 .evaluateXPath("//Body-of-water/Chlorinator/Shared-Type/text()", xmlResponse);
113 final List<String> chlorinatorProperty2 = thingHandler.evaluateXPath("//Body-of-water/Chlorinator/Mode/text()",
115 final List<String> chlorinatorProperty3 = thingHandler
116 .evaluateXPath("//Body-of-water/Chlorinator/Cell-Type/text()", xmlResponse);
117 final List<String> chlorinatorProperty4 = thingHandler
118 .evaluateXPath("//Body-of-water/Chlorinator/Dispenser-Type/text()", xmlResponse);
120 discoverDevices(thingHandler, xmlResponse, "Chlorinator", HaywardTypeToRequest.CHLORINATOR,
121 HaywardBindingConstants.THING_TYPE_CHLORINATOR, (props, i) -> {
122 props.put(HaywardBindingConstants.PROPERTY_CHLORINATOR_SHAREDTYPE, chlorinatorProperty1.get(i));
123 props.put(HaywardBindingConstants.PROPERTY_CHLORINATOR_MODE, chlorinatorProperty2.get(i));
124 props.put(HaywardBindingConstants.PROPERTY_CHLORINATOR_CELLTYPE, chlorinatorProperty3.get(i));
125 props.put(HaywardBindingConstants.PROPERTY_CHLORINATOR_DISPENSERTYPE, chlorinatorProperty4.get(i));
128 // Find ColorLogic Lights
129 final List<String> colorLogicProperty1 = thingHandler.evaluateXPath("//Backyard//ColorLogic-Light/Type/text()",
132 discoverDevices(thingHandler, xmlResponse, "ColorLogic-Light", HaywardTypeToRequest.COLORLOGIC,
133 HaywardBindingConstants.THING_TYPE_COLORLOGIC, (props, i) -> {
134 props.put(HaywardBindingConstants.PROPERTY_COLORLOGIC_TYPE, colorLogicProperty1.get(i));
138 final List<String> filterProperty1 = thingHandler.evaluateXPath("//Body-of-water/Filter/Shared-Type/text()",
140 final List<String> filterProperty2 = thingHandler.evaluateXPath("//Body-of-water/Filter/Filter-Type/text()",
142 final List<String> filterProperty3 = thingHandler.evaluateXPath("//Body-of-water/Filter/Priming-Enabled/text()",
144 final List<String> filterProperty4 = thingHandler.evaluateXPath("//Body-of-water/Filter/Min-Pump-Speed/text()",
146 final List<String> filterProperty5 = thingHandler.evaluateXPath("//Body-of-water/Filter/Max-Pump-Speed/text()",
148 final List<String> filterProperty6 = thingHandler.evaluateXPath("//Body-of-water/Filter/Min-Pump-RPM/text()",
150 final List<String> filterProperty7 = thingHandler.evaluateXPath("//Body-of-water/Filter/Max-Pump-RPM/text()",
152 final List<String> filterProperty8 = thingHandler
153 .evaluateXPath("//Body-of-water/Filter/Vsp-Low-Pump-Speed/text()", xmlResponse);
154 final List<String> filterProperty9 = thingHandler
155 .evaluateXPath("//Body-of-water/Filter/Vsp-Medium-Pump-Speed/text()", xmlResponse);
156 final List<String> filterProperty10 = thingHandler
157 .evaluateXPath("//Body-of-water/Filter/Vsp-High-Pump-Speed/text()", xmlResponse);
158 final List<String> filterProperty11 = thingHandler
159 .evaluateXPath("//Body-of-water/Filter/Vsp-Custom-Pump-Speed/text()", xmlResponse);
160 final List<String> filterProperty12 = thingHandler
161 .evaluateXPath("//Body-of-water/Filter/Freeze-Protect-Override-Interval/text()", xmlResponse);
163 discoverDevices(thingHandler, xmlResponse, "Filter", HaywardTypeToRequest.FILTER,
164 HaywardBindingConstants.THING_TYPE_FILTER, (props, i) -> {
165 props.put(HaywardBindingConstants.PROPERTY_FILTER_SHAREDTYPE, filterProperty1.get(i));
166 props.put(HaywardBindingConstants.PROPERTY_FILTER_FILTERTYPE, filterProperty2.get(i));
167 props.put(HaywardBindingConstants.PROPERTY_FILTER_PRIMINGENABLED, filterProperty3.get(i));
168 props.put(HaywardBindingConstants.PROPERTY_FILTER_MINSPEED, filterProperty4.get(i));
169 props.put(HaywardBindingConstants.PROPERTY_FILTER_MAXSPEED, filterProperty5.get(i));
170 props.put(HaywardBindingConstants.PROPERTY_FILTER_MINRPM, filterProperty6.get(i));
171 props.put(HaywardBindingConstants.PROPERTY_FILTER_MAXRPM, filterProperty7.get(i));
172 props.put(HaywardBindingConstants.PROPERTY_FILTER_LOWSPEED, filterProperty8.get(i));
173 props.put(HaywardBindingConstants.PROPERTY_FILTER_MEDSPEED, filterProperty9.get(i));
174 props.put(HaywardBindingConstants.PROPERTY_FILTER_HIGHSPEED, filterProperty10.get(i));
175 props.put(HaywardBindingConstants.PROPERTY_FILTER_CUSTOMSPEED, filterProperty11.get(i));
176 props.put(HaywardBindingConstants.PROPERTY_FILTER_FREEZEPROTECTOVERRIDEINTERVAL,
177 filterProperty12.get(i));
181 final List<String> heaterProperty1 = thingHandler
182 .evaluateXPath("//Body-of-water/Heater/Operation/Heater-Equipment/Type/text()", xmlResponse);
183 final List<String> heaterProperty2 = thingHandler
184 .evaluateXPath("//Body-of-water/Heater/Operation/Heater-Equipment/Heater-Type/text()", xmlResponse);
185 final List<String> heaterProperty3 = thingHandler.evaluateXPath(
186 "//Body-of-water/Heater/Operation/Heater-Equipment/Shared-Equipment-System-ID/text()", xmlResponse);
188 discoverDevices(thingHandler, xmlResponse, "Heater-Equipment", HaywardTypeToRequest.HEATER,
189 HaywardBindingConstants.THING_TYPE_HEATER, (props, i) -> {
190 props.put(HaywardBindingConstants.PROPERTY_HEATER_TYPE, heaterProperty1.get(i));
191 props.put(HaywardBindingConstants.PROPERTY_HEATER_HEATERTYPE, heaterProperty2.get(i));
192 props.put(HaywardBindingConstants.PROPERTY_HEATER_SHAREDEQUIPID, heaterProperty3.get(i));
196 final List<String> pumpProperty1 = thingHandler.evaluateXPath("//Body-of-water/Pump/Type/text()", xmlResponse);
197 final List<String> pumpProperty2 = thingHandler.evaluateXPath("//Body-of-water/Pump/Function/text()",
199 final List<String> pumpProperty3 = thingHandler.evaluateXPath("//Body-of-water/Pump/Priming-Enabled/text()",
201 final List<String> pumpProperty4 = thingHandler.evaluateXPath("//Body-of-water/Pump/Min-Pump-Speed/text()",
203 final List<String> pumpProperty5 = thingHandler.evaluateXPath("//Body-of-water/Pump/Max-Pump-Speed/text()",
205 final List<String> pumpProperty6 = thingHandler.evaluateXPath("//Body-of-water/Pump/Min-Pump-RPM/text()",
207 final List<String> pumpProperty7 = thingHandler.evaluateXPath("//Body-of-water/Pump/Max-Pump-RPM/text()",
209 final List<String> pumpProperty8 = thingHandler.evaluateXPath("//Body-of-water/Pump/Vsp-Low-Pump-Speed/text()",
211 final List<String> pumpProperty9 = thingHandler
212 .evaluateXPath("//Body-of-water/Pump/Vsp-Medium-Pump-Speed/text()", xmlResponse);
213 final List<String> pumpProperty10 = thingHandler
214 .evaluateXPath("//Body-of-water/Pump/Vsp-High-Pump-Speed/text()", xmlResponse);
215 final List<String> pumpProperty11 = thingHandler
216 .evaluateXPath("//Body-of-water/Pump/Vsp-Custom-Pump-Speed/text()", xmlResponse);
218 discoverDevices(thingHandler, xmlResponse, "Pump", HaywardTypeToRequest.PUMP,
219 HaywardBindingConstants.THING_TYPE_PUMP, (props, i) -> {
220 props.put(HaywardBindingConstants.PROPERTY_PUMP_TYPE, pumpProperty1.get(i));
221 props.put(HaywardBindingConstants.PROPERTY_PUMP_FUNCTION, pumpProperty2.get(i));
222 props.put(HaywardBindingConstants.PROPERTY_PUMP_PRIMINGENABLED, pumpProperty3.get(i));
223 props.put(HaywardBindingConstants.PROPERTY_PUMP_MINSPEED, pumpProperty4.get(i));
224 props.put(HaywardBindingConstants.PROPERTY_PUMP_MAXSPEED, pumpProperty5.get(i));
225 props.put(HaywardBindingConstants.PROPERTY_PUMP_MINRPM, pumpProperty6.get(i));
226 props.put(HaywardBindingConstants.PROPERTY_PUMP_MAXRPM, pumpProperty7.get(i));
227 props.put(HaywardBindingConstants.PROPERTY_PUMP_LOWSPEED, pumpProperty8.get(i));
228 props.put(HaywardBindingConstants.PROPERTY_PUMP_MEDSPEED, pumpProperty9.get(i));
229 props.put(HaywardBindingConstants.PROPERTY_PUMP_HIGHSPEED, pumpProperty10.get(i));
230 props.put(HaywardBindingConstants.PROPERTY_PUMP_CUSTOMSPEED, pumpProperty11.get(i));
234 final List<String> relayProperty1 = thingHandler.evaluateXPath("//Backyard//Relay/Type/text()", xmlResponse);
235 final List<String> relayProperty2 = thingHandler.evaluateXPath("//Backyard//Relay/Function/text()",
238 discoverDevices(thingHandler, xmlResponse, "Relay", HaywardTypeToRequest.RELAY,
239 HaywardBindingConstants.THING_TYPE_RELAY, (props, i) -> {
240 props.put(HaywardBindingConstants.PROPERTY_RELAY_TYPE, relayProperty1.get(i));
241 props.put(HaywardBindingConstants.PROPERTY_RELAY_FUNCTION, relayProperty2.get(i));
244 // Find Virtual Heaters
245 final List<String> virtualHeaterProperty1 = thingHandler
246 .evaluateXPath("//Body-of-water/Heater/Shared-Type/text()", xmlResponse);
247 final List<String> virtualHeaterProperty2 = thingHandler
248 .evaluateXPath("//Body-of-water/Heater/Min-Settable-Water-Temp/text()", xmlResponse);
249 final List<String> virtualHeaterProperty3 = thingHandler
250 .evaluateXPath("//Body-of-water/Heater/Max-Settable-Water-Temp/text()", xmlResponse);
251 final List<String> virtualHeaterProperty4 = thingHandler
252 .evaluateXPath("//Body-of-water/Heater/Max-Water-Temp/text()", xmlResponse);
254 discoverDevices(thingHandler, xmlResponse, "Heater", HaywardTypeToRequest.VIRTUALHEATER,
255 HaywardBindingConstants.THING_TYPE_VIRTUALHEATER, (props, i) -> {
256 props.put(HaywardBindingConstants.PROPERTY_VIRTUALHEATER_SHAREDTYPE, virtualHeaterProperty1.get(i));
257 props.put(HaywardBindingConstants.PROPERTY_VIRTUALHEATER_MINSETTABLEWATERTEMP,
258 virtualHeaterProperty2.get(i));
259 props.put(HaywardBindingConstants.PROPERTY_VIRTUALHEATER_MAXSETTABLEWATERTEMP,
260 virtualHeaterProperty3.get(i));
261 props.put(HaywardBindingConstants.PROPERTY_VIRTUALHEATER_MAXWATERTEMP,
262 virtualHeaterProperty4.get(i));
266 private void discoverDevices(HaywardBridgeHandler bridgehandler, String xmlResponse, String xmlSearchTerm,
267 HaywardTypeToRequest type, ThingTypeUID thingType,
268 @Nullable BiConsumer<Map<String, Object>, Integer> additionalPropertyConsumer) {
269 List<String> systemIDs = bridgehandler.evaluateXPath("//Backyard//" + xmlSearchTerm + "/System-Id/text()",
273 // Set Virtual Heater Name
274 if (HaywardBindingConstants.THING_TYPE_VIRTUALHEATER.equals(thingType)) {
275 names = new ArrayList<>(systemIDs);
276 Collections.fill(names, "Heater");
278 names = bridgehandler.evaluateXPath("//Backyard//" + xmlSearchTerm + "/Name/text()", xmlResponse);
281 for (int i = 0; i < systemIDs.size(); i++) {
282 // get Body of Water for each item
283 List<String> bowID = bridgehandler.evaluateXPath(
284 "//*[System-Id=" + systemIDs.get(i) + "]/ancestor::Body-of-water/System-Id/text()", xmlResponse);
285 List<String> bowName = bridgehandler.evaluateXPath(
286 "//*[System-Id=" + systemIDs.get(i) + "]/ancestor::Body-of-water/Name/text()", xmlResponse);
288 Map<String, Object> properties = new HashMap<>();
289 properties.put(HaywardBindingConstants.PROPERTY_TYPE, type);
290 properties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, systemIDs.get(i));
292 if (!bowID.isEmpty()) {
293 properties.put(HaywardBindingConstants.PROPERTY_BOWID, bowID.get(0));
295 // Set BOWID = 0 for backyard items
296 properties.put(HaywardBindingConstants.PROPERTY_BOWID, "0");
299 if (!bowName.isEmpty()) {
300 properties.put(HaywardBindingConstants.PROPERTY_BOWNAME, bowName.get(0));
302 // Set BOWNAME = Backyard for backyard items
303 properties.put(HaywardBindingConstants.PROPERTY_BOWNAME, "Backyard");
306 if (additionalPropertyConsumer != null) {
307 additionalPropertyConsumer.accept(properties, i);
310 onDeviceDiscovered(thingType, names.get(i), properties);
314 public void onDeviceDiscovered(ThingTypeUID thingType, String label, Map<String, Object> properties) {
315 HaywardBridgeHandler bridgehandler = thingHandler;
316 String systemID = (String) properties.get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
317 if (bridgehandler != null) {
318 if (systemID != null) {
319 ThingUID thingUID = new ThingUID(thingType, bridgehandler.getThing().getUID(), systemID);
320 DiscoveryResult result = DiscoveryResultBuilder.create(thingUID)
321 .withBridge(bridgehandler.getThing().getUID())
322 .withRepresentationProperty(HaywardBindingConstants.PROPERTY_SYSTEM_ID)
323 .withLabel("Hayward " + label).withProperties(properties).build();
324 thingDiscovered(result);