]> git.basschouten.com Git - openhab-addons.git/blob
1fef8ec2433412c07026d7f2c8541ab67a6de0b3
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.ihc.internal.ws.services;
14
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Set;
19
20 import javax.xml.xpath.XPathExpressionException;
21
22 import org.openhab.binding.ihc.internal.ws.datatypes.XPathUtils;
23 import org.openhab.binding.ihc.internal.ws.exeptions.IhcExecption;
24 import org.openhab.binding.ihc.internal.ws.http.IhcConnectionPool;
25 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSBooleanValue;
26 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSDateValue;
27 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSEnumValue;
28 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSFloatingPointValue;
29 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSIntegerValue;
30 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSResourceValue;
31 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSTimeValue;
32 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSTimerValue;
33 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSWeekdayValue;
34 import org.w3c.dom.Node;
35 import org.w3c.dom.NodeList;
36
37 /**
38  * Class to handle IHC / ELKO LS Controller's resource interaction service.
39  *
40  * Service is used to fetch or update resource values from/to controller.
41  *
42  * @author Pauli Anttila - Initial contribution
43  */
44 public class IhcResourceInteractionService extends IhcBaseService {
45
46     public IhcResourceInteractionService(String host, int timeout, IhcConnectionPool ihcConnectionPool) {
47         super(ihcConnectionPool, timeout, host, "ResourceInteractionService");
48     }
49
50     /**
51      * Query resource value from controller.
52      *
53      * @param resoureId Resource Identifier.
54      * @return Resource value.
55      */
56     public WSResourceValue resourceQuery(int resoureId) throws IhcExecption {
57         // @formatter:off
58         final String soapQuery =
59                   """
60                 <?xml version="1.0" encoding="UTF-8"?>
61                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
62                  <soapenv:Body>
63                   <ns1:getRuntimeValue1 xmlns:ns1="utcs">%s</ns1:getRuntimeValue1>
64                  </soapenv:Body>
65                 </soapenv:Envelope>\
66                 """;
67         // @formatter:on
68
69         String query = String.format(soapQuery, String.valueOf(resoureId));
70         String response = sendSoapQuery(null, query);
71         NodeList nodeList;
72         try {
73             nodeList = XPathUtils.parseList(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:getRuntimeValue2");
74
75             if (nodeList != null && nodeList.getLength() == 1) {
76                 WSResourceValue val = parseResourceValue(nodeList.item(0));
77
78                 if (val != null && val.resourceID == resoureId) {
79                     return val;
80                 } else {
81                     throw new IhcExecption("No resource id found");
82                 }
83             } else {
84                 throw new IhcExecption("No resource value found");
85             }
86         } catch (XPathExpressionException | NumberFormatException | IOException e) {
87             throw new IhcExecption("Error occured during XML data parsing", e);
88         }
89     }
90
91     private WSResourceValue parseResourceValue(Node n) throws XPathExpressionException, NumberFormatException {
92         // parse resource id
93         String resourceId = XPathUtils.getSpeficValueFromNode(n, "ns1:resourceID");
94
95         if (resourceId != null && !resourceId.isBlank()) {
96             int id = Integer.parseInt(resourceId);
97
98             // Parse floating point value
99             String floatingPointValue = getValue(n, "floatingPointValue");
100             if (floatingPointValue != null && !floatingPointValue.isBlank()) {
101                 String min = getValue(n, "minimumValue");
102                 String max = getValue(n, "maximumValue");
103                 return new WSFloatingPointValue(id, Double.valueOf(floatingPointValue), Double.valueOf(min),
104                         Double.valueOf(max));
105             }
106
107             // Parse boolean value
108             String value = getValue(n, "value");
109             if (value != null && !value.isBlank()) {
110                 return new WSBooleanValue(id, Boolean.valueOf(value));
111             }
112
113             // Parse integer value
114             String integer = getValue(n, "integer");
115             if (integer != null && !integer.isBlank()) {
116                 String min = getValue(n, "minimumValue");
117                 String max = getValue(n, "maximumValue");
118                 return new WSIntegerValue(id, Integer.valueOf(integer), Integer.valueOf(min), Integer.valueOf(max));
119             }
120
121             // Parse timer value
122             String milliseconds = getValue(n, "milliseconds");
123             if (milliseconds != null && !milliseconds.isBlank()) {
124                 return new WSTimerValue(id, Integer.valueOf(milliseconds));
125             }
126
127             // Parse time value
128             String hours = getValue(n, "hours");
129             if (hours != null && !hours.isBlank()) {
130                 String minutes = getValue(n, "minutes");
131                 String seconds = getValue(n, "seconds");
132                 return new WSTimeValue(id, Integer.valueOf(hours), Integer.valueOf(minutes), Integer.valueOf(seconds));
133             }
134
135             // Parse date value
136             String year = getValue(n, "year");
137             if (year != null && !year.isBlank()) {
138                 String month = getValue(n, "month");
139                 String day = getValue(n, "day");
140                 return new WSDateValue(id, Short.valueOf(year), Byte.valueOf(month), Byte.valueOf(day));
141             }
142
143             // Parse enum value
144             String definitionTypeID = getValue(n, "definitionTypeID");
145             if (definitionTypeID != null && !definitionTypeID.isBlank()) {
146                 String enumValueID = getValue(n, "enumValueID");
147                 String enumName = getValue(n, "enumName");
148                 return new WSEnumValue(id, Integer.valueOf(definitionTypeID), Integer.valueOf(enumValueID), enumName);
149             }
150
151             // Parse week day value
152             value = getValue(n, "weekdayNumber");
153             if (value != null && !value.isBlank()) {
154                 return new WSWeekdayValue(id, Integer.valueOf(value));
155             }
156
157             // Unknown value type
158             throw new IllegalArgumentException("Unsupported value type");
159         }
160         return null;
161     }
162
163     private String getValue(Node n, String value) throws XPathExpressionException {
164         return XPathUtils.getSpeficValueFromNode(n, "ns1:value/" + XPathUtils.createIgnoreNameSpaceSyntaxExpr(value));
165     }
166
167     /**
168      * Update resource value to controller.
169      *
170      *
171      * @param value Resource value.
172      * @return True if value is successfully updated.
173      */
174     public boolean resourceUpdate(WSResourceValue value) throws IhcExecption {
175         boolean retval = false;
176
177         if (value instanceof WSFloatingPointValue pointValue) {
178             retval = resourceUpdate(pointValue);
179         } else if (value instanceof WSBooleanValue booleanValue) {
180             retval = resourceUpdate(booleanValue);
181         } else if (value instanceof WSIntegerValue integerValue) {
182             retval = resourceUpdate(integerValue);
183         } else if (value instanceof WSTimerValue timerValue) {
184             retval = resourceUpdate(timerValue);
185         } else if (value instanceof WSWeekdayValue weekdayValue) {
186             retval = resourceUpdate(weekdayValue);
187         } else if (value instanceof WSEnumValue enumValue) {
188             retval = resourceUpdate(enumValue);
189         } else if (value instanceof WSTimeValue timeValue) {
190             retval = resourceUpdate(timeValue);
191         } else if (value instanceof WSDateValue dateValue) {
192             retval = resourceUpdate(dateValue);
193         } else {
194             throw new IhcExecption("Unsupported value type " + value.getClass().toString());
195         }
196
197         return retval;
198     }
199
200     public boolean resourceUpdate(WSBooleanValue value) throws IhcExecption {
201         // @formatter:off
202         final String soapQuery =
203                   """
204                 <?xml version="1.0" encoding="UTF-8"?>
205                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
206                  <soap:Body>
207                   <setResourceValue1 xmlns="utcs">
208                    <value xmlns:q1="utcs.values" xsi:type="q1:WSBooleanValue">
209                     <q1:value>%s</q1:value>
210                    </value>
211                    <resourceID>%s</resourceID>
212                    <isValueRuntime>true</isValueRuntime>
213                   </setResourceValue1>
214                  </soap:Body>
215                 </soap:Envelope>\
216                 """;
217         // @formatter:on
218
219         String query = String.format(soapQuery, value.value ? "true" : "false", value.resourceID);
220         return doResourceUpdate(query);
221     }
222
223     public boolean resourceUpdate(WSFloatingPointValue value) throws IhcExecption {
224         // @formatter:off
225         final String soapQuery =
226                   """
227                 <?xml version="1.0" encoding="UTF-8"?>
228                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
229                  <soap:Body>
230                   <setResourceValue1 xmlns="utcs">
231                    <value xmlns:q1="utcs.values" xsi:type="q1:WSFloatingPointValue">
232                     <q1:maximumValue>%s</q1:maximumValue>
233                     <q1:minimumValue>%s</q1:minimumValue>
234                     <q1:floatingPointValue>%s</q1:floatingPointValue>
235                    </value>
236                    <resourceID>%s</resourceID>
237                    <isValueRuntime>true</isValueRuntime>
238                   </setResourceValue1>
239                  </soap:Body>
240                 </soap:Envelope>\
241                 """;
242         // @formatter:on
243
244         String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
245         return doResourceUpdate(query);
246     }
247
248     public boolean resourceUpdate(WSIntegerValue value) throws IhcExecption {
249         // @formatter:off
250         final String soapQuery =
251                   """
252                 <?xml version="1.0" encoding="UTF-8"?>
253                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
254                  <soap:Body>
255                   <setResourceValue1 xmlns="utcs">
256                    <value xmlns:q1="utcs.values" xsi:type="q1:WSIntegerValue">
257                     <q1:maximumValue>%s</q1:maximumValue>
258                     <q1:minimumValue>%s</q1:minimumValue>
259                     <q1:integer>%s</q1:integer>
260                    </value>
261                    <resourceID>%s</resourceID>
262                    <isValueRuntime>true</isValueRuntime>
263                   </setResourceValue1>
264                  </soap:Body>
265                 </soap:Envelope>\
266                 """;
267         // @formatter:on
268
269         String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
270         return doResourceUpdate(query);
271     }
272
273     public boolean resourceUpdate(WSTimerValue value) throws IhcExecption {
274         // @formatter:off
275         final String soapQuery =
276                   """
277                 <?xml version="1.0" encoding="UTF-8"?>
278                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
279                  <soap:Body>
280                   <setResourceValue1 xmlns="utcs">
281                    <value xmlns:q1="utcs.values" xsi:type="q1:WSTimerValue">
282                     <q1:milliseconds>%s</q1:milliseconds>
283                    </value>
284                    <resourceID>%s</resourceID>
285                    <isValueRuntime>true</isValueRuntime>
286                   </setResourceValue1>
287                  </soap:Body>
288                 </soap:Envelope>\
289                 """;
290         // @formatter:on
291
292         String query = String.format(soapQuery, value.milliseconds, value.resourceID);
293         return doResourceUpdate(query);
294     }
295
296     public boolean resourceUpdate(WSWeekdayValue value) throws IhcExecption {
297         // @formatter:off
298         final String soapQuery =
299                   """
300                 <?xml version="1.0" encoding="UTF-8"?>
301                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
302                  <soap:Body>
303                   <setResourceValue1 xmlns="utcs">
304                    <value xmlns:q1="utcs.values" xsi:type="q1:WSWeekdayValue">
305                     <q1:weekdayNumber>%s</q1:weekdayNumber>
306                    </value>
307                    <resourceID>%s</resourceID>
308                    <isValueRuntime>true</isValueRuntime>
309                   </setResourceValue1>
310                  </soap:Body>
311                 </soap:Envelope>\
312                 """;
313         // @formatter:on
314
315         String query = String.format(soapQuery, value.weekdayNumber, value.resourceID);
316         return doResourceUpdate(query);
317     }
318
319     public boolean resourceUpdate(WSEnumValue value) throws IhcExecption {
320         // @formatter:off
321         final String soapQuery =
322                   """
323                 <?xml version="1.0" encoding="UTF-8"?>
324                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
325                  <soap:Body>
326                   <setResourceValue1 xmlns="utcs">
327                    <value xmlns:q1="utcs.values" xsi:type="q1:WSEnumValue">
328                     <q1:definitionTypeID>%s</q1:definitionTypeID>
329                     <q1:enumValueID>%s</q1:enumValueID>
330                     <q1:enumName>%s</q1:enumName>
331                    </value>
332                    <resourceID>%s</resourceID>
333                    <isValueRuntime>true</isValueRuntime>
334                   </setResourceValue1>
335                  </soap:Body>
336                 </soap:Envelope>\
337                 """;
338         // @formatter:on
339
340         String query = String.format(soapQuery, value.definitionTypeID, value.enumValueID, value.enumName,
341                 value.resourceID);
342         return doResourceUpdate(query);
343     }
344
345     public boolean resourceUpdate(WSTimeValue value) throws IhcExecption {
346         // @formatter:off
347         final String soapQuery =
348                   """
349                 <?xml version="1.0" encoding="UTF-8"?>
350                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
351                  <soap:Body>
352                   <setResourceValue1 xmlns="utcs">
353                    <value xmlns:q1="utcs.values" xsi:type="q1:WSTimeValue">
354                     <q1:hours>%s</q1:hours>
355                     <q1:minutes>%s</q1:minutes>
356                     <q1:seconds>%s</q1:seconds>
357                    </value>
358                    <resourceID>%s</resourceID>
359                    <isValueRuntime>true</isValueRuntime>
360                   </setResourceValue1>
361                  </soap:Body>
362                 </soap:Envelope>\
363                 """;
364         // @formatter:on
365
366         String query = String.format(soapQuery, value.hours, value.minutes, value.seconds, value.resourceID);
367         return doResourceUpdate(query);
368     }
369
370     public boolean resourceUpdate(WSDateValue value) throws IhcExecption {
371         // @formatter:off
372         final String soapQuery =
373                   """
374                 <?xml version="1.0" encoding="UTF-8"?>
375                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
376                  <soap:Body>
377                   <setResourceValue1 xmlns="utcs">
378                    <value xmlns:q1="utcs.values" xsi:type="q1:WSDateValue">
379                     <q1:month>%s</q1:month>
380                     <q1:year>%s</q1:year>
381                     <q1:day>%s</q1:day>
382                    </value>
383                    <resourceID>%s</resourceID>
384                    <isValueRuntime>true</isValueRuntime>
385                   </setResourceValue1>
386                  </soap:Body>
387                 </soap:Envelope>\
388                 """;
389         // @formatter:on
390
391         String query = String.format(soapQuery, value.month, value.year, value.day, value.resourceID);
392         return doResourceUpdate(query);
393     }
394
395     private boolean doResourceUpdate(String query) throws IhcExecption {
396         String response = sendSoapQuery(null, query);
397         try {
398             return Boolean.parseBoolean(
399                     XPathUtils.parseXMLValue(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:setResourceValue2"));
400         } catch (IOException | XPathExpressionException e) {
401             throw new IhcExecption(e);
402         }
403     }
404
405     /**
406      * Enable resources runtime value notifications.
407      *
408      * @param resourceIdList List of resource Identifiers.
409      */
410     public void enableRuntimeValueNotifications(Set<Integer> resourceIdList) throws IhcExecption {
411         // @formatter:off
412         final String soapQueryPrefix =
413                   """
414                 <?xml version="1.0" encoding="UTF-8"?>
415                 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
416                  <soap:Body>
417                   <enableRuntimeValueNotifications1 xmlns="utcs">
418                 """;
419
420         final String soapQuerySuffix =
421                   """
422                   </enableRuntimeValueNotifications1>
423                  </soap:Body>
424                 </soap:Envelope>\
425                 """;
426         // @formatter:on
427
428         String query = soapQueryPrefix;
429         for (int i : resourceIdList) {
430             query += "   <xsd:arrayItem>" + i + "</xsd:arrayItem>\n";
431         }
432         query += soapQuerySuffix;
433         sendSoapQuery(null, query);
434     }
435
436     /**
437      * Wait runtime value notifications.
438      *
439      * Runtime value notification should firstly be activated by
440      * enableRuntimeValueNotifications function.
441      *
442      * @param timeoutInSeconds How many seconds to wait notifications.
443      * @return List of received runtime value notifications.
444      * @throws IhcExecption
445      */
446     public List<WSResourceValue> waitResourceValueNotifications(int timeoutInSeconds) throws IhcExecption {
447         // @formatter:off
448         final String soapQuery =
449                   """
450                 <?xml version="1.0" encoding="UTF-8"?>
451                 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:utcs="utcs">
452                  <soapenv:Body>
453                   <utcs:waitForResourceValueChanges1>%s</utcs:waitForResourceValueChanges1>
454                  </soapenv:Body>
455                 </soapenv:Envelope>\
456                 """;
457         // @formatter:on
458
459         String query = String.format(soapQuery, timeoutInSeconds);
460         String response = sendSoapQuery(null, query, getTimeout() + timeoutInSeconds * 1000);
461         List<WSResourceValue> resourceValueList = new ArrayList<>();
462
463         try {
464             NodeList nodeList = XPathUtils.parseList(response,
465                     "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:waitForResourceValueChanges2/ns1:arrayItem");
466
467             if (nodeList != null) {
468                 if (nodeList.getLength() == 1) {
469                     String resourceId = XPathUtils.getSpeficValueFromNode(nodeList.item(0), "ns1:resourceID");
470                     if (resourceId == null || resourceId.isEmpty()) {
471                         // IHC controller indicates timeout, return empty list
472                         return resourceValueList;
473                     }
474                 }
475
476                 for (int i = 0; i < nodeList.getLength(); i++) {
477                     WSResourceValue newVal = parseResourceValue(nodeList.item(i));
478                     if (newVal != null) {
479                         resourceValueList.add(newVal);
480                     }
481                 }
482             } else {
483                 throw new IhcExecption("Illegal resource value notification response received");
484             }
485             return resourceValueList;
486         } catch (XPathExpressionException | NumberFormatException | IOException e) {
487             throw new IhcExecption("Error occured during XML data parsing", e);
488         }
489     }
490 }