]> git.basschouten.com Git - openhab-addons.git/blob
3a2eb778ad092496f88327223f964f0bf91c7eb4
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.haywardomnilogic.internal.discovery;
14
15 import static org.openhab.binding.haywardomnilogic.internal.HaywardBindingConstants.THING_TYPES_UIDS;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.function.BiConsumer;
23
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.AbstractDiscoveryService;
31 import org.openhab.core.config.discovery.DiscoveryResult;
32 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
33 import org.openhab.core.config.discovery.DiscoveryService;
34 import org.openhab.core.thing.ThingTypeUID;
35 import org.openhab.core.thing.ThingUID;
36 import org.openhab.core.thing.binding.ThingHandler;
37 import org.openhab.core.thing.binding.ThingHandlerService;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * Sets up the discovery results and details
43  *
44  * @author Matt Myers - Initial contribution
45  */
46
47 @NonNullByDefault
48 public class HaywardDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService {
49     private final Logger logger = LoggerFactory.getLogger(HaywardDiscoveryService.class);
50     private @Nullable HaywardBridgeHandler discoveryBridgehandler;
51
52     public HaywardDiscoveryService() {
53         super(THING_TYPES_UIDS, 0, false);
54     }
55
56     @Override
57     public void activate() {
58         super.activate(null);
59     }
60
61     @Override
62     public void deactivate() {
63         super.deactivate();
64     }
65
66     @Override
67     protected void startScan() {
68         HaywardBridgeHandler bridgehandler = discoveryBridgehandler;
69         try {
70             if (bridgehandler != null) {
71                 String xmlResults = bridgehandler.getMspConfig();
72                 mspConfigDiscovery(xmlResults);
73             }
74         } catch (HaywardException e) {
75             logger.warn("Exception during discovery scan: {}", e.getMessage());
76         } catch (InterruptedException e) {
77             return;
78         }
79     }
80
81     public synchronized void mspConfigDiscovery(String xmlResponse) {
82         List<String> systemIDs = new ArrayList<>();
83         List<String> names = new ArrayList<>();
84         Map<String, Object> backyardProperties = new HashMap<>();
85         Map<String, Object> bowProperties = new HashMap<>();
86         HaywardBridgeHandler bridgehandler = discoveryBridgehandler;
87
88         if (bridgehandler == null) {
89             return;
90         }
91
92         // Find Backyard
93         names = bridgehandler.evaluateXPath("//Backyard/Name/text()", xmlResponse);
94
95         for (int i = 0; i < names.size(); i++) {
96             backyardProperties.put(HaywardBindingConstants.PROPERTY_TYPE, HaywardTypeToRequest.BACKYARD);
97             backyardProperties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, bridgehandler.account.mspSystemID);
98
99             onDeviceDiscovered(HaywardBindingConstants.THING_TYPE_BACKYARD, names.get(i), backyardProperties);
100         }
101
102         // Find Bodies of Water
103         systemIDs = bridgehandler.evaluateXPath("//Body-of-water/System-Id/text()", xmlResponse);
104         names = bridgehandler.evaluateXPath("//Body-of-water/Name/text()", xmlResponse);
105
106         final List<String> bowProperty1 = bridgehandler.evaluateXPath("//Body-of-water/Type/text()", xmlResponse);
107         final List<String> bowProperty2 = bridgehandler.evaluateXPath("//Body-of-water/Shared-Type/text()",
108                 xmlResponse);
109         final List<String> bowProperty3 = bridgehandler.evaluateXPath("//Body-of-water/Shared-Priority/text()",
110                 xmlResponse);
111         final List<String> bowProperty4 = bridgehandler
112                 .evaluateXPath("//Body-of-water/Shared-Equipment-System-ID/text()", xmlResponse);
113         final List<String> bowProperty5 = bridgehandler.evaluateXPath("//Body-of-water/Supports-Spillover/text()",
114                 xmlResponse);
115         final List<String> bowProperty6 = bridgehandler.evaluateXPath("//Body-of-water/Size-In-Gallons/text()",
116                 xmlResponse);
117
118         for (int i = 0; i < systemIDs.size(); i++) {
119             bowProperties.put(HaywardBindingConstants.PROPERTY_TYPE, HaywardTypeToRequest.BOW);
120             bowProperties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, systemIDs.get(i));
121             bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_TYPE, bowProperty1.get(i));
122             bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SHAREDTYPE, bowProperty2.get(i));
123             bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SHAREDPRIORITY, bowProperty3.get(i));
124             bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SHAREDEQUIPID, bowProperty4.get(i));
125             bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SUPPORTSSPILLOVER, bowProperty5.get(i));
126             bowProperties.put(HaywardBindingConstants.PROPERTY_BOW_SIZEINGALLONS, bowProperty6.get(i));
127
128             onDeviceDiscovered(HaywardBindingConstants.THING_TYPE_BOW, names.get(i), bowProperties);
129         }
130
131         // Find Chlorinators
132         final List<String> chlorinatorProperty1 = bridgehandler
133                 .evaluateXPath("//Body-of-water/Chlorinator/Shared-Type/text()", xmlResponse);
134         final List<String> chlorinatorProperty2 = bridgehandler.evaluateXPath("//Body-of-water/Chlorinator/Mode/text()",
135                 xmlResponse);
136         final List<String> chlorinatorProperty3 = bridgehandler
137                 .evaluateXPath("//Body-of-water/Chlorinator/Cell-Type/text()", xmlResponse);
138         final List<String> chlorinatorProperty4 = bridgehandler
139                 .evaluateXPath("//Body-of-water/Chlorinator/Dispenser-Type/text()", xmlResponse);
140
141         discoverDevices(bridgehandler, xmlResponse, "Chlorinator", HaywardTypeToRequest.CHLORINATOR,
142                 HaywardBindingConstants.THING_TYPE_CHLORINATOR, (props, i) -> {
143                     props.put(HaywardBindingConstants.PROPERTY_CHLORINATOR_SHAREDTYPE, chlorinatorProperty1.get(i));
144                     props.put(HaywardBindingConstants.PROPERTY_CHLORINATOR_MODE, chlorinatorProperty2.get(i));
145                     props.put(HaywardBindingConstants.PROPERTY_CHLORINATOR_CELLTYPE, chlorinatorProperty3.get(i));
146                     props.put(HaywardBindingConstants.PROPERTY_CHLORINATOR_DISPENSERTYPE, chlorinatorProperty4.get(i));
147                 });
148
149         // Find ColorLogic Lights
150         final List<String> colorLogicProperty1 = bridgehandler.evaluateXPath("//Backyard//ColorLogic-Light/Type/text()",
151                 xmlResponse);
152
153         discoverDevices(bridgehandler, xmlResponse, "ColorLogic-Light", HaywardTypeToRequest.COLORLOGIC,
154                 HaywardBindingConstants.THING_TYPE_COLORLOGIC, (props, i) -> {
155                     props.put(HaywardBindingConstants.PROPERTY_COLORLOGIC_TYPE, colorLogicProperty1.get(i));
156                 });
157
158         // Find Filters
159         final List<String> filterProperty1 = bridgehandler.evaluateXPath("//Body-of-water/Filter/Shared-Type/text()",
160                 xmlResponse);
161         final List<String> filterProperty2 = bridgehandler.evaluateXPath("//Body-of-water/Filter/Filter-Type/text()",
162                 xmlResponse);
163         final List<String> filterProperty3 = bridgehandler
164                 .evaluateXPath("//Body-of-water/Filter/Priming-Enabled/text()", xmlResponse);
165         final List<String> filterProperty4 = bridgehandler.evaluateXPath("//Body-of-water/Filter/Min-Pump-Speed/text()",
166                 xmlResponse);
167         final List<String> filterProperty5 = bridgehandler.evaluateXPath("//Body-of-water/Filter/Max-Pump-Speed/text()",
168                 xmlResponse);
169         final List<String> filterProperty6 = bridgehandler.evaluateXPath("//Body-of-water/Filter/Min-Pump-RPM/text()",
170                 xmlResponse);
171         final List<String> filterProperty7 = bridgehandler.evaluateXPath("//Body-of-water/Filter/Max-Pump-RPM/text()",
172                 xmlResponse);
173         final List<String> filterProperty8 = bridgehandler
174                 .evaluateXPath("//Body-of-water/Filter/Vsp-Low-Pump-Speed/text()", xmlResponse);
175         final List<String> filterProperty9 = bridgehandler
176                 .evaluateXPath("//Body-of-water/Filter/Vsp-Medium-Pump-Speed/text()", xmlResponse);
177         final List<String> filterProperty10 = bridgehandler
178                 .evaluateXPath("//Body-of-water/Filter/Vsp-High-Pump-Speed/text()", xmlResponse);
179         final List<String> filterProperty11 = bridgehandler
180                 .evaluateXPath("//Body-of-water/Filter/Vsp-Custom-Pump-Speed/text()", xmlResponse);
181         final List<String> filterProperty12 = bridgehandler
182                 .evaluateXPath("//Body-of-water/Filter/Freeze-Protect-Override-Interval/text()", xmlResponse);
183
184         discoverDevices(bridgehandler, xmlResponse, "Filter", HaywardTypeToRequest.FILTER,
185                 HaywardBindingConstants.THING_TYPE_FILTER, (props, i) -> {
186                     props.put(HaywardBindingConstants.PROPERTY_FILTER_SHAREDTYPE, filterProperty1.get(i));
187                     props.put(HaywardBindingConstants.PROPERTY_FILTER_FILTERTYPE, filterProperty2.get(i));
188                     props.put(HaywardBindingConstants.PROPERTY_FILTER_PRIMINGENABLED, filterProperty3.get(i));
189                     props.put(HaywardBindingConstants.PROPERTY_FILTER_MINSPEED, filterProperty4.get(i));
190                     props.put(HaywardBindingConstants.PROPERTY_FILTER_MAXSPEED, filterProperty5.get(i));
191                     props.put(HaywardBindingConstants.PROPERTY_FILTER_MINRPM, filterProperty6.get(i));
192                     props.put(HaywardBindingConstants.PROPERTY_FILTER_MAXRPM, filterProperty7.get(i));
193                     props.put(HaywardBindingConstants.PROPERTY_FILTER_LOWSPEED, filterProperty8.get(i));
194                     props.put(HaywardBindingConstants.PROPERTY_FILTER_MEDSPEED, filterProperty9.get(i));
195                     props.put(HaywardBindingConstants.PROPERTY_FILTER_HIGHSPEED, filterProperty10.get(i));
196                     props.put(HaywardBindingConstants.PROPERTY_FILTER_CUSTOMSPEED, filterProperty11.get(i));
197                     props.put(HaywardBindingConstants.PROPERTY_FILTER_FREEZEPROTECTOVERRIDEINTERVAL,
198                             filterProperty12.get(i));
199                 });
200
201         // Find Heaters
202         final List<String> heaterProperty1 = bridgehandler
203                 .evaluateXPath("//Body-of-water/Heater/Operation/Heater-Equipment/Type/text()", xmlResponse);
204         final List<String> heaterProperty2 = bridgehandler
205                 .evaluateXPath("//Body-of-water/Heater/Operation/Heater-Equipment/Heater-Type/text()", xmlResponse);
206         final List<String> heaterProperty3 = bridgehandler.evaluateXPath(
207                 "//Body-of-water/Heater/Operation/Heater-Equipment/Shared-Equipment-System-ID/text()", xmlResponse);
208
209         discoverDevices(bridgehandler, xmlResponse, "Heater-Equipment", HaywardTypeToRequest.HEATER,
210                 HaywardBindingConstants.THING_TYPE_HEATER, (props, i) -> {
211                     props.put(HaywardBindingConstants.PROPERTY_HEATER_TYPE, heaterProperty1.get(i));
212                     props.put(HaywardBindingConstants.PROPERTY_HEATER_HEATERTYPE, heaterProperty2.get(i));
213                     props.put(HaywardBindingConstants.PROPERTY_HEATER_SHAREDEQUIPID, heaterProperty3.get(i));
214                 });
215
216         // Find Pumps
217         final List<String> pumpProperty1 = bridgehandler.evaluateXPath("//Body-of-water/Pump/Type/text()", xmlResponse);
218         final List<String> pumpProperty2 = bridgehandler.evaluateXPath("//Body-of-water/Pump/Function/text()",
219                 xmlResponse);
220         final List<String> pumpProperty3 = bridgehandler.evaluateXPath("//Body-of-water/Pump/Priming-Enabled/text()",
221                 xmlResponse);
222         final List<String> pumpProperty4 = bridgehandler.evaluateXPath("//Body-of-water/Pump/Min-Pump-Speed/text()",
223                 xmlResponse);
224         final List<String> pumpProperty5 = bridgehandler.evaluateXPath("//Body-of-water/Pump/Max-Pump-Speed/text()",
225                 xmlResponse);
226         final List<String> pumpProperty6 = bridgehandler.evaluateXPath("//Body-of-water/Pump/Min-Pump-RPM/text()",
227                 xmlResponse);
228         final List<String> pumpProperty7 = bridgehandler.evaluateXPath("//Body-of-water/Pump/Max-Pump-RPM/text()",
229                 xmlResponse);
230         final List<String> pumpProperty8 = bridgehandler.evaluateXPath("//Body-of-water/Pump/Vsp-Low-Pump-Speed/text()",
231                 xmlResponse);
232         final List<String> pumpProperty9 = bridgehandler
233                 .evaluateXPath("//Body-of-water/Pump/Vsp-Medium-Pump-Speed/text()", xmlResponse);
234         final List<String> pumpProperty10 = bridgehandler
235                 .evaluateXPath("//Body-of-water/Pump/Vsp-High-Pump-Speed/text()", xmlResponse);
236         final List<String> pumpProperty11 = bridgehandler
237                 .evaluateXPath("//Body-of-water/Pump/Vsp-Custom-Pump-Speed/text()", xmlResponse);
238
239         discoverDevices(bridgehandler, xmlResponse, "Pump", HaywardTypeToRequest.PUMP,
240                 HaywardBindingConstants.THING_TYPE_PUMP, (props, i) -> {
241                     props.put(HaywardBindingConstants.PROPERTY_PUMP_TYPE, pumpProperty1.get(i));
242                     props.put(HaywardBindingConstants.PROPERTY_PUMP_FUNCTION, pumpProperty2.get(i));
243                     props.put(HaywardBindingConstants.PROPERTY_PUMP_PRIMINGENABLED, pumpProperty3.get(i));
244                     props.put(HaywardBindingConstants.PROPERTY_PUMP_MINSPEED, pumpProperty4.get(i));
245                     props.put(HaywardBindingConstants.PROPERTY_PUMP_MAXSPEED, pumpProperty5.get(i));
246                     props.put(HaywardBindingConstants.PROPERTY_PUMP_MINRPM, pumpProperty6.get(i));
247                     props.put(HaywardBindingConstants.PROPERTY_PUMP_MAXRPM, pumpProperty7.get(i));
248                     props.put(HaywardBindingConstants.PROPERTY_PUMP_LOWSPEED, pumpProperty8.get(i));
249                     props.put(HaywardBindingConstants.PROPERTY_PUMP_MEDSPEED, pumpProperty9.get(i));
250                     props.put(HaywardBindingConstants.PROPERTY_PUMP_HIGHSPEED, pumpProperty10.get(i));
251                     props.put(HaywardBindingConstants.PROPERTY_PUMP_CUSTOMSPEED, pumpProperty11.get(i));
252                 });
253
254         // Find Relays
255         final List<String> relayProperty1 = bridgehandler.evaluateXPath("//Backyard//Relay/Type/text()", xmlResponse);
256         final List<String> relayProperty2 = bridgehandler.evaluateXPath("//Backyard//Relay/Function/text()",
257                 xmlResponse);
258
259         discoverDevices(bridgehandler, xmlResponse, "Relay", HaywardTypeToRequest.RELAY,
260                 HaywardBindingConstants.THING_TYPE_RELAY, (props, i) -> {
261                     props.put(HaywardBindingConstants.PROPERTY_RELAY_TYPE, relayProperty1.get(i));
262                     props.put(HaywardBindingConstants.PROPERTY_RELAY_FUNCTION, relayProperty2.get(i));
263                 });
264
265         // Find Virtual Heaters
266         final List<String> virtualHeaterProperty1 = bridgehandler
267                 .evaluateXPath("//Body-of-water/Heater/Shared-Type/text()", xmlResponse);
268         final List<String> virtualHeaterProperty2 = bridgehandler
269                 .evaluateXPath("//Body-of-water/Heater/Min-Settable-Water-Temp/text()", xmlResponse);
270         final List<String> virtualHeaterProperty3 = bridgehandler
271                 .evaluateXPath("//Body-of-water/Heater/Max-Settable-Water-Temp/text()", xmlResponse);
272         final List<String> virtualHeaterProperty4 = bridgehandler
273                 .evaluateXPath("//Body-of-water/Heater/Max-Water-Temp/text()", xmlResponse);
274
275         discoverDevices(bridgehandler, xmlResponse, "Heater", HaywardTypeToRequest.VIRTUALHEATER,
276                 HaywardBindingConstants.THING_TYPE_VIRTUALHEATER, (props, i) -> {
277                     props.put(HaywardBindingConstants.PROPERTY_VIRTUALHEATER_SHAREDTYPE, virtualHeaterProperty1.get(i));
278                     props.put(HaywardBindingConstants.PROPERTY_VIRTUALHEATER_MINSETTABLEWATERTEMP,
279                             virtualHeaterProperty2.get(i));
280                     props.put(HaywardBindingConstants.PROPERTY_VIRTUALHEATER_MAXSETTABLEWATERTEMP,
281                             virtualHeaterProperty3.get(i));
282                     props.put(HaywardBindingConstants.PROPERTY_VIRTUALHEATER_MAXWATERTEMP,
283                             virtualHeaterProperty4.get(i));
284                 });
285     }
286
287     private void discoverDevices(HaywardBridgeHandler bridgehandler, String xmlResponse, String xmlSearchTerm,
288             HaywardTypeToRequest type, ThingTypeUID thingType,
289             @Nullable BiConsumer<Map<String, Object>, Integer> additionalPropertyConsumer) {
290         List<String> systemIDs = bridgehandler.evaluateXPath("//Backyard//" + xmlSearchTerm + "/System-Id/text()",
291                 xmlResponse);
292         List<String> names;
293
294         // Set Virtual Heater Name
295         if (HaywardBindingConstants.THING_TYPE_VIRTUALHEATER.equals(thingType)) {
296             names = new ArrayList<>(systemIDs);
297             Collections.fill(names, "Heater");
298         } else {
299             names = bridgehandler.evaluateXPath("//Backyard//" + xmlSearchTerm + "/Name/text()", xmlResponse);
300         }
301
302         for (int i = 0; i < systemIDs.size(); i++) {
303             // get Body of Water for each item
304             List<String> bowID = bridgehandler.evaluateXPath(
305                     "//*[System-Id=" + systemIDs.get(i) + "]/ancestor::Body-of-water/System-Id/text()", xmlResponse);
306             List<String> bowName = bridgehandler.evaluateXPath(
307                     "//*[System-Id=" + systemIDs.get(i) + "]/ancestor::Body-of-water/Name/text()", xmlResponse);
308
309             Map<String, Object> properties = new HashMap<>();
310             properties.put(HaywardBindingConstants.PROPERTY_TYPE, type);
311             properties.put(HaywardBindingConstants.PROPERTY_SYSTEM_ID, systemIDs.get(i));
312
313             if (!bowID.isEmpty()) {
314                 properties.put(HaywardBindingConstants.PROPERTY_BOWID, bowID.get(0));
315             } else {
316                 // Set BOWID = 0 for backyard items
317                 properties.put(HaywardBindingConstants.PROPERTY_BOWID, "0");
318             }
319
320             if (!bowName.isEmpty()) {
321                 properties.put(HaywardBindingConstants.PROPERTY_BOWNAME, bowName.get(0));
322             } else {
323                 // Set BOWNAME = Backyard for backyard items
324                 properties.put(HaywardBindingConstants.PROPERTY_BOWNAME, "Backyard");
325             }
326
327             if (additionalPropertyConsumer != null) {
328                 additionalPropertyConsumer.accept(properties, i);
329             }
330
331             onDeviceDiscovered(thingType, names.get(i), properties);
332         }
333     }
334
335     public void onDeviceDiscovered(ThingTypeUID thingType, String label, Map<String, Object> properties) {
336         HaywardBridgeHandler bridgehandler = discoveryBridgehandler;
337         String systemID = (String) properties.get(HaywardBindingConstants.PROPERTY_SYSTEM_ID);
338         if (bridgehandler != null) {
339             if (systemID != null) {
340                 ThingUID thingUID = new ThingUID(thingType, bridgehandler.getThing().getUID(), systemID);
341                 DiscoveryResult result = DiscoveryResultBuilder.create(thingUID)
342                         .withBridge(bridgehandler.getThing().getUID())
343                         .withRepresentationProperty(HaywardBindingConstants.PROPERTY_SYSTEM_ID)
344                         .withLabel("Hayward " + label).withProperties(properties).build();
345                 thingDiscovered(result);
346             }
347         }
348     }
349
350     @Override
351     public void setThingHandler(@Nullable ThingHandler handler) {
352         if (handler instanceof HaywardBridgeHandler bridgeHandler) {
353             this.discoveryBridgehandler = bridgeHandler;
354         }
355     }
356
357     @Override
358     public @Nullable ThingHandler getThingHandler() {
359         return discoveryBridgehandler;
360     }
361 }