2 * Copyright (c) 2010-2023 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.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;
39 * Class to handle IHC / ELKO LS Controller's resource interaction service.
41 * Service is used to fetch or update resource values from/to controller.
43 * @author Pauli Anttila - Initial contribution
45 public class IhcResourceInteractionService extends IhcBaseService {
47 public IhcResourceInteractionService(String host, int timeout, IhcConnectionPool ihcConnectionPool) {
48 super(ihcConnectionPool, timeout, host, "ResourceInteractionService");
52 * Query resource value from controller.
54 * @param resoureId Resource Identifier.
55 * @return Resource value.
57 public WSResourceValue resourceQuery(int resoureId) throws IhcExecption {
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"
63 + " <ns1:getRuntimeValue1 xmlns:ns1=\"utcs\">%s</ns1:getRuntimeValue1>\n"
64 + " </soapenv:Body>\n"
65 + "</soapenv:Envelope>";
68 String query = String.format(soapQuery, String.valueOf(resoureId));
69 String response = sendSoapQuery(null, query);
72 nodeList = XPathUtils.parseList(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:getRuntimeValue2");
74 if (nodeList != null && nodeList.getLength() == 1) {
75 WSResourceValue val = parseResourceValue(nodeList.item(0));
77 if (val != null && val.resourceID == resoureId) {
80 throw new IhcExecption("No resource id found");
83 throw new IhcExecption("No resource value found");
85 } catch (XPathExpressionException | NumberFormatException | IOException e) {
86 throw new IhcExecption("Error occured during XML data parsing", e);
90 private WSResourceValue parseResourceValue(Node n) throws XPathExpressionException, NumberFormatException {
92 String resourceId = XPathUtils.getSpeficValueFromNode(n, "ns1:resourceID");
94 if (resourceId != null && !resourceId.isBlank()) {
95 int id = Integer.parseInt(resourceId);
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));
106 // Parse boolean value
107 String value = getValue(n, "value");
108 if (value != null && !value.isBlank()) {
109 return new WSBooleanValue(id, Boolean.valueOf(value));
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));
121 String milliseconds = getValue(n, "milliseconds");
122 if (milliseconds != null && !milliseconds.isBlank()) {
123 return new WSTimerValue(id, Integer.valueOf(milliseconds));
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));
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));
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);
150 // Parse week day value
151 value = getValue(n, "weekdayNumber");
152 if (value != null && !value.isBlank()) {
153 return new WSWeekdayValue(id, Integer.valueOf(value));
156 // Unknown value type
157 throw new IllegalArgumentException("Unsupported value type");
162 private String getValue(Node n, String value) throws XPathExpressionException {
163 return XPathUtils.getSpeficValueFromNode(n, "ns1:value/" + XPathUtils.createIgnoreNameSpaceSyntaxExpr(value));
167 * Update resource value to controller.
170 * @param value Resource value.
171 * @return True if value is successfully updated.
173 public boolean resourceUpdate(WSResourceValue value) throws IhcExecption {
174 boolean retval = false;
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);
193 throw new IhcExecption("Unsupported value type " + value.getClass().toString());
199 public boolean resourceUpdate(WSBooleanValue value) throws IhcExecption {
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"
205 + " <setResourceValue1 xmlns=\"utcs\">\n"
206 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSBooleanValue\">\n"
207 + " <q1:value>%s</q1:value>\n"
209 + " <resourceID>%s</resourceID>\n"
210 + " <isValueRuntime>true</isValueRuntime>\n"
211 + " </setResourceValue1>\n"
213 + "</soap:Envelope>";
216 String query = String.format(soapQuery, value.value ? "true" : "false", value.resourceID);
217 return doResourceUpdate(query);
220 public boolean resourceUpdate(WSFloatingPointValue value) throws IhcExecption {
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"
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"
232 + " <resourceID>%s</resourceID>\n"
233 + " <isValueRuntime>true</isValueRuntime>\n"
234 + " </setResourceValue1>\n"
236 + "</soap:Envelope>";
239 String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
240 return doResourceUpdate(query);
243 public boolean resourceUpdate(WSIntegerValue value) throws IhcExecption {
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"
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"
255 + " <resourceID>%s</resourceID>\n"
256 + " <isValueRuntime>true</isValueRuntime>\n"
257 + " </setResourceValue1>\n"
259 + "</soap:Envelope>";
262 String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
263 return doResourceUpdate(query);
266 public boolean resourceUpdate(WSTimerValue value) throws IhcExecption {
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"
272 + " <setResourceValue1 xmlns=\"utcs\">\n"
273 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSTimerValue\">\n"
274 + " <q1:milliseconds>%s</q1:milliseconds>\n"
276 + " <resourceID>%s</resourceID>\n"
277 + " <isValueRuntime>true</isValueRuntime>\n"
278 + " </setResourceValue1>\n"
280 + "</soap:Envelope>";
283 String query = String.format(soapQuery, value.milliseconds, value.resourceID);
284 return doResourceUpdate(query);
287 public boolean resourceUpdate(WSWeekdayValue value) throws IhcExecption {
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"
293 + " <setResourceValue1 xmlns=\"utcs\">\n"
294 + " <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSWeekdayValue\">\n"
295 + " <q1:weekdayNumber>%s</q1:weekdayNumber>\n"
297 + " <resourceID>%s</resourceID>\n"
298 + " <isValueRuntime>true</isValueRuntime>\n"
299 + " </setResourceValue1>\n"
301 + "</soap:Envelope>";
304 String query = String.format(soapQuery, value.weekdayNumber, value.resourceID);
305 return doResourceUpdate(query);
308 public boolean resourceUpdate(WSEnumValue value) throws IhcExecption {
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"
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"
320 + " <resourceID>%s</resourceID>\n"
321 + " <isValueRuntime>true</isValueRuntime>\n"
322 + " </setResourceValue1>\n"
324 + "</soap:Envelope>";
327 String query = String.format(soapQuery, value.definitionTypeID, value.enumValueID, value.enumName,
329 return doResourceUpdate(query);
332 public boolean resourceUpdate(WSTimeValue value) throws IhcExecption {
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"
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"
344 + " <resourceID>%s</resourceID>\n"
345 + " <isValueRuntime>true</isValueRuntime>\n"
346 + " </setResourceValue1>\n"
348 + "</soap:Envelope>";
351 String query = String.format(soapQuery, value.hours, value.minutes, value.seconds, value.resourceID);
352 return doResourceUpdate(query);
355 public boolean resourceUpdate(WSDateValue value) throws IhcExecption {
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"
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"
367 + " <resourceID>%s</resourceID>\n"
368 + " <isValueRuntime>true</isValueRuntime>\n"
369 + " </setResourceValue1>\n"
371 + "</soap:Envelope>";
374 String query = String.format(soapQuery, value.month, value.year, value.day, value.resourceID);
375 return doResourceUpdate(query);
378 private boolean doResourceUpdate(String query) throws IhcExecption {
379 String response = sendSoapQuery(null, query);
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);
389 * Enable resources runtime value notifications.
391 * @param resourceIdList List of resource Identifiers.
392 * @return True is connection successfully opened.
394 public void enableRuntimeValueNotifications(Set<Integer> resourceIdList) throws IhcExecption {
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"
400 + " <enableRuntimeValueNotifications1 xmlns=\"utcs\">\n";
402 final String soapQuerySuffix =
403 " </enableRuntimeValueNotifications1>\n"
405 + "</soap:Envelope>";
408 String query = soapQueryPrefix;
409 for (int i : resourceIdList) {
410 query += " <xsd:arrayItem>" + i + "</xsd:arrayItem>\n";
412 query += soapQuerySuffix;
413 sendSoapQuery(null, query);
417 * Wait runtime value notifications.
419 * Runtime value notification should firstly be activated by
420 * enableRuntimeValueNotifications function.
422 * @param timeoutInSeconds How many seconds to wait notifications.
423 * @return List of received runtime value notifications.
424 * @throws SocketTimeoutException
425 * @throws IhcTimeoutExecption
427 public List<WSResourceValue> waitResourceValueNotifications(int timeoutInSeconds) throws IhcExecption {
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>";
438 String query = String.format(soapQuery, timeoutInSeconds);
439 String response = sendSoapQuery(null, query, getTimeout() + timeoutInSeconds * 1000);
440 List<WSResourceValue> resourceValueList = new ArrayList<>();
443 NodeList nodeList = XPathUtils.parseList(response,
444 "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:waitForResourceValueChanges2/ns1:arrayItem");
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;
455 for (int i = 0; i < nodeList.getLength(); i++) {
456 WSResourceValue newVal = parseResourceValue(nodeList.item(i));
457 if (newVal != null) {
458 resourceValueList.add(newVal);
462 throw new IhcExecption("Illegal resource value notification response received");
464 return resourceValueList;
465 } catch (XPathExpressionException | NumberFormatException | IOException e) {
466 throw new IhcExecption("Error occured during XML data parsing", e);