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 =
61 <?xml version="1.0" encoding="UTF-8"?>
62 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
64 <ns1:getRuntimeValue1 xmlns:ns1="utcs">%s</ns1:getRuntimeValue1>
70 String query = String.format(soapQuery, String.valueOf(resoureId));
71 String response = sendSoapQuery(null, query);
74 nodeList = XPathUtils.parseList(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:getRuntimeValue2");
76 if (nodeList != null && nodeList.getLength() == 1) {
77 WSResourceValue val = parseResourceValue(nodeList.item(0));
79 if (val != null && val.resourceID == resoureId) {
82 throw new IhcExecption("No resource id found");
85 throw new IhcExecption("No resource value found");
87 } catch (XPathExpressionException | NumberFormatException | IOException e) {
88 throw new IhcExecption("Error occured during XML data parsing", e);
92 private WSResourceValue parseResourceValue(Node n) throws XPathExpressionException, NumberFormatException {
94 String resourceId = XPathUtils.getSpeficValueFromNode(n, "ns1:resourceID");
96 if (resourceId != null && !resourceId.isBlank()) {
97 int id = Integer.parseInt(resourceId);
99 // Parse floating point value
100 String floatingPointValue = getValue(n, "floatingPointValue");
101 if (floatingPointValue != null && !floatingPointValue.isBlank()) {
102 String min = getValue(n, "minimumValue");
103 String max = getValue(n, "maximumValue");
104 return new WSFloatingPointValue(id, Double.valueOf(floatingPointValue), Double.valueOf(min),
105 Double.valueOf(max));
108 // Parse boolean value
109 String value = getValue(n, "value");
110 if (value != null && !value.isBlank()) {
111 return new WSBooleanValue(id, Boolean.valueOf(value));
114 // Parse integer value
115 String integer = getValue(n, "integer");
116 if (integer != null && !integer.isBlank()) {
117 String min = getValue(n, "minimumValue");
118 String max = getValue(n, "maximumValue");
119 return new WSIntegerValue(id, Integer.valueOf(integer), Integer.valueOf(min), Integer.valueOf(max));
123 String milliseconds = getValue(n, "milliseconds");
124 if (milliseconds != null && !milliseconds.isBlank()) {
125 return new WSTimerValue(id, Integer.valueOf(milliseconds));
129 String hours = getValue(n, "hours");
130 if (hours != null && !hours.isBlank()) {
131 String minutes = getValue(n, "minutes");
132 String seconds = getValue(n, "seconds");
133 return new WSTimeValue(id, Integer.valueOf(hours), Integer.valueOf(minutes), Integer.valueOf(seconds));
137 String year = getValue(n, "year");
138 if (year != null && !year.isBlank()) {
139 String month = getValue(n, "month");
140 String day = getValue(n, "day");
141 return new WSDateValue(id, Short.valueOf(year), Byte.valueOf(month), Byte.valueOf(day));
145 String definitionTypeID = getValue(n, "definitionTypeID");
146 if (definitionTypeID != null && !definitionTypeID.isBlank()) {
147 String enumValueID = getValue(n, "enumValueID");
148 String enumName = getValue(n, "enumName");
149 return new WSEnumValue(id, Integer.valueOf(definitionTypeID), Integer.valueOf(enumValueID), enumName);
152 // Parse week day value
153 value = getValue(n, "weekdayNumber");
154 if (value != null && !value.isBlank()) {
155 return new WSWeekdayValue(id, Integer.valueOf(value));
158 // Unknown value type
159 throw new IllegalArgumentException("Unsupported value type");
164 private String getValue(Node n, String value) throws XPathExpressionException {
165 return XPathUtils.getSpeficValueFromNode(n, "ns1:value/" + XPathUtils.createIgnoreNameSpaceSyntaxExpr(value));
169 * Update resource value to controller.
172 * @param value Resource value.
173 * @return True if value is successfully updated.
175 public boolean resourceUpdate(WSResourceValue value) throws IhcExecption {
176 boolean retval = false;
178 if (value instanceof WSFloatingPointValue pointValue) {
179 retval = resourceUpdate(pointValue);
180 } else if (value instanceof WSBooleanValue booleanValue) {
181 retval = resourceUpdate(booleanValue);
182 } else if (value instanceof WSIntegerValue integerValue) {
183 retval = resourceUpdate(integerValue);
184 } else if (value instanceof WSTimerValue timerValue) {
185 retval = resourceUpdate(timerValue);
186 } else if (value instanceof WSWeekdayValue weekdayValue) {
187 retval = resourceUpdate(weekdayValue);
188 } else if (value instanceof WSEnumValue enumValue) {
189 retval = resourceUpdate(enumValue);
190 } else if (value instanceof WSTimeValue timeValue) {
191 retval = resourceUpdate(timeValue);
192 } else if (value instanceof WSDateValue dateValue) {
193 retval = resourceUpdate(dateValue);
195 throw new IhcExecption("Unsupported value type " + value.getClass().toString());
201 public boolean resourceUpdate(WSBooleanValue value) throws IhcExecption {
203 final String soapQuery =
205 <?xml version="1.0" encoding="UTF-8"?>
206 <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/">
208 <setResourceValue1 xmlns="utcs">
209 <value xmlns:q1="utcs.values" xsi:type="q1:WSBooleanValue">
210 <q1:value>%s</q1:value>
212 <resourceID>%s</resourceID>
213 <isValueRuntime>true</isValueRuntime>
220 String query = String.format(soapQuery, value.value ? "true" : "false", value.resourceID);
221 return doResourceUpdate(query);
224 public boolean resourceUpdate(WSFloatingPointValue value) throws IhcExecption {
226 final String soapQuery =
228 <?xml version="1.0" encoding="UTF-8"?>
229 <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/">
231 <setResourceValue1 xmlns="utcs">
232 <value xmlns:q1="utcs.values" xsi:type="q1:WSFloatingPointValue">
233 <q1:maximumValue>%s</q1:maximumValue>
234 <q1:minimumValue>%s</q1:minimumValue>
235 <q1:floatingPointValue>%s</q1:floatingPointValue>
237 <resourceID>%s</resourceID>
238 <isValueRuntime>true</isValueRuntime>
245 String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
246 return doResourceUpdate(query);
249 public boolean resourceUpdate(WSIntegerValue value) throws IhcExecption {
251 final String soapQuery =
253 <?xml version="1.0" encoding="UTF-8"?>
254 <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/">
256 <setResourceValue1 xmlns="utcs">
257 <value xmlns:q1="utcs.values" xsi:type="q1:WSIntegerValue">
258 <q1:maximumValue>%s</q1:maximumValue>
259 <q1:minimumValue>%s</q1:minimumValue>
260 <q1:integer>%s</q1:integer>
262 <resourceID>%s</resourceID>
263 <isValueRuntime>true</isValueRuntime>
270 String query = String.format(soapQuery, value.maximumValue, value.minimumValue, value.value, value.resourceID);
271 return doResourceUpdate(query);
274 public boolean resourceUpdate(WSTimerValue value) throws IhcExecption {
276 final String soapQuery =
278 <?xml version="1.0" encoding="UTF-8"?>
279 <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/">
281 <setResourceValue1 xmlns="utcs">
282 <value xmlns:q1="utcs.values" xsi:type="q1:WSTimerValue">
283 <q1:milliseconds>%s</q1:milliseconds>
285 <resourceID>%s</resourceID>
286 <isValueRuntime>true</isValueRuntime>
293 String query = String.format(soapQuery, value.milliseconds, value.resourceID);
294 return doResourceUpdate(query);
297 public boolean resourceUpdate(WSWeekdayValue value) throws IhcExecption {
299 final String soapQuery =
301 <?xml version="1.0" encoding="UTF-8"?>
302 <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/">
304 <setResourceValue1 xmlns="utcs">
305 <value xmlns:q1="utcs.values" xsi:type="q1:WSWeekdayValue">
306 <q1:weekdayNumber>%s</q1:weekdayNumber>
308 <resourceID>%s</resourceID>
309 <isValueRuntime>true</isValueRuntime>
316 String query = String.format(soapQuery, value.weekdayNumber, value.resourceID);
317 return doResourceUpdate(query);
320 public boolean resourceUpdate(WSEnumValue value) throws IhcExecption {
322 final String soapQuery =
324 <?xml version="1.0" encoding="UTF-8"?>
325 <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/">
327 <setResourceValue1 xmlns="utcs">
328 <value xmlns:q1="utcs.values" xsi:type="q1:WSEnumValue">
329 <q1:definitionTypeID>%s</q1:definitionTypeID>
330 <q1:enumValueID>%s</q1:enumValueID>
331 <q1:enumName>%s</q1:enumName>
333 <resourceID>%s</resourceID>
334 <isValueRuntime>true</isValueRuntime>
341 String query = String.format(soapQuery, value.definitionTypeID, value.enumValueID, value.enumName,
343 return doResourceUpdate(query);
346 public boolean resourceUpdate(WSTimeValue value) throws IhcExecption {
348 final String soapQuery =
350 <?xml version="1.0" encoding="UTF-8"?>
351 <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/">
353 <setResourceValue1 xmlns="utcs">
354 <value xmlns:q1="utcs.values" xsi:type="q1:WSTimeValue">
355 <q1:hours>%s</q1:hours>
356 <q1:minutes>%s</q1:minutes>
357 <q1:seconds>%s</q1:seconds>
359 <resourceID>%s</resourceID>
360 <isValueRuntime>true</isValueRuntime>
367 String query = String.format(soapQuery, value.hours, value.minutes, value.seconds, value.resourceID);
368 return doResourceUpdate(query);
371 public boolean resourceUpdate(WSDateValue value) throws IhcExecption {
373 final String soapQuery =
375 <?xml version="1.0" encoding="UTF-8"?>
376 <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/">
378 <setResourceValue1 xmlns="utcs">
379 <value xmlns:q1="utcs.values" xsi:type="q1:WSDateValue">
380 <q1:month>%s</q1:month>
381 <q1:year>%s</q1:year>
384 <resourceID>%s</resourceID>
385 <isValueRuntime>true</isValueRuntime>
392 String query = String.format(soapQuery, value.month, value.year, value.day, value.resourceID);
393 return doResourceUpdate(query);
396 private boolean doResourceUpdate(String query) throws IhcExecption {
397 String response = sendSoapQuery(null, query);
399 return Boolean.parseBoolean(
400 XPathUtils.parseXMLValue(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:setResourceValue2"));
401 } catch (IOException | XPathExpressionException e) {
402 throw new IhcExecption(e);
407 * Enable resources runtime value notifications.
409 * @param resourceIdList List of resource Identifiers.
411 public void enableRuntimeValueNotifications(Set<Integer> resourceIdList) throws IhcExecption {
413 final String soapQueryPrefix =
415 <?xml version="1.0" encoding="UTF-8"?>
416 <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/">
418 <enableRuntimeValueNotifications1 xmlns="utcs">
421 final String soapQuerySuffix =
423 </enableRuntimeValueNotifications1>
429 String query = soapQueryPrefix;
430 for (int i : resourceIdList) {
431 query += " <xsd:arrayItem>" + i + "</xsd:arrayItem>\n";
433 query += soapQuerySuffix;
434 sendSoapQuery(null, query);
438 * Wait runtime value notifications.
440 * Runtime value notification should firstly be activated by
441 * enableRuntimeValueNotifications function.
443 * @param timeoutInSeconds How many seconds to wait notifications.
444 * @return List of received runtime value notifications.
445 * @throws SocketTimeoutException
446 * @throws IhcExecption
448 public List<WSResourceValue> waitResourceValueNotifications(int timeoutInSeconds) throws IhcExecption {
450 final String soapQuery =
452 <?xml version="1.0" encoding="UTF-8"?>
453 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:utcs="utcs">
455 <utcs:waitForResourceValueChanges1>%s</utcs:waitForResourceValueChanges1>
461 String query = String.format(soapQuery, timeoutInSeconds);
462 String response = sendSoapQuery(null, query, getTimeout() + timeoutInSeconds * 1000);
463 List<WSResourceValue> resourceValueList = new ArrayList<>();
466 NodeList nodeList = XPathUtils.parseList(response,
467 "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:waitForResourceValueChanges2/ns1:arrayItem");
469 if (nodeList != null) {
470 if (nodeList.getLength() == 1) {
471 String resourceId = XPathUtils.getSpeficValueFromNode(nodeList.item(0), "ns1:resourceID");
472 if (resourceId == null || resourceId.isEmpty()) {
473 // IHC controller indicates timeout, return empty list
474 return resourceValueList;
478 for (int i = 0; i < nodeList.getLength(); i++) {
479 WSResourceValue newVal = parseResourceValue(nodeList.item(i));
480 if (newVal != null) {
481 resourceValueList.add(newVal);
485 throw new IhcExecption("Illegal resource value notification response received");
487 return resourceValueList;
488 } catch (XPathExpressionException | NumberFormatException | IOException e) {
489 throw new IhcExecption("Error occured during XML data parsing", e);