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