[mcd] Initial contribution (#13051)

Signed-off-by: Simon Dengler <simon_dengler@gmx.de>
This commit is contained in:
simon-dengler 2022-07-10 20:20:23 +02:00 committed by GitHub
parent 4f57ae7879
commit 9bd8854e0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1386 additions and 0 deletions

View File

@ -178,6 +178,7 @@
/bundles/org.openhab.binding.magentatv/ @markus7017
/bundles/org.openhab.binding.mail/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.max/ @marcelrv
/bundles/org.openhab.binding.mcd/ @simon-dengler
/bundles/org.openhab.binding.mcp23017/ @aogorek
/bundles/org.openhab.binding.mecmeter/ @kaikreuzer
/bundles/org.openhab.binding.melcloud/ @lucacalcaterra @paulianttila @thewiep

View File

@ -886,6 +886,11 @@
<artifactId>org.openhab.binding.max</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mcd</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mcp23017</artifactId>

View File

@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@ -0,0 +1,143 @@
# MCD Binding
This binding allows you to send sensor events from your openHAB environment to the cloud application Managing Care Digital (MCD) by [C&S Computer und Software GmbH](https://www.managingcare.de/).
MCD is the platform for inpatient and outpatient nursing services.
Our REST API allows you to send a variety of sensor events to the system and thus being able to connect your Ambient Assisted Living (AAL) or smart home environment to the documentation software of your nursing service.
Please note that a valid account is needed to access MCD and the Sensor API.
## Supported Things
There are two supported things: **MCD Bridge** and **MCD Sensor Thing**.
## Discovery
Discovery is not supported.
## Thing Configuration
This section shows the configuration parameters of both supported things.
### MCD Bridge
The MCD Bridge (`mcdBridge`) needs to be configured with your valid C&S MCD / sync API credentials.
| parameter | description |
|-----------|------------------------------------|
| userEmail | Email of account |
| userPassword | valid password for the given email |
### MCD Sensor Thing
Each sensor thing (`mcdSensor`) needs to be configured with the identical serial number, that is assigned to this sensor in MCD.
| parameter | description |
|----------------|------------------------------------|
| serialNumber | serial number of the sensor in MCD |
## Channels
The `mcdSensor` thing supports the following channels. To see the sensors' events, please visit [Managing Care Digital](https://cundsdokumentation.de/) and navigate to the dashboard.
| channel | type | description |
|-------------|--------|-----------------------------------------------|
| lastEvent | String | shows the last event that was sent with date and time |
| sendEvent | String | stateless channel for sending events to the API, see list below for valid commands |
The channel `sendEvent` accepts valid Sensor Event Definitions as well as the corresponding ID.
The following table contains all currently accepted Sensor Event Definitions that can be passed as String type commands.
As soon as new events are added to the API, you can use their ID, even if the Definition is not yet added to this list.
For more information about the API, you can have a look at the [C&S Sync API](https://cunds-syncapi.azurewebsites.net/ApiDocumentation).
| Valid String Type Commands |
|------------|
| BEDEXIT |
| BEDENTRY |
| FALL |
| CHANGEPOSITION |
| BATTERYSTATE |
| INACTIVITY |
| ALARM |
| OPEN |
| CLOSE |
| ON |
| OFF |
| ACTIVITY |
| CAPACITY |
| GAS |
| VITALVALUE |
| ROOMEXIT |
| ROOMENTRY |
| REMOVESENSOR |
| SITDOWN |
| STANDUP |
| INACTIVITYROOM |
| SMOKEALARM |
| HEAT |
| COLD |
| QUALITYAIR |
| ALARMAIR |
| ROOMTEMPERATURE |
| HUMIDITY |
| AIRPRESSURE |
| CO2 |
| INDEXUV |
| WEARTIME |
| FIRSTURINE |
| NEWDIAPER |
| DIAPERREMOVED |
| NOCONNECTION |
| LOWBATTERY |
| CONTROLLSENSOR |
| LYING |
| SPILLED |
| DAMAGED |
| GEOEXIT |
| GEOENTRY |
| WALKING |
| RESTING |
| TURNAROUND |
| HOMEEMERGENCY |
| TOILETFLUSH |
| DORSALPOSITION |
| ABDOMINALPOSITION |
| LYINGLEFT |
| LYINGRIGHT |
| LYINGHALFLEFT |
| LYINGHALFRIGHT |
| MOVEMENT |
| PRESENCE |
| NUMBERPERSONS |
| BRIGHTNESSZONE |
## Full Example
Here is an example for the textual configuration. You can of course use the Administration section of the GUI as well.
demo.things:
```
Bridge mcd:mcdBridge:exampleBridge [userEmail="your.email@examle.com", userPassword="your.password"]{
Thing mcd:mcdSensor:examlpeSensor [serialNumber="123"]
Thing mcd:mcdSensor:secondExamlpeSensor [serialNumber="456"]
}
```
demo.items:
```
String lastValue "Last Value" {channel="mcd:mcdSensor:examlpeSensor:lastValue"}
String sendEvent "Send Event" {channel="mcd:mcdSensor:examlpeSensor:sendEvent"}
```
demo.sitemap:
```
Text item=sendEvent
Text item=lastValue
```

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.4.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.mcd</artifactId>
<name>openHAB Add-ons :: Bundles :: MCD Binding</name>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.mcd-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-mcd" description="Managing Care Digital (MCD) Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mcd/${project.version}</bundle>
</feature>
</features>

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link McdBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
public class McdBindingConstants {
private static final String BINDING_ID = "mcd";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_MCD_BRIDGE = new ThingTypeUID(BINDING_ID, "mcdBridge");
public static final ThingTypeUID THING_TYPE_SENSOR = new ThingTypeUID(BINDING_ID, "mcdSensor");
// List of all Channel ids
public static final String LAST_VALUE = "lastValue";
public static final String SEND_EVENT = "sendEvent";
}

View File

@ -0,0 +1,74 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal;
import static org.openhab.binding.mcd.internal.McdBindingConstants.THING_TYPE_MCD_BRIDGE;
import static org.openhab.binding.mcd.internal.McdBindingConstants.THING_TYPE_SENSOR;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.mcd.internal.handler.McdBridgeHandler;
import org.openhab.binding.mcd.internal.handler.SensorThingHandler;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link McdHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.mcd", service = ThingHandlerFactory.class)
public class McdHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_MCD_BRIDGE,
THING_TYPE_SENSOR);
private final HttpClient httpClient;
@Activate
public McdHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
this.httpClient = httpClientFactory.getCommonHttpClient();
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_MCD_BRIDGE.equals(thingTypeUID)) {
return new McdBridgeHandler((Bridge) thing, httpClient);
}
if (THING_TYPE_SENSOR.equals(thingTypeUID)) {
return new SensorThingHandler(thing, httpClient);
}
return null;
}
}

View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link McdBridgeConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
public class McdBridgeConfiguration {
private @Nullable String userEmail;
private @Nullable String userPassword;
/**
* Return user email as string
*
* @return User email as string
*/
@Nullable
String getUserEmail() {
return userEmail;
}
/**
* Return user password as string
*
* @return password as string
*/
@Nullable
String getUserPassword() {
return userPassword;
}
}

View File

@ -0,0 +1,164 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.handler;
import java.util.HashSet;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.mcd.internal.util.Listener;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
/**
* The {@link McdBridgeHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
public class McdBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(McdBridgeHandler.class);
private @Nullable McdBridgeConfiguration config;
private final HttpClient httpClient;
private final Gson gson;
private String accessToken = "";
private int expiresIn;
private @Nullable ScheduledFuture<?> future = null;
private HashSet<Listener> listeners = new HashSet<>();
public McdBridgeHandler(Bridge bridge, HttpClient httpClient) {
super(bridge);
this.httpClient = httpClient;
gson = new Gson();
}
@Override
public void initialize() {
config = getConfigAs(McdBridgeConfiguration.class);
updateStatus(ThingStatus.UNKNOWN);
scheduler.execute(this::logMeIn);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void dispose() {
ScheduledFuture<?> localFuture = future;
if (localFuture != null) {
localFuture.cancel(true);
}
}
public void register(Listener listener) {
listeners.add(listener);
}
private void triggerEvent() {
logger.debug("Event triggered");
for (Listener l : listeners) {
l.onEvent();
}
}
/**
* Uses the given credentials to log the user in.
*/
protected void logMeIn() {
logger.debug("Logging in...");
McdBridgeConfiguration localConfig = config;
if (localConfig != null) {
try {
Request request = httpClient.newRequest("https://cunds-syncapi.azurewebsites.net/token")
.method(HttpMethod.POST).header(HttpHeader.CONTENT_TYPE, "application/x-www-form-urlencoded")
.header(HttpHeader.HOST, "cunds-syncapi.azurewebsites.net")
.header(HttpHeader.ACCEPT, "application/json");
String content = "grant_type=password&username=" + localConfig.getUserEmail() + "&password="
+ localConfig.getUserPassword();
request.content(new StringContentProvider(content), "application/x-www-form-urlencoded");
request.send(new BufferingResponseListener() {
@NonNullByDefault({})
@Override
public void onComplete(Result result) {
String contentString = getContentAsString();
JsonObject content = gson.fromJson(contentString, JsonObject.class);
int responseCode = result.getResponse().getStatus();
switch (responseCode) {
case 200:
if (content != null && content.has("access_token")) {
updateStatus(ThingStatus.ONLINE);
accessToken = content.get("access_token").getAsString();
expiresIn = content.get("expires_in").getAsInt();
long delay = ((long) expiresIn) * 1000L - 60000L;
Runnable task = () -> {
logMeIn();
};
future = scheduler.schedule(task, delay, TimeUnit.MILLISECONDS);
getAccessToken();
break;
} // else go to default
case 400:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"wrong credentials");
break;
case 0:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"please check your internet connection");
break;
default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Login was not successful");
}
triggerEvent();
}
});
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
}
/**
* Should be used by C&S binding's things to obtain the bridge's access token
*
* @return returns the access token as String
*/
protected String getAccessToken() {
return accessToken;
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link SensorThingConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
public class SensorThingConfiguration {
private @Nullable String serialNumber;
/**
* return serial number as string
*
* @return serial number as string
*/
public @Nullable String getSerialNumber() {
return serialNumber;
}
}

View File

@ -0,0 +1,427 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.handler;
import static org.openhab.binding.mcd.internal.McdBindingConstants.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.mcd.internal.util.Callback;
import org.openhab.binding.mcd.internal.util.SensorEventDef;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* Handler for the SensorThing of the MCD Binding.
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
public class SensorThingHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(SensorThingHandler.class);
private final HttpClient httpClient;
private final @Nullable Gson gson;
private @Nullable McdBridgeHandler mcdBridgeHandler;
private @Nullable String serialNumber = "";
private @Nullable SensorThingConfiguration config;
private int maxSensorEventId = 0;
private boolean initIsDone = false;
public SensorThingHandler(Thing thing, HttpClient httpClient) {
super(thing);
this.httpClient = httpClient;
gson = new Gson();
}
@Override
public void initialize() {
config = getConfigAs(SensorThingConfiguration.class);
Bridge bridge = getBridge();
if (bridge != null) {
mcdBridgeHandler = (McdBridgeHandler) bridge.getHandler();
} else {
mcdBridgeHandler = null;
}
updateStatus(ThingStatus.UNKNOWN);
scheduler.execute(this::init);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
refreshChannelValue();
} else if (mcdBridgeHandler != null) {
String channelId = channelUID.getId();
// check for the right channel id
if (channelId.equals(SEND_EVENT)) {
String commandString = command.toString();
int sensorEventId = SensorEventDef.getSensorEventId(commandString);
if (sensorEventId < 1 || sensorEventId > maxSensorEventId) {
// check, if an id is passed as number
try {
sensorEventId = Integer.parseInt(commandString);
if (sensorEventId < 1 || sensorEventId > maxSensorEventId) {
logger.warn("Invalid Command!");
} else {
sendSensorEvent(serialNumber, sensorEventId);
}
} catch (Exception e) {
logger.warn("Invalid Command!");
}
} else {
// command was valid (and id is between 1 and max)
sendSensorEvent(serialNumber, sensorEventId);
}
} else {
logger.warn("Received command for unexpected channel!");
}
refreshChannelValue();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Bridge is offline.");
}
}
// this is called from initialize()
private void init() {
SensorThingConfiguration localConfig = config;
if (localConfig != null) {
serialNumber = localConfig.getSerialNumber();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Cannot access config data.");
}
McdBridgeHandler localMcdBridgeHandler = mcdBridgeHandler;
if (localMcdBridgeHandler != null) {
updateStatus(ThingStatus.ONLINE);
if (!initIsDone) {
// build and register listener
localMcdBridgeHandler.register(() -> {
try {
// determine, if thing is specified correctly and if it is online
fetchDeviceInfo(res -> {
if (res != null) {
JsonObject result = res.getAsJsonObject();
if (result.has("SerialNumber")) {
// check for serial number in MCD cloud
if (result.get("SerialNumber").isJsonNull()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Serial number does not exist in MCD!");
} else {
// refresh channel values and set thing status to ONLINE
refreshChannelValue();
updateStatus(ThingStatus.ONLINE);
}
}
}
});
fetchEventDef(jsonElement -> {
if (jsonElement != null) {
JsonArray eventDefArray = jsonElement.getAsJsonArray();
maxSensorEventId = eventDefArray.size();
}
});
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
});
initIsDone = true;
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "unable to access bridge");
}
}
/**
* This method uses the things serial number in order to obtain the latest
* sensor event, that was registered in the
* C&S MCD cloud, and then updates the channels with this latest value.
*/
private void refreshChannelValue() {
try {
/*
* First, the device info for the given serial number is requested from the
* cloud, which is then used fetch
* the latest sensor event and update the channels.
*/
fetchDeviceInfo(deviceInfo -> {
// build request URI String
String requestUrl = getUrlStringFromDeviceInfo((JsonObject) deviceInfo);
try {
if (requestUrl != null) {
// get latest sensor event
fetchLatestValue(requestUrl, result -> {
JsonObject latestValue = getLatestValueFromJsonArray((JsonArray) result);
// update channels
updateChannels(latestValue);
});
} else {
logger.warn(
"Unable to synchronize! Please assign sensor to patient or organization unit in MCD!");
}
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
});
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
/**
* Updates the channels of the sensor thing with the latest value.
*
* @param latestValue the latest value as JsonObject as obtained from the REST
* API
*/
private void updateChannels(@Nullable JsonObject latestValue) {
if (latestValue != null) {
String event = latestValue.get("EventDef").getAsString();
String dateString = latestValue.get("DateEntry").getAsString();
try {
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(dateString);
dateString = new SimpleDateFormat("dd.MM.yyyy', 'HH:mm:ss").format(date);
} catch (Exception e) {
logger.debug("{}", e.getMessage());
}
updateState(LAST_VALUE, new StringType(event + ", " + dateString));
}
}
/**
* Make asynchronous HTTP request to fetch the sensors last value as JsonObject.
*
* @param urlString Contains the request URI as String
* @param callback Implementation of interface Callback
* (org.openhab.binding.mcd.internal.util), that includes
* the proceeding of the obtained JsonObject.
* @throws Exception Throws HTTP related Exceptions.
*/
private void fetchLatestValue(String urlString, Callback callback) throws Exception {
McdBridgeHandler localMcdBridgeHandler = mcdBridgeHandler;
if (localMcdBridgeHandler != null) {
String accessToken = localMcdBridgeHandler.getAccessToken();
Request request = httpClient.newRequest(urlString).method(HttpMethod.GET)
.header(HttpHeader.HOST, "cunds-syncapi.azurewebsites.net")
.header(HttpHeader.ACCEPT, "application/json")
.header(HttpHeader.AUTHORIZATION, "Bearer " + accessToken);
request.send(new BufferingResponseListener() {
@NonNullByDefault({})
@Override
public void onComplete(Result result) {
String contentString = getContentAsString();
Gson localGson = gson;
if (localGson != null) {
JsonArray content = localGson.fromJson(contentString, JsonArray.class);
callback.jsonElementTypeCallback(content);
}
}
});
}
}
/**
* get device info as json via http request
*
* @param callback instance of callback interface
* @throws Exception throws http related exceptions
*/
private void fetchDeviceInfo(Callback callback) throws Exception {
McdBridgeHandler localMcdBridgeHandler = mcdBridgeHandler;
if (localMcdBridgeHandler != null) {
String accessToken = localMcdBridgeHandler.getAccessToken();
Request request = httpClient
.newRequest("https://cunds-syncapi.azurewebsites.net/api/Device?serialNumber=" + serialNumber)
.method(HttpMethod.GET).header(HttpHeader.HOST, "cunds-syncapi.azurewebsites.net")
.header(HttpHeader.ACCEPT, "application/json")
.header(HttpHeader.AUTHORIZATION, "Bearer " + accessToken);
request.send(new BufferingResponseListener() {
@NonNullByDefault({})
@Override
public void onComplete(Result result) {
String contentString = getContentAsString();
Gson localGson = gson;
if (localGson != null) {
JsonObject content = localGson.fromJson(contentString, JsonObject.class);
callback.jsonElementTypeCallback(content);
}
}
});
}
}
/**
* Sends a GET request to the C&S REST API to receive the list of sensor event
* definitions.
*
* @param callback Implementation of interface Callback
* (org.openhab.binding.mcd.internal.util), that includes
* the proceeding of the obtained JsonObject.
* @throws Exception Throws HTTP related Exceptions.
*/
private void fetchEventDef(Callback callback) throws Exception {
McdBridgeHandler localMcdBridgeHandler = mcdBridgeHandler;
if (localMcdBridgeHandler != null) {
String accessToken = localMcdBridgeHandler.getAccessToken();
Request request = httpClient.newRequest("https://cunds-syncapi.azurewebsites.net/api/ApiSensor/GetEventDef")
.method(HttpMethod.GET).header(HttpHeader.HOST, "cunds-syncapi.azurewebsites.net")
.header(HttpHeader.ACCEPT, "application/json")
.header(HttpHeader.AUTHORIZATION, "Bearer " + accessToken);
request.send(new BufferingResponseListener() {
@NonNullByDefault({})
@Override
public void onComplete(Result result) {
String contentString = getContentAsString();
Gson localGson = gson;
if (localGson != null) {
JsonArray content = localGson.fromJson(contentString, JsonArray.class);
callback.jsonElementTypeCallback(content);
}
}
});
}
}
/**
* Builds the URI String for requesting the latest sensor event from the API. In
* order to do that, the parameter
* deviceInfo is needed.
*
* @param deviceInfo JsonObject that contains the device info as received from
* the C&S API
* @return returns the URI as String or null, if no patient or organisation unit
* is assigned to the sensor in the
* MCD cloud
*/
@Nullable
String getUrlStringFromDeviceInfo(@Nullable JsonObject deviceInfo) {
if (deviceInfo != null) {
if (deviceInfo.has("SerialNumber") && deviceInfo.get("SerialNumber").getAsString().equals(serialNumber)) {
if (deviceInfo.has("PatientDevices") && deviceInfo.getAsJsonArray("PatientDevices").size() != 0) {
JsonArray array = deviceInfo.getAsJsonArray("PatientDevices");
JsonObject patient = array.get(0).getAsJsonObject();
if (patient.has("UuidPerson") && !patient.get("UuidPerson").isJsonNull()) {
return "https://cunds-syncapi.azurewebsites.net/api/ApiSensor/GetLatestApiSensorEvents"
+ "?UuidPatient=" + patient.get("UuidPerson").getAsString() + "&SerialNumber="
+ serialNumber + "&Count=1";
}
} else if (deviceInfo.has("OrganisationUnitDevices")
&& deviceInfo.getAsJsonArray("OrganisationUnitDevices").size() != 0) {
JsonArray array = deviceInfo.getAsJsonArray("OrganisationUnitDevices");
JsonObject orgUnit = array.get(0).getAsJsonObject();
if (orgUnit.has("UuidOrganisationUnit") && !orgUnit.get("UuidOrganisationUnit").isJsonNull()) {
return "https://cunds-syncapi.azurewebsites.net/api/ApiSensor/GetLatestApiSensorEvents"
+ "?UuidOrganisationUnit=" + orgUnit.get("UuidOrganisationUnit").getAsString()
+ "&SerialNumber=" + serialNumber + "&Count=1";
}
}
} else {
init();
}
}
return null;
}
/**
* Extracts the latest value from the JsonArray, that is obtained by the C&S
* SensorApi.
*
* @param jsonArray the array that contains the latest value
* @return the latest value as JsonObject or null.
*/
@Nullable
static JsonObject getLatestValueFromJsonArray(@Nullable JsonArray jsonArray) {
if (jsonArray != null) {
if (jsonArray.size() != 0) {
JsonObject patientObject = jsonArray.get(0).getAsJsonObject();
JsonArray devicesArray = patientObject.getAsJsonArray("Devices");
if (devicesArray.size() != 0) {
JsonObject deviceObject = devicesArray.get(0).getAsJsonObject();
if (deviceObject.has("Events")) {
JsonArray eventsArray = deviceObject.getAsJsonArray("Events");
if (eventsArray.size() != 0) {
return eventsArray.get(0).getAsJsonObject();
}
}
}
}
}
return null;
}
/**
* Sends data to the cloud via POST request and switches the channel states from
* ON to OFF for a number of channels.
*
* @param serialNumber serial number of the sensor in the MCD cloud
* @param sensorEventDef specifies the type of sensor event, that will be sent
*/
private void sendSensorEvent(@Nullable String serialNumber, int sensorEventDef) {
try {
McdBridgeHandler localMcdBridgeHandler = mcdBridgeHandler;
if (localMcdBridgeHandler != null) {
String accessToken = localMcdBridgeHandler.getAccessToken();
Date date = new Date();
String dateString = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(date);
Request request = httpClient.newRequest("https://cunds-syncapi.azurewebsites.net/api/ApiSensor")
.method(HttpMethod.POST).header(HttpHeader.CONTENT_TYPE, "application/json")
.header(HttpHeader.ACCEPT, "application/json")
.header(HttpHeader.AUTHORIZATION, "Bearer " + accessToken);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("SerialNumber", serialNumber);
jsonObject.addProperty("IdApiSensorEventDef", sensorEventDef);
jsonObject.addProperty("DateEntry", dateString);
jsonObject.addProperty("DateSend", dateString);
request.content(
new StringContentProvider("application/json", jsonObject.toString(), StandardCharsets.UTF_8));
request.send(new BufferingResponseListener() {
@NonNullByDefault({})
@Override
public void onComplete(Result result) {
if (result.getResponse().getStatus() != 201) {
logger.debug("Unable to send sensor event:\n{}", result.getResponse().toString());
} else {
logger.debug("Sensor event was stored successfully.");
refreshChannelValue();
}
}
});
}
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.util;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.JsonElement;
/**
* This interface is used for callback events.
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
public interface Callback {
void jsonElementTypeCallback(@Nullable JsonElement jsonObject);
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.util;
import java.util.EventListener;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Interface for registration of event listeners.
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
public interface Listener extends EventListener {
void onEvent();
}

View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.util;
import java.util.ArrayList;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* class that contains MCD SensorEventDefinitions
*
* @author Simon Dengler - Initial contribution
*/
@NonNullByDefault
public class SensorEventDef {
// Sensor Events in order of their ids as specified by C&S syncapi
private static final String[] EVENT_DEFINITION_ARRAY = { "", "UNDEFINED", "BEDEXIT", "BEDENTRY", "FALL",
"CHANGEPOSITION", "BATTERYSTATE", "INACTIVITY", "ALARM", "OPEN", "CLOSE", "ON", "OFF", "ACTIVITY",
"CAPACITY", "GAS", "VITALVALUE", "ROOMEXIT", "ROOMENTRY", "REMOVESENSOR", "SITDOWN", "STANDUP",
"INACTIVITYROOM", "SMOKEALARM", "HEAT", "COLD", "QUALITYAIR", "ALARMAIR", "ROOMTEMPERATURE", "HUMIDITY",
"AIRPRESSURE", "CO2", "INDEXUV", "WEARTIME", "FIRSTURINE", "NEWDIAPER", "DIAPERREMOVED", "NOCONNECTION",
"LOWBATTERY", "CONTROLLSENSOR", "LYING", "SPILLED", "DAMAGED", "GEOEXIT", "GEOENTRY", "WALKING", "RESTING",
"TURNAROUND", "HOMEEMERGENCY", "TOILETFLUSH", "DORSALPOSITION", "ABDOMINALPOSITION", "LYINGLEFT",
"LYINGRIGHT", "LYINGHALFLEFT", "LYINGHALFRIGHT", "MOVEMENT", "PRESENCE", "NUMBERPERSONS",
"BRIGHTNESSZONE" };
private static ArrayList<String> sensorEventDefinition = new ArrayList<String>(
Arrays.asList(EVENT_DEFINITION_ARRAY));
public static ArrayList<String> getSensorEventDefinition() {
return sensorEventDefinition;
}
public static int getSensorEventId(String eventName) {
return sensorEventDefinition.indexOf(eventName);
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="mcd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>MCD Binding</name>
<description>This binding allows you to send sensor events from your openHAB environment to the cloud application
Managing Care Digital (MCD) by C&amp;S Computer und Software GmbH (https://www.managingcare.de/). MCD is the platform
for inpatient and outpatient nursing services. Our REST API allows you to send a variety of sensor events to the
system and thus being able to connect your Ambient Assisted Living (AAL) or smart home environment to the
documentation software of your nursing service. Please note that a valid account is needed to access MCD and the
Sensor API.</description>
</binding:binding>

View File

@ -0,0 +1,85 @@
# binding
binding.mcd.name = MCD Binding
binding.mcd.description = This binding allows you to send sensor events from your openHAB environment to the cloud application Managing Care Digital (MCD) by C&S Computer und Software GmbH (https://www.managingcare.de/). MCD is the platform for inpatient and outpatient nursing services. Our REST API allows you to send a variety of sensor events to the system and thus being able to connect your Ambient Assisted Living (AAL) or smart home environment to the documentation software of your nursing service. Please note that a valid account is needed to access MCD and the Sensor API.
# thing types
thing-type.mcd.mcdBridge.label = MCD Bridge
thing-type.mcd.mcdBridge.description = C&S Managing Care Digital (MCD) account that is used to send data.
thing-type.mcd.mcdSensor.label = MCD Sensor
thing-type.mcd.mcdSensor.description = Sends data for one mcdSensor to MCD, the C&S cloud application.
# thing types config
thing-type.config.mcd.mcdBridge.userEmail.label = Email
thing-type.config.mcd.mcdBridge.userEmail.description = Email of your MCD account.
thing-type.config.mcd.mcdBridge.userPassword.label = Password
thing-type.config.mcd.mcdBridge.userPassword.description = Password for your MCD account.
thing-type.config.mcd.mcdSensor.serialNumber.label = Serial Number
thing-type.config.mcd.mcdSensor.serialNumber.description = Please enter the serial number of the sensor. It must match the serial number of this sensor in MCD.
# channel types
channel-type.mcd.lastValue.label = Last Value
channel-type.mcd.lastValue.description = Shows time and value of the last sensor event (readonly).
channel-type.mcd.sendEvent.label = Send Event
channel-type.mcd.sendEvent.description = A stateless channel for sending sensor events.
channel-type.mcd.sendEvent.command.option.BEDEXIT = Bed Exit
channel-type.mcd.sendEvent.command.option.BEDENTRY = Bed Entry
channel-type.mcd.sendEvent.command.option.FALL = Fall
channel-type.mcd.sendEvent.command.option.CHANGEPOSITION = Change Position
channel-type.mcd.sendEvent.command.option.BATTERYSTATE = Battery State
channel-type.mcd.sendEvent.command.option.INACTIVITY = Inactivity
channel-type.mcd.sendEvent.command.option.ALARM = Alarm
channel-type.mcd.sendEvent.command.option.OPEN = Open
channel-type.mcd.sendEvent.command.option.CLOSE = Close
channel-type.mcd.sendEvent.command.option.ON = On
channel-type.mcd.sendEvent.command.option.OFF = Off
channel-type.mcd.sendEvent.command.option.ACTIVITY = Activity
channel-type.mcd.sendEvent.command.option.CAPACITY = Capacity
channel-type.mcd.sendEvent.command.option.GAS = Gas
channel-type.mcd.sendEvent.command.option.VITALVALUE = Vital Value
channel-type.mcd.sendEvent.command.option.ROOMEXIT = Room Exit
channel-type.mcd.sendEvent.command.option.ROOMENTRY = Room Entry
channel-type.mcd.sendEvent.command.option.REMOVESENSOR = Remove Sensor
channel-type.mcd.sendEvent.command.option.SITDOWN = Sit Down
channel-type.mcd.sendEvent.command.option.STANDUP = Stand Up
channel-type.mcd.sendEvent.command.option.INACTIVITYROOM = Inactivity Room
channel-type.mcd.sendEvent.command.option.SMOKEALARM = Smoke Alarm
channel-type.mcd.sendEvent.command.option.HEAT = Heat
channel-type.mcd.sendEvent.command.option.COLD = Cold
channel-type.mcd.sendEvent.command.option.QUALITYAIR = Quality Air
channel-type.mcd.sendEvent.command.option.ALARMAIR = Alarm Air
channel-type.mcd.sendEvent.command.option.ROOMTEMPERATURE = Room Temperature
channel-type.mcd.sendEvent.command.option.HUMIDITY = Humidity
channel-type.mcd.sendEvent.command.option.AIRPRESSURE = Airpressure
channel-type.mcd.sendEvent.command.option.CO2 = CO2
channel-type.mcd.sendEvent.command.option.INDEXUV = UV Index
channel-type.mcd.sendEvent.command.option.WEARTIME = Weartime
channel-type.mcd.sendEvent.command.option.FIRSTURINE = First Urine
channel-type.mcd.sendEvent.command.option.NEWDIAPER = New Diaper
channel-type.mcd.sendEvent.command.option.DIAPERREMOVED = Diaper Removal
channel-type.mcd.sendEvent.command.option.NOCONNECTION = No Connection
channel-type.mcd.sendEvent.command.option.LOWBATTERY = Low Battery
channel-type.mcd.sendEvent.command.option.CONTROLLSENSOR = Controll Sensor
channel-type.mcd.sendEvent.command.option.LYING = Lying
channel-type.mcd.sendEvent.command.option.SPILLED = Spilled
channel-type.mcd.sendEvent.command.option.DAMAGED = Damaged
channel-type.mcd.sendEvent.command.option.GEOEXIT = Geo Exit
channel-type.mcd.sendEvent.command.option.GEOENTRY = Geo Entry
channel-type.mcd.sendEvent.command.option.WALKING = Walking
channel-type.mcd.sendEvent.command.option.RESTING = Resting
channel-type.mcd.sendEvent.command.option.TURNAROUND = Turnaround
channel-type.mcd.sendEvent.command.option.HOMEEMERGENCY = Home Emergency
channel-type.mcd.sendEvent.command.option.TOILETFLUSH = Toilet Flush
channel-type.mcd.sendEvent.command.option.DORSALPOSITION = Dorsal Position
channel-type.mcd.sendEvent.command.option.ABDOMINALPOSITION = Abdominal Position
channel-type.mcd.sendEvent.command.option.LYINGLEFT = Lying Left
channel-type.mcd.sendEvent.command.option.LYINGRIGHT = Lying Right
channel-type.mcd.sendEvent.command.option.LYINGHALFLEFT = Lying Half Left
channel-type.mcd.sendEvent.command.option.LYINGHALFRIGHT = Lying Half Right
channel-type.mcd.sendEvent.command.option.MOVEMENT = Movement
channel-type.mcd.sendEvent.command.option.PRESENCE = Presence
channel-type.mcd.sendEvent.command.option.NUMBERPERSONS = Number Persons
channel-type.mcd.sendEvent.command.option.BRIGHTNESSZONE = Brightness Zone

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="mcd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Thing and Bridge Types -->
<bridge-type id="mcdBridge">
<label>MCD Bridge</label>
<description>C&amp;S Managing Care Digital (MCD) account that is used to send data.</description>
<config-description>
<parameter name="userEmail" type="text" required="true">
<context>email</context>
<label>Email</label>
<description>Email of your MCD account.</description>
</parameter>
<parameter name="userPassword" type="text" required="true">
<context>password</context>
<label>Password</label>
<description>Password for your MCD account.</description>
</parameter>
</config-description>
</bridge-type>
<thing-type id="mcdSensor">
<supported-bridge-type-refs>
<bridge-type-ref id="mcdBridge"/>
</supported-bridge-type-refs>
<label>MCD Sensor</label>
<description>Sends data for one mcdSensor to MCD, the C&amp;S cloud application.</description>
<channels>
<channel id="lastValue" typeId="lastValue"/>
<channel id="sendEvent" typeId="sendEvent"/>
</channels>
<config-description>
<parameter name="serialNumber" type="text" required="true">
<label>Serial Number</label>
<description>Please enter the serial number of the sensor. It must match the serial number of this sensor in MCD.</description>
</parameter>
</config-description>
</thing-type>
<channel-type id="lastValue">
<item-type>String</item-type>
<label>Last Value</label>
<description>Shows time and value of the last sensor event (readonly).</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="sendEvent">
<item-type>String</item-type>
<label>Send Event</label>
<description>A stateless channel for sending sensor events.</description>
<command>
<options>
<option value="BEDEXIT">Bed Exit</option>
<option value="BEDENTRY">Bed Entry</option>
<option value="FALL">Fall</option>
<option value="CHANGEPOSITION">Change Position</option>
<option value="BATTERYSTATE">Battery State</option>
<option value="INACTIVITY">Inactivity</option>
<option value="ALARM">Alarm</option>
<option value="OPEN">Open</option>
<option value="CLOSE">Close</option>
<option value="ON">On</option>
<option value="OFF">Off</option>
<option value="ACTIVITY">Activity</option>
<option value="CAPACITY">Capacity</option>
<option value="GAS">Gas</option>
<option value="VITALVALUE">Vital Value</option>
<option value="ROOMEXIT">Room Exit</option>
<option value="ROOMENTRY">Room Entry</option>
<option value="REMOVESENSOR">Remove Sensor</option>
<option value="SITDOWN">Sit Down</option>
<option value="STANDUP">Stand Up</option>
<option value="INACTIVITYROOM">Inactivity Room</option>
<option value="SMOKEALARM">Smoke Alarm</option>
<option value="HEAT">Heat</option>
<option value="COLD">Cold</option>
<option value="QUALITYAIR">Quality Air</option>
<option value="ALARMAIR">Alarm Air</option>
<option value="ROOMTEMPERATURE">Room Temperature</option>
<option value="HUMIDITY">Humidity</option>
<option value="AIRPRESSURE">Airpressure</option>
<option value="CO2">CO2</option>
<option value="INDEXUV">UV Index</option>
<option value="WEARTIME">Weartime</option>
<option value="FIRSTURINE">First Urine</option>
<option value="NEWDIAPER">New Diaper</option>
<option value="DIAPERREMOVED">Diaper Removal</option>
<option value="NOCONNECTION">No Connection</option>
<option value="LOWBATTERY">Low Battery</option>
<option value="CONTROLLSENSOR">Controll Sensor</option>
<option value="LYING">Lying</option>
<option value="SPILLED">Spilled</option>
<option value="DAMAGED">Damaged</option>
<option value="GEOEXIT">Geo Exit</option>
<option value="GEOENTRY">Geo Entry</option>
<option value="WALKING">Walking</option>
<option value="RESTING">Resting</option>
<option value="TURNAROUND">Turnaround</option>
<option value="HOMEEMERGENCY">Home Emergency</option>
<option value="TOILETFLUSH">Toilet Flush</option>
<option value="DORSALPOSITION">Dorsal Position</option>
<option value="ABDOMINALPOSITION">Abdominal Position</option>
<option value="LYINGLEFT">Lying Left</option>
<option value="LYINGRIGHT">Lying Right</option>
<option value="LYINGHALFLEFT">Lying Half Left</option>
<option value="LYINGHALFRIGHT">Lying Half Right</option>
<option value="MOVEMENT">Movement</option>
<option value="PRESENCE">Presence</option>
<option value="NUMBERPERSONS">Number Persons</option>
<option value="BRIGHTNESSZONE">Brightness Zone</option>
</options>
</command>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* @author Dengler - Initial contribution
*/
@NonNullByDefault
public class SensorThingHandlerTest {
private final Gson gson = new Gson();
@Test
public void getLatestValueFromJsonObjectTest() {
String arrayString = "[\n" + " {\n" + " \"IdPatient\": 1,\n" + " \"LastName\": \"Mustermann\",\n"
+ " \"FirstName\": \"Max\",\n" + " \"Devices\": [\n" + " {\n" + " \"IdDevice\": 2,\n"
+ " \"SerialNumber\": \"001\",\n" + " \"Name\": \"Test Sitzkissen\",\n"
+ " \"Events\": [\n" + " {\n" + " \"EventDef\": \"Alarm\",\n"
+ " \"DateEntry\": \"2021-11-22T10:17:56.2866667\"\n" + " }\n" + " ]\n"
+ " }\n" + " ]\n" + " }\n" + "]";
JsonArray array = gson.fromJson(arrayString, JsonArray.class);
JsonObject object = SensorThingHandler.getLatestValueFromJsonArray(array);
String string = object != null ? object.toString() : null;
assertEquals("{\"EventDef\":\"Alarm\",\"DateEntry\":\"2021-11-22T10:17:56.2866667\"}", string);
arrayString = "[\n" + " {\n" + " \"IdPatient\": 1,\n" + " \"LastName\": \"Mustermann\",\n"
+ " \"FirstName\": \"Max\",\n" + " \"Devices\": [\n" + " {\n" + " \"IdDevice\": 2,\n"
+ " \"SerialNumber\": \"001\",\n" + " \"Name\": \"Test Sitzkissen\"\n" + " }\n"
+ " ]\n" + " }\n" + "]";
array = gson.fromJson(arrayString, JsonArray.class);
assertNull(SensorThingHandler.getLatestValueFromJsonArray(array));
}
@Test
public void dateFormatTest2() {
try {
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse("2021-11-22T14:00:09.9933333");
String dateString = new SimpleDateFormat("yyyy-MM-dd', 'HH:mm:ss").format(date);
assertEquals("2021-11-22, 14:00:09", dateString);
} catch (Exception e) {
}
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mcd.internal.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
/**
* @author Dengler - Initial contribution
*/
@NonNullByDefault
public class SensorEventDefTest {
@Test
public void testGetSensorEventDefinition() {
assertEquals(-1, SensorEventDef.getSensorEventId("banane"));
assertEquals(0, SensorEventDef.getSensorEventId(""));
assertEquals(2, SensorEventDef.getSensorEventId("BEDEXIT"));
}
}

View File

@ -211,6 +211,7 @@
<module>org.openhab.binding.magentatv</module>
<module>org.openhab.binding.mail</module>
<module>org.openhab.binding.max</module>
<module>org.openhab.binding.mcd</module>
<module>org.openhab.binding.mcp23017</module>
<module>org.openhab.binding.mecmeter</module>
<module>org.openhab.binding.melcloud</module>