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.util.ArrayList;
17 import java.util.List;
20 import javax.xml.xpath.XPathExpressionException;
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;
38 * Class to handle IHC / ELKO LS Controller's resource interaction service.
40 * Service is used to fetch or update resource values from/to controller.
42 * @author Pauli Anttila - Initial contribution
44 public class IhcResourceInteractionService extends IhcBaseService {
46 public IhcResourceInteractionService(String host, int timeout, IhcConnectionPool ihcConnectionPool) {
47 super(ihcConnectionPool, timeout, host, "ResourceInteractionService");
51 * Query resource value from controller.
53 * @param resoureId Resource Identifier.
54 * @return Resource value.
56 public WSResourceValue resourceQuery(int resoureId) throws IhcExecption {
58 final String soapQuery =
60 <?xml version="1.0" encoding="UTF-8"?>
61 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
63 <ns1:getRuntimeValue1 xmlns:ns1="utcs">%s</ns1:getRuntimeValue1>
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 (resourceId != null && !resourceId.isBlank()) {
96 int id = Integer.parseInt(resourceId);
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));
107 // Parse boolean value
108 String value = getValue(n, "value");
109 if (value != null && !value.isBlank()) {
110 return new WSBooleanValue(id, Boolean.valueOf(value));
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));
122 String milliseconds = getValue(n, "milliseconds");
123 if (milliseconds != null && !milliseconds.isBlank()) {
124 return new WSTimerValue(id, Integer.valueOf(milliseconds));
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));
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));
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);
151 // Parse week day value
152 value = getValue(n, "weekdayNumber");
153 if (value != null && !value.isBlank()) {
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 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);
194 throw new IhcExecption("Unsupported value type " + value.getClass().toString());
200 public boolean resourceUpdate(WSBooleanValue value) throws IhcExecption {
202 final String soapQuery =
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/">
207 <setResourceValue1 xmlns="utcs">
208 <value xmlns:q1="utcs.values" xsi:type="q1:WSBooleanValue">
209 <q1:value>%s</q1:value>
211 <resourceID>%s</resourceID>
212 <isValueRuntime>true</isValueRuntime>
219 String query = String.format(soapQuery, value.value ? "true" : "false", value.resourceID);
220 return doResourceUpdate(query);
223 public boolean resourceUpdate(WSFloatingPointValue value) throws IhcExecption {
225 final String soapQuery =
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/">
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>
236 <resourceID>%s</resourceID>
237 <isValueRuntime>true</isValueRuntime>
244 String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
245 return doResourceUpdate(query);
248 public boolean resourceUpdate(WSIntegerValue value) throws IhcExecption {
250 final String soapQuery =
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/">
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>
261 <resourceID>%s</resourceID>
262 <isValueRuntime>true</isValueRuntime>
269 String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
270 return doResourceUpdate(query);
273 public boolean resourceUpdate(WSTimerValue value) throws IhcExecption {
275 final String soapQuery =
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/">
280 <setResourceValue1 xmlns="utcs">
281 <value xmlns:q1="utcs.values" xsi:type="q1:WSTimerValue">
282 <q1:milliseconds>%s</q1:milliseconds>
284 <resourceID>%s</resourceID>
285 <isValueRuntime>true</isValueRuntime>
292 String query = String.format(soapQuery, value.milliseconds, value.resourceID);
293 return doResourceUpdate(query);
296 public boolean resourceUpdate(WSWeekdayValue value) throws IhcExecption {
298 final String soapQuery =
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/">
303 <setResourceValue1 xmlns="utcs">
304 <value xmlns:q1="utcs.values" xsi:type="q1:WSWeekdayValue">
305 <q1:weekdayNumber>%s</q1:weekdayNumber>
307 <resourceID>%s</resourceID>
308 <isValueRuntime>true</isValueRuntime>
315 String query = String.format(soapQuery, value.weekdayNumber, value.resourceID);
316 return doResourceUpdate(query);
319 public boolean resourceUpdate(WSEnumValue value) throws IhcExecption {
321 final String soapQuery =
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/">
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>
332 <resourceID>%s</resourceID>
333 <isValueRuntime>true</isValueRuntime>
340 String query = String.format(soapQuery, value.definitionTypeID, value.enumValueID, value.enumName,
342 return doResourceUpdate(query);
345 public boolean resourceUpdate(WSTimeValue value) throws IhcExecption {
347 final String soapQuery =
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/">
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>
358 <resourceID>%s</resourceID>
359 <isValueRuntime>true</isValueRuntime>
366 String query = String.format(soapQuery, value.hours, value.minutes, value.seconds, value.resourceID);
367 return doResourceUpdate(query);
370 public boolean resourceUpdate(WSDateValue value) throws IhcExecption {
372 final String soapQuery =
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/">
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>
383 <resourceID>%s</resourceID>
384 <isValueRuntime>true</isValueRuntime>
391 String query = String.format(soapQuery, value.month, value.year, value.day, value.resourceID);
392 return doResourceUpdate(query);
395 private boolean doResourceUpdate(String query) throws IhcExecption {
396 String response = sendSoapQuery(null, query);
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);
406 * Enable resources runtime value notifications.
408 * @param resourceIdList List of resource Identifiers.
410 public void enableRuntimeValueNotifications(Set<Integer> resourceIdList) throws IhcExecption {
412 final String soapQueryPrefix =
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/">
417 <enableRuntimeValueNotifications1 xmlns="utcs">
420 final String soapQuerySuffix =
422 </enableRuntimeValueNotifications1>
428 String query = soapQueryPrefix;
429 for (int i : resourceIdList) {
430 query += " <xsd:arrayItem>" + i + "</xsd:arrayItem>\n";
432 query += soapQuerySuffix;
433 sendSoapQuery(null, query);
437 * Wait runtime value notifications.
439 * Runtime value notification should firstly be activated by
440 * enableRuntimeValueNotifications function.
442 * @param timeoutInSeconds How many seconds to wait notifications.
443 * @return List of received runtime value notifications.
444 * @throws IhcExecption
446 public List<WSResourceValue> waitResourceValueNotifications(int timeoutInSeconds) throws IhcExecption {
448 final String soapQuery =
450 <?xml version="1.0" encoding="UTF-8"?>
451 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:utcs="utcs">
453 <utcs:waitForResourceValueChanges1>%s</utcs:waitForResourceValueChanges1>
459 String query = String.format(soapQuery, timeoutInSeconds);
460 String response = sendSoapQuery(null, query, getTimeout() + timeoutInSeconds * 1000);
461 List<WSResourceValue> resourceValueList = new ArrayList<>();
464 NodeList nodeList = XPathUtils.parseList(response,
465 "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:waitForResourceValueChanges2/ns1:arrayItem");
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;
476 for (int i = 0; i < nodeList.getLength(); i++) {
477 WSResourceValue newVal = parseResourceValue(nodeList.item(i));
478 if (newVal != null) {
479 resourceValueList.add(newVal);
483 throw new IhcExecption("Illegal resource value notification response received");
485 return resourceValueList;
486 } catch (XPathExpressionException | NumberFormatException | IOException e) {
487 throw new IhcExecption("Error occured during XML data parsing", e);