2 * Copyright (c) 2010-2021 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.ihc.internal.ws.services;
15 import java.io.IOException;
16 import java.net.SocketTimeoutException;
17 import java.util.ArrayList;
18 import java.util.List;
21 import javax.xml.xpath.XPathExpressionException;
23 import org.apache.commons.lang.StringUtils;
24 import org.openhab.binding.ihc.internal.ws.datatypes.XPathUtils;
25 import org.openhab.binding.ihc.internal.ws.exeptions.IhcExecption;
26 import org.openhab.binding.ihc.internal.ws.http.IhcConnectionPool;
27 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSBooleanValue;
28 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSDateValue;
29 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSEnumValue;
30 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSFloatingPointValue;
31 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSIntegerValue;
32 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSResourceValue;
33 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSTimeValue;
34 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSTimerValue;
35 import org.openhab.binding.ihc.internal.ws.resourcevalues.WSWeekdayValue;
36 import org.w3c.dom.Node;
37 import org.w3c.dom.NodeList;
40 * Class to handle IHC / ELKO LS Controller's resource interaction service.
42 * Service is used to fetch or update resource values from/to controller.
44 * @author Pauli Anttila - Initial contribution
46 public class IhcResourceInteractionService extends IhcBaseService {
48 public IhcResourceInteractionService(String host, int timeout, IhcConnectionPool ihcConnectionPool) {
49 super(ihcConnectionPool, timeout, host, "ResourceInteractionService");
53 * Query resource value from controller.
55 * @param resoureId Resource Identifier.
56 * @return Resource value.
58 public WSResourceValue resourceQuery(int resoureId) throws IhcExecption {
60 final String soapQuery =
61 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
62 + "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
64 + " <ns1:getRuntimeValue1 xmlns:ns1=\"utcs\">%s</ns1:getRuntimeValue1>\n"
65 + " </soapenv:Body>\n"
66 + "</soapenv:Envelope>";
69 String query = String.format(soapQuery, String.valueOf(resoureId));
70 String response = sendSoapQuery(null, query);
73 nodeList = XPathUtils.parseList(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:getRuntimeValue2");
75 if (nodeList != null && nodeList.getLength() == 1) {
76 WSResourceValue val = parseResourceValue(nodeList.item(0));
78 if (val != null && val.resourceID == resoureId) {
81 throw new IhcExecption("No resource id found");
84 throw new IhcExecption("No resource value found");
86 } catch (XPathExpressionException | NumberFormatException | IOException e) {
87 throw new IhcExecption("Error occured during XML data parsing", e);
91 private WSResourceValue parseResourceValue(Node n) throws XPathExpressionException, NumberFormatException {
93 String resourceId = XPathUtils.getSpeficValueFromNode(n, "ns1:resourceID");
95 if (StringUtils.isNotBlank(resourceId)) {
96 int id = Integer.parseInt(resourceId);
98 // Parse floating point value
99 String floatingPointValue = getValue(n, "floatingPointValue");
100 if (StringUtils.isNotBlank(floatingPointValue)) {
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));
107 // Parse boolean value
108 String value = getValue(n, "value");
109 if (StringUtils.isNotBlank(value)) {
110 return new WSBooleanValue(id, Boolean.valueOf(value));
113 // Parse integer value
114 String integer = getValue(n, "integer");
115 if (StringUtils.isNotBlank(integer)) {
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));
122 String milliseconds = getValue(n, "milliseconds");
123 if (StringUtils.isNotBlank(milliseconds)) {
124 return new WSTimerValue(id, Integer.valueOf(milliseconds));
128 String hours = getValue(n, "hours");
129 if (StringUtils.isNotBlank(hours)) {
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));
136 String year = getValue(n, "year");
137 if (StringUtils.isNotBlank(year)) {
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));
144 String definitionTypeID = getValue(n, "definitionTypeID");
145 if (StringUtils.isNotBlank(definitionTypeID)) {
146 String enumValueID = getValue(n, "enumValueID");
147 String enumName = getValue(n, "enumName");
148 return new WSEnumValue(id, Integer.valueOf(definitionTypeID), Integer.valueOf(enumValueID), enumName);
151 // Parse week day value
152 value = getValue(n, "weekdayNumber");
153 if (StringUtils.isNotBlank(value)) {
154 return new WSWeekdayValue(id, Integer.valueOf(value));
157 // Unknown value type
158 throw new IllegalArgumentException("Unsupported value type");
163 private String getValue(Node n, String value) throws XPathExpressionException {
164 return XPathUtils.getSpeficValueFromNode(n, "ns1:value/" + XPathUtils.createIgnoreNameSpaceSyntaxExpr(value));
168 * Update resource value to controller.
171 * @param value Resource value.
172 * @return True if value is successfully updated.
174 public boolean resourceUpdate(WSResourceValue value) throws IhcExecption {
175 boolean retval = false;
177 if (value instanceof WSFloatingPointValue) {
178 retval = resourceUpdate((WSFloatingPointValue) value);
179 } else if (value instanceof WSBooleanValue) {
180 retval = resourceUpdate((WSBooleanValue) value);
181 } else if (value instanceof WSIntegerValue) {
182 retval = resourceUpdate((WSIntegerValue) value);
183 } else if (value instanceof WSTimerValue) {
184 retval = resourceUpdate((WSTimerValue) value);
185 } else if (value instanceof WSWeekdayValue) {
186 retval = resourceUpdate((WSWeekdayValue) value);
187 } else if (value instanceof WSEnumValue) {
188 retval = resourceUpdate((WSEnumValue) value);
189 } else if (value instanceof WSTimeValue) {
190 retval = resourceUpdate((WSTimeValue) value);
191 } else if (value instanceof WSDateValue) {
192 retval = resourceUpdate((WSDateValue) value);
194 throw new IhcExecption("Unsupported value type " + value.getClass().toString());
200 public boolean resourceUpdate(WSBooleanValue value) throws IhcExecption {
202 final String soapQuery =
203 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
204 + "<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"
206 + " <setResourceValue1 xmlns=\"utcs\">\n"
207 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSBooleanValue\">\n"
208 + " <q1:value>%s</q1:value>\n"
210 + " <resourceID>%s</resourceID>\n"
211 + " <isValueRuntime>true</isValueRuntime>\n"
212 + " </setResourceValue1>\n"
214 + "</soap:Envelope>";
217 String query = String.format(soapQuery, value.value ? "true" : "false", value.resourceID);
218 return doResourceUpdate(query);
221 public boolean resourceUpdate(WSFloatingPointValue value) throws IhcExecption {
223 final String soapQuery =
224 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
225 + "<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"
227 + " <setResourceValue1 xmlns=\"utcs\">\n"
228 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSFloatingPointValue\">\n"
229 + " <q1:maximumValue>%s</q1:maximumValue>\n"
230 + " <q1:minimumValue>%s</q1:minimumValue>\n"
231 + " <q1:floatingPointValue>%s</q1:floatingPointValue>\n"
233 + " <resourceID>%s</resourceID>\n"
234 + " <isValueRuntime>true</isValueRuntime>\n"
235 + " </setResourceValue1>\n"
237 + "</soap:Envelope>";
240 String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
241 return doResourceUpdate(query);
244 public boolean resourceUpdate(WSIntegerValue value) throws IhcExecption {
246 final String soapQuery =
247 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
248 + "<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"
250 + " <setResourceValue1 xmlns=\"utcs\">\n"
251 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSIntegerValue\">\n"
252 + " <q1:maximumValue>%s</q1:maximumValue>\n"
253 + " <q1:minimumValue>%s</q1:minimumValue>\n"
254 + " <q1:integer>%s</q1:integer>\n"
256 + " <resourceID>%s</resourceID>\n"
257 + " <isValueRuntime>true</isValueRuntime>\n"
258 + " </setResourceValue1>\n"
260 + "</soap:Envelope>";
263 String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
264 return doResourceUpdate(query);
267 public boolean resourceUpdate(WSTimerValue value) throws IhcExecption {
269 final String soapQuery =
270 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
271 + "<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"
273 + " <setResourceValue1 xmlns=\"utcs\">\n"
274 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSTimerValue\">\n"
275 + " <q1:milliseconds>%s</q1:milliseconds>\n"
277 + " <resourceID>%s</resourceID>\n"
278 + " <isValueRuntime>true</isValueRuntime>\n"
279 + " </setResourceValue1>\n"
281 + "</soap:Envelope>";
284 String query = String.format(soapQuery, value.milliseconds, value.resourceID);
285 return doResourceUpdate(query);
288 public boolean resourceUpdate(WSWeekdayValue value) throws IhcExecption {
290 final String soapQuery =
291 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
292 + "<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"
294 + " <setResourceValue1 xmlns=\"utcs\">\n"
295 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSWeekdayValue\">\n"
296 + " <q1:weekdayNumber>%s</q1:weekdayNumber>\n"
298 + " <resourceID>%s</resourceID>\n"
299 + " <isValueRuntime>true</isValueRuntime>\n"
300 + " </setResourceValue1>\n"
302 + "</soap:Envelope>";
305 String query = String.format(soapQuery, value.weekdayNumber, value.resourceID);
306 return doResourceUpdate(query);
309 public boolean resourceUpdate(WSEnumValue value) throws IhcExecption {
311 final String soapQuery =
312 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
313 + "<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"
315 + " <setResourceValue1 xmlns=\"utcs\">\n"
316 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSEnumValue\">\n"
317 + " <q1:definitionTypeID>%s</q1:definitionTypeID>\n"
318 + " <q1:enumValueID>%s</q1:enumValueID>\n"
319 + " <q1:enumName>%s</q1:enumName>\n"
321 + " <resourceID>%s</resourceID>\n"
322 + " <isValueRuntime>true</isValueRuntime>\n"
323 + " </setResourceValue1>\n"
325 + "</soap:Envelope>";
328 String query = String.format(soapQuery, value.definitionTypeID, value.enumValueID, value.enumName,
330 return doResourceUpdate(query);
333 public boolean resourceUpdate(WSTimeValue value) throws IhcExecption {
335 final String soapQuery =
336 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
337 + "<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"
339 + " <setResourceValue1 xmlns=\"utcs\">\n"
340 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSTimeValue\">\n"
341 + " <q1:hours>%s</q1:hours>\n"
342 + " <q1:minutes>%s</q1:minutes>\n"
343 + " <q1:seconds>%s</q1:seconds>\n"
345 + " <resourceID>%s</resourceID>\n"
346 + " <isValueRuntime>true</isValueRuntime>\n"
347 + " </setResourceValue1>\n"
349 + "</soap:Envelope>";
352 String query = String.format(soapQuery, value.hours, value.minutes, value.seconds, value.resourceID);
353 return doResourceUpdate(query);
356 public boolean resourceUpdate(WSDateValue value) throws IhcExecption {
358 final String soapQuery =
359 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
360 + "<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"
362 + " <setResourceValue1 xmlns=\"utcs\">\n"
363 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSDateValue\">\n"
364 + " <q1:month>%s</q1:month>\n"
365 + " <q1:year>%s</q1:year>\n"
366 + " <q1:day>%s</q1:day>\n"
368 + " <resourceID>%s</resourceID>\n"
369 + " <isValueRuntime>true</isValueRuntime>\n"
370 + " </setResourceValue1>\n"
372 + "</soap:Envelope>";
375 String query = String.format(soapQuery, value.month, value.year, value.day, value.resourceID);
376 return doResourceUpdate(query);
379 private boolean doResourceUpdate(String query) throws IhcExecption {
380 String response = sendSoapQuery(null, query);
382 return Boolean.parseBoolean(
383 XPathUtils.parseXMLValue(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:setResourceValue2"));
384 } catch (IOException | XPathExpressionException e) {
385 throw new IhcExecption(e);
390 * Enable resources runtime value notifications.
392 * @param resourceIdList List of resource Identifiers.
393 * @return True is connection successfully opened.
395 public void enableRuntimeValueNotifications(Set<Integer> resourceIdList) throws IhcExecption {
397 final String soapQueryPrefix =
398 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
399 + "<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"
401 + " <enableRuntimeValueNotifications1 xmlns=\"utcs\">\n";
403 final String soapQuerySuffix =
404 " </enableRuntimeValueNotifications1>\n"
406 + "</soap:Envelope>";
409 String query = soapQueryPrefix;
410 for (int i : resourceIdList) {
411 query += " <xsd:arrayItem>" + i + "</xsd:arrayItem>\n";
413 query += soapQuerySuffix;
414 sendSoapQuery(null, query);
418 * Wait runtime value notifications.
420 * Runtime value notification should firstly be activated by
421 * enableRuntimeValueNotifications function.
423 * @param timeoutInSeconds How many seconds to wait notifications.
424 * @return List of received runtime value notifications.
425 * @throws SocketTimeoutException
426 * @throws IhcTimeoutExecption
428 public List<WSResourceValue> waitResourceValueNotifications(int timeoutInSeconds) throws IhcExecption {
430 final String soapQuery =
431 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
432 + "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:utcs=\"utcs\">\n"
433 + " <soapenv:Body>\n"
434 + " <utcs:waitForResourceValueChanges1>%s</utcs:waitForResourceValueChanges1>\n"
435 + " </soapenv:Body>\n"
436 + "</soapenv:Envelope>";
439 String query = String.format(soapQuery, timeoutInSeconds);
440 String response = sendSoapQuery(null, query, getTimeout() + timeoutInSeconds * 1000);
441 List<WSResourceValue> resourceValueList = new ArrayList<>();
444 NodeList nodeList = XPathUtils.parseList(response,
445 "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:waitForResourceValueChanges2/ns1:arrayItem");
447 if (nodeList != null) {
448 if (nodeList.getLength() == 1) {
449 String resourceId = XPathUtils.getSpeficValueFromNode(nodeList.item(0), "ns1:resourceID");
450 if (resourceId == null || resourceId.isEmpty()) {
451 // IHC controller indicates timeout, return empty list
452 return resourceValueList;
456 for (int i = 0; i < nodeList.getLength(); i++) {
457 WSResourceValue newVal = parseResourceValue(nodeList.item(i));
458 if (newVal != null) {
459 resourceValueList.add(newVal);
463 throw new IhcExecption("Illegal resource value notification response received");
465 return resourceValueList;
466 } catch (XPathExpressionException | NumberFormatException | IOException e) {
467 throw new IhcExecption("Error occured during XML data parsing", e);