diff --git a/.github/workflows/detekt-analysis.yml b/.github/workflows/detekt-analysis.yml deleted file mode 100644 index aabd2a2369..0000000000 --- a/.github/workflows/detekt-analysis.yml +++ /dev/null @@ -1,103 +0,0 @@ -# This workflow performs a static analysis of your Kotlin source code using -# Detekt. -# -# Scans are triggered: -# 1. On every push to default and protected branches -# 2. On every Pull Request targeting the default branch -# 3. On a weekly schedule -# 4. Manually, on demand, via the "workflow_dispatch" event -# -# The workflow should work with no modifications, but you might like to use a -# later version of the Detekt CLI by modifing the $DETEKT_RELEASE_TAG -# environment variable. -name: Scan with Detekt - -on: - # Triggers the workflow on push or pull request events but only for default and protected branches - push: - branches: [ dev, beta, stable ] - pull_request: - branches: [ dev ] - schedule: - - cron: '19 11 * * 4' - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -env: - # Release tag associated with version of Detekt to be installed - # SARIF support (required for this workflow) was introduced in Detekt v1.15.0 - DETEKT_RELEASE_TAG: v1.15.0 - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "scan" - scan: - name: Scan - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - # Gets the download URL associated with the $DETEKT_RELEASE_TAG - - name: Get Detekt download URL - id: detekt_info - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - DETEKT_DOWNLOAD_URL=$( gh api graphql --field tagName=$DETEKT_RELEASE_TAG --raw-field query=' - query getReleaseAssetDownloadUrl($tagName: String!) { - repository(name: "detekt", owner: "detekt") { - release(tagName: $tagName) { - releaseAssets(name: "detekt", first: 1) { - nodes { - downloadUrl - } - } - } - } - } - ' | \ - jq --raw-output '.data.repository.release.releaseAssets.nodes[0].downloadUrl' ) - echo "::set-output name=download_url::$DETEKT_DOWNLOAD_URL" - - # Sets up the detekt cli - - name: Setup Detekt - run: | - dest=$( mktemp -d ) - curl --request GET \ - --url ${{ steps.detekt_info.outputs.download_url }} \ - --silent \ - --location \ - --output $dest/detekt - chmod a+x $dest/detekt - echo $dest >> $GITHUB_PATH - - # Performs static analysis using Detekt - - name: Run Detekt - continue-on-error: true - run: | - detekt --input ${{ github.workspace }} --report sarif:${{ github.workspace }}/detekt.sarif.json - - # Modifies the SARIF output produced by Detekt so that absolute URIs are relative - # This is so we can easily map results onto their source files - # This can be removed once relative URI support lands in Detekt: https://git.io/JLBbA - - name: Make artifact location URIs relative - continue-on-error: true - run: | - echo "$( - jq \ - --arg github_workspace ${{ github.workspace }} \ - '. | ( .runs[].results[].locations[].physicalLocation.artifactLocation.uri |= if test($github_workspace) then .[($github_workspace | length | . + 1):] else . end )' \ - ${{ github.workspace }}/detekt.sarif.json - )" > ${{ github.workspace }}/detekt.sarif.json - - # Uploads results to GitHub repository using the upload-sarif action - - uses: github/codeql-action/upload-sarif@v1 - with: - # Path to SARIF file relative to the root of the repository - sarif_file: ${{ github.workspace }}/detekt.sarif.json - checkout_path: ${{ github.workspace }} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/OpenHABDeviceManagerLauncher.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/OpenHABDeviceManagerLauncher.java index e4e56163c7..32bb4c924c 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/OpenHABDeviceManagerLauncher.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/OpenHABDeviceManagerLauncher.java @@ -36,7 +36,7 @@ public class OpenHABDeviceManagerLauncher extends AbstractLauncher { - public OpenHABDeviceManagerLauncher() throws org.openbase.jul.exception.InstantiationException { + public OpenHABDeviceManagerLauncher() { super(OpenHABDeviceManagerLauncher.class, OpenHABDeviceManager.class); } @@ -45,9 +45,14 @@ public OpenHABDeviceManagerLauncher() throws org.openbase.jul.exception.Instanti */ public static void main(final String[] args) { BCO.printLogo(); - AbstractLauncher.main(BCO.class, OpenHABDeviceManagerLauncher.class, args, OpenHABDeviceManagerLauncher.class, + AbstractLauncher.main( + BCO.class, + OpenHABDeviceManagerLauncher.class, + args, + OpenHABDeviceManagerLauncher.class, OpenHABConfigSynchronizerLauncher.class, - OpenHABSitemapSynchronizerLauncher.class); + OpenHABSitemapSynchronizerLauncher.class + ); } @Override diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.java deleted file mode 100644 index 97618bfd04..0000000000 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.java +++ /dev/null @@ -1,287 +0,0 @@ -package org.openbase.bco.device.openhab.communication; - -/*- - * #%L - * BCO Openhab Device Manager - * %% - * Copyright (C) 2015 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; -import jakarta.ws.rs.core.MediaType; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.InitializationException; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.exception.NotAvailableException; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.iface.Shutdownable; -import org.openhab.core.config.discovery.dto.DiscoveryResultDTO; -import org.openhab.core.io.rest.core.item.EnrichedItemDTO; -import org.openhab.core.io.rest.core.thing.EnrichedThingDTO; -import org.openhab.core.items.dto.ItemDTO; -import org.openhab.core.thing.dto.ThingDTO; -import org.openhab.core.thing.link.dto.ItemChannelLinkDTO; -import org.openhab.core.types.Command; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public class OpenHABRestCommunicator extends OpenHABRestConnection { - - - public static final String ITEMS_TARGET = "items"; - public static final String LINKS_TARGET = "links"; - public static final String THINGS_TARGET = "things"; - public static final String INBOX_TARGET = "inbox"; - public static final String DISCOVERY_TARGET = "discovery"; - public static final String ADDONS_TARGET = "addons"; - public static final String ADDONS_BINDING_PREFIX = "binding-"; - public static final String INSTALL_TARGET = "install"; - public static final String UNINSTALL_TARGET = "uninstall"; - public static final String BINDINGS_TARGET = "bindings"; - public static final String CONFIG_TARGET = "config"; - public static final String SCAN_TARGET = "scan"; - - private static final Logger LOGGER = LoggerFactory.getLogger(OpenHABRestCommunicator.class); - - private static OpenHABRestCommunicator instance = null; - - public synchronized static OpenHABRestCommunicator getInstance() { - if (instance == null) { - try { - instance = new OpenHABRestCommunicator(); - Shutdownable.registerShutdownHook(instance); - } catch (InitializationException ex) { - ExceptionPrinter.printHistory("Could not create OpenHABRestCommunicator", ex, LOGGER); - } catch (CouldNotPerformException ex) { - // only thrown if instance would be null - } - } - - return instance; - } - - private OpenHABRestCommunicator() throws InstantiationException { - super(); - } - - // ========================================================================================================================================== - // THINGS - // ========================================================================================================================================== - - public EnrichedThingDTO registerThing(final ThingDTO thingDTO) throws CouldNotPerformException { - return jsonToClass(JsonParser.parseString(postJson(THINGS_TARGET, thingDTO)), EnrichedThingDTO.class); - } - - public EnrichedThingDTO updateThing(final EnrichedThingDTO enrichedThingDTO) throws CouldNotPerformException { - return jsonToClass(JsonParser.parseString(putJson(THINGS_TARGET + SEPARATOR + enrichedThingDTO.UID, enrichedThingDTO)), EnrichedThingDTO.class); - } - - public EnrichedThingDTO deleteThing(final EnrichedThingDTO enrichedThingDTO) throws CouldNotPerformException { - return deleteThing(enrichedThingDTO.UID); - } - - public EnrichedThingDTO deleteThing(final String thingUID) throws CouldNotPerformException { - return jsonToClass(JsonParser.parseString(delete(THINGS_TARGET + SEPARATOR + thingUID)), EnrichedThingDTO.class); - } - - public EnrichedThingDTO getThing(final String thingUID) throws NotAvailableException { - try { - return jsonToClass(JsonParser.parseString(get(THINGS_TARGET + SEPARATOR + thingUID)), EnrichedThingDTO.class); - } catch (CouldNotPerformException ex) { - throw new NotAvailableException("Thing[" + thingUID + "]"); - } - } - - public List getThings() throws CouldNotPerformException { - return jsonElementToTypedList(JsonParser.parseString(get(THINGS_TARGET)), EnrichedThingDTO.class); - } - - // ========================================================================================================================================== - // ITEMS - // ========================================================================================================================================== - - public ItemDTO registerItem(final ItemDTO itemDTO) throws CouldNotPerformException { - final List itemDTOList = new ArrayList<>(); - itemDTOList.add(itemDTO); - return registerItems(itemDTOList).get(0); - } - - public List registerItems(final List itemDTOList) throws CouldNotPerformException { - return jsonElementToTypedList(JsonParser.parseString(putJson(ITEMS_TARGET, itemDTOList)), ItemDTO.class); - } - - public ItemDTO updateItem(final ItemDTO itemDTO) throws CouldNotPerformException { - return jsonToClass(JsonParser.parseString(putJson(ITEMS_TARGET + SEPARATOR + itemDTO.name, itemDTO)), ItemDTO.class); - } - - public ItemDTO deleteItem(final ItemDTO itemDTO) throws CouldNotPerformException { - return deleteItem(itemDTO.name); - } - - public ItemDTO deleteItem(final String itemName) throws CouldNotPerformException { - LOGGER.warn("Delete item {}", itemName); - return jsonToClass(JsonParser.parseString(delete(ITEMS_TARGET + SEPARATOR + itemName)), ItemDTO.class); - } - - public List getItems() throws CouldNotPerformException { - return jsonElementToTypedList(JsonParser.parseString(get(ITEMS_TARGET)), EnrichedItemDTO.class); - } - - public EnrichedItemDTO getItem(final String itemName) throws NotAvailableException { - try { - return jsonToClass(JsonParser.parseString(get(ITEMS_TARGET + SEPARATOR + itemName)), EnrichedItemDTO.class); - } catch (CouldNotPerformException ex) { - throw new NotAvailableException("Item with name[" + itemName + "]"); - } - } - - public boolean hasItem(final String itemName) { - try { - getItem(itemName); - return true; - } catch (NotAvailableException ex) { - return false; - } - } - - public void postCommand(final String itemName, final Command command) throws CouldNotPerformException { - postCommand(itemName, command.toString()); - } - - public void postCommand(final String itemName, final String command) throws CouldNotPerformException { - post(ITEMS_TARGET + SEPARATOR + itemName, command, MediaType.TEXT_PLAIN_TYPE); - } - - // ========================================================================================================================================== - // ITEM_CHANNEL_LINK - // ========================================================================================================================================== - - public void registerItemChannelLink(final String itemName, final String channelUID) throws CouldNotPerformException { - registerItemChannelLink(new ItemChannelLinkDTO(itemName, channelUID, new HashMap<>())); - } - - public void registerItemChannelLink(final ItemChannelLinkDTO itemChannelLinkDTO) throws CouldNotPerformException { - putJson(LINKS_TARGET + SEPARATOR + itemChannelLinkDTO.itemName + SEPARATOR + itemChannelLinkDTO.channelUID, itemChannelLinkDTO); - } - - public void deleteItemChannelLink(final ItemChannelLinkDTO itemChannelLinkDTO) throws CouldNotPerformException { - deleteItemChannelLink(itemChannelLinkDTO.itemName, itemChannelLinkDTO.channelUID); - } - - public void deleteItemChannelLink(final String itemName, final String channelUID) throws CouldNotPerformException { - delete(LINKS_TARGET + SEPARATOR + itemName + SEPARATOR + channelUID); - } - - public List getItemChannelLinks() throws CouldNotPerformException { - return jsonElementToTypedList(JsonParser.parseString(get(LINKS_TARGET)), ItemChannelLinkDTO.class); - } - - // ========================================================================================================================================== - // DISCOVERY - // ========================================================================================================================================== - - /** - * @param bindingId - * @return the discovery timeout in seconds - * @throws CouldNotPerformException - */ - public Integer startDiscovery(final String bindingId) throws CouldNotPerformException { - final String response = post(DISCOVERY_TARGET + SEPARATOR + BINDINGS_TARGET + SEPARATOR + bindingId + SCAN_TARGET, "", MediaType.APPLICATION_JSON_TYPE); - int discoveryTimeout = Integer.parseInt(response); - - if (discoveryTimeout <= 0) { - throw new CouldNotPerformException("Invalid discovery timeout. Maybe binding " + bindingId + " is not available"); - } - - return discoveryTimeout; - } - - public void approve(final String thingUID, final String label) throws CouldNotPerformException { - post(INBOX_TARGET + SEPARATOR + thingUID + SEPARATOR + APPROVE_TARGET, label, MediaType.TEXT_PLAIN_TYPE); - } - - public List getDiscoveryResults() throws CouldNotPerformException { - return jsonElementToTypedList(JsonParser.parseString(get(INBOX_TARGET)), DiscoveryResultDTO.class); - } - - // ========================================================================================================================================== - // Extensions - // ========================================================================================================================================== - - public void installBinding(final String bindingId) throws CouldNotPerformException { - LOGGER.debug("Install Binding[" + bindingId + "]"); - post(ADDONS_TARGET + SEPARATOR + ADDONS_BINDING_PREFIX + bindingId + SEPARATOR + INSTALL_TARGET, "", MediaType.APPLICATION_JSON_TYPE); - } - - public boolean isBindingInstalled(final String bindingId) { - try { - get(BINDINGS_TARGET + SEPARATOR + bindingId + SEPARATOR + CONFIG_TARGET); - LOGGER.debug("Binding[" + bindingId + "] currently not installed!"); - return true; - } catch (CouldNotPerformException ex) { - LOGGER.debug("Binding[" + bindingId + "] is already installed."); - return false; - } - } - - public void uninstallBindings(final String bindingId) throws CouldNotPerformException { - post(ADDONS_TARGET + SEPARATOR + bindingId + SEPARATOR + UNINSTALL_TARGET, "", MediaType.APPLICATION_JSON_TYPE); - } - - // ========================================================================================================================================== - // UTIL - // ========================================================================================================================================== - - private List jsonElementToTypedList(final JsonElement jsonElement, final Class clazz) throws CouldNotPerformException { - if (jsonElement.isJsonArray()) { - return jsonArrayToTypedList(jsonElement.getAsJsonArray(), clazz); - } else { - throw new CouldNotPerformException("JsonElement is not a JsonArray and thus cannot be converted to a list"); - } - } - - private List jsonArrayToTypedList(final JsonArray jsonArray, final Class clazz) throws CouldNotPerformException { - final List result = new ArrayList(); - - for (final JsonElement jsonElement : jsonArray) { - result.add(jsonToClass(jsonElement, clazz)); - } - - return result; - } - - private T jsonToClass(final JsonElement jsonElement, final Class clazz) throws CouldNotPerformException { - try { - return gson.fromJson(jsonElement, clazz); - } catch (JsonSyntaxException ex) { - throw new CouldNotPerformException("Could not parse jsonElement into object of class[" + clazz.getSimpleName() + "]", ex); - } - } - - @Override - protected void testConnection() throws CouldNotPerformException { - get(INBOX_TARGET, true); - } -} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.kt b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.kt new file mode 100644 index 0000000000..dd79838c2a --- /dev/null +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestCommunicator.kt @@ -0,0 +1,308 @@ +package org.openbase.bco.device.openhab.communication + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonParser +import com.google.gson.JsonSyntaxException +import jakarta.ws.rs.core.MediaType +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.InitializationException +import org.openbase.jul.exception.NotAvailableException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.iface.Shutdownable +import org.openhab.core.config.discovery.dto.DiscoveryResultDTO +import org.openhab.core.io.rest.core.item.EnrichedItemDTO +import org.openhab.core.io.rest.core.thing.EnrichedThingDTO +import org.openhab.core.items.dto.ItemDTO +import org.openhab.core.thing.dto.ThingDTO +import org.openhab.core.thing.link.dto.ItemChannelLinkDTO +import org.openhab.core.types.Command +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class OpenHABRestCommunicator private constructor() : OpenHABRestConnection() { + // ========================================================================================================================================== + // THINGS + // ========================================================================================================================================== + @Throws(CouldNotPerformException::class) + fun registerThing(thingDTO: ThingDTO?): EnrichedThingDTO { + return jsonToClass(JsonParser.parseString(postJson(THINGS_TARGET, thingDTO)), EnrichedThingDTO::class.java) + } + + @Throws(CouldNotPerformException::class) + fun updateThing(enrichedThingDTO: EnrichedThingDTO): EnrichedThingDTO { + return jsonToClass( + JsonParser.parseString( + putJson( + THINGS_TARGET + SEPARATOR + enrichedThingDTO.UID, + enrichedThingDTO + ) + ), EnrichedThingDTO::class.java + ) + } + + @Throws(CouldNotPerformException::class) + fun deleteThing(enrichedThingDTO: EnrichedThingDTO): EnrichedThingDTO { + return deleteThing(enrichedThingDTO.UID) + } + + @Throws(CouldNotPerformException::class) + fun deleteThing(thingUID: String): EnrichedThingDTO { + return jsonToClass( + JsonParser.parseString(delete(THINGS_TARGET + SEPARATOR + thingUID)), + EnrichedThingDTO::class.java + ) + } + + @Throws(NotAvailableException::class) + fun getThing(thingUID: String): EnrichedThingDTO { + try { + return jsonToClass( + JsonParser.parseString(get(THINGS_TARGET + SEPARATOR + thingUID)), + EnrichedThingDTO::class.java + ) + } catch (ex: CouldNotPerformException) { + throw NotAvailableException("Thing[$thingUID]") + } + } + + @get:Throws(CouldNotPerformException::class) + val things: List + get() = jsonElementToTypedList(JsonParser.parseString(get(THINGS_TARGET)), EnrichedThingDTO::class.java) + + // ========================================================================================================================================== + // ITEMS + // ========================================================================================================================================== + @Throws(CouldNotPerformException::class) + fun registerItem(itemDTO: ItemDTO): ItemDTO { + val itemDTOList: MutableList = ArrayList() + itemDTOList.add(itemDTO) + return registerItems(itemDTOList)[0] + } + + @Throws(CouldNotPerformException::class) + fun registerItems(itemDTOList: List?): List { + return jsonElementToTypedList(JsonParser.parseString(putJson(ITEMS_TARGET, itemDTOList)), ItemDTO::class.java) + } + + @Throws(CouldNotPerformException::class) + fun updateItem(itemDTO: ItemDTO): ItemDTO { + return jsonToClass( + JsonParser.parseString(putJson(ITEMS_TARGET + SEPARATOR + itemDTO.name, itemDTO)), + ItemDTO::class.java + ) + } + + @Throws(CouldNotPerformException::class) + fun deleteItem(itemDTO: ItemDTO): ItemDTO = deleteItem(itemDTO.name) + + @Throws(CouldNotPerformException::class) + fun deleteItem(itemName: String): ItemDTO { + LOGGER.warn("Delete item {}", itemName) + return jsonToClass(JsonParser.parseString(delete(ITEMS_TARGET + SEPARATOR + itemName)), ItemDTO::class.java) + } + + @get:Throws(CouldNotPerformException::class) + val items: List + get() = jsonElementToTypedList(JsonParser.parseString(get(ITEMS_TARGET)), EnrichedItemDTO::class.java) + + @Throws(NotAvailableException::class) + fun getItem(itemName: String): EnrichedItemDTO = + try { + jsonToClass( + JsonParser.parseString(get(ITEMS_TARGET + SEPARATOR + itemName)), + EnrichedItemDTO::class.java + ) + } catch (ex: CouldNotPerformException) { + throw NotAvailableException("Item with name[$itemName]") + } + + fun hasItem(itemName: String): Boolean { + try { + getItem(itemName) + return true + } catch (ex: NotAvailableException) { + return false + } + } + + @Throws(CouldNotPerformException::class) + fun postCommand(itemName: String, command: Command) { + postCommand(itemName, command.toString()) + } + + @Throws(CouldNotPerformException::class) + fun postCommand(itemName: String, command: String?) { + post(ITEMS_TARGET + SEPARATOR + itemName, command!!, MediaType.TEXT_PLAIN_TYPE) + } + + // ========================================================================================================================================== + // ITEM_CHANNEL_LINK + // ========================================================================================================================================== + @Throws(CouldNotPerformException::class) + fun registerItemChannelLink(itemName: String?, channelUID: String?) { + registerItemChannelLink(ItemChannelLinkDTO(itemName, channelUID, HashMap())) + } + + @Throws(CouldNotPerformException::class) + fun registerItemChannelLink(itemChannelLinkDTO: ItemChannelLinkDTO) { + putJson( + LINKS_TARGET + SEPARATOR + itemChannelLinkDTO.itemName + SEPARATOR + itemChannelLinkDTO.channelUID, + itemChannelLinkDTO + ) + } + + @Throws(CouldNotPerformException::class) + fun deleteItemChannelLink(itemChannelLinkDTO: ItemChannelLinkDTO) { + deleteItemChannelLink(itemChannelLinkDTO.itemName, itemChannelLinkDTO.channelUID) + } + + @Throws(CouldNotPerformException::class) + fun deleteItemChannelLink(itemName: String, channelUID: String) { + delete(LINKS_TARGET + SEPARATOR + itemName + SEPARATOR + channelUID) + } + + @get:Throws(CouldNotPerformException::class) + val itemChannelLinks: List + get() = jsonElementToTypedList(JsonParser.parseString(get(LINKS_TARGET)), ItemChannelLinkDTO::class.java) + + // ========================================================================================================================================== + // DISCOVERY + // ========================================================================================================================================== + /** + * @param bindingId + * + * @return the discovery timeout in seconds + * + * @throws CouldNotPerformException + */ + @Throws(CouldNotPerformException::class) + fun startDiscovery(bindingId: String): Int { + val response = post( + DISCOVERY_TARGET + SEPARATOR + BINDINGS_TARGET + SEPARATOR + bindingId + SCAN_TARGET, + "", + MediaType.APPLICATION_JSON_TYPE + ) + val discoveryTimeout = response.toInt() + + if (discoveryTimeout <= 0) { + throw CouldNotPerformException("Invalid discovery timeout. Maybe binding $bindingId is not available") + } + + return discoveryTimeout + } + + @Throws(CouldNotPerformException::class) + fun approve(thingUID: String, label: String?) { + post(INBOX_TARGET + SEPARATOR + thingUID + SEPARATOR + APPROVE_TARGET, label!!, MediaType.TEXT_PLAIN_TYPE) + } + + @get:Throws(CouldNotPerformException::class) + val discoveryResults: List + get() = jsonElementToTypedList(JsonParser.parseString(get(INBOX_TARGET)), DiscoveryResultDTO::class.java) + + // ========================================================================================================================================== + // Extensions + // ========================================================================================================================================== + @Throws(CouldNotPerformException::class) + fun installBinding(bindingId: String) { + LOGGER.debug("Install Binding[$bindingId]") + post( + ADDONS_TARGET + SEPARATOR + ADDONS_BINDING_PREFIX + bindingId + SEPARATOR + INSTALL_TARGET, + "", + MediaType.APPLICATION_JSON_TYPE + ) + } + + fun isBindingInstalled(bindingId: String): Boolean { + try { + get(BINDINGS_TARGET + SEPARATOR + bindingId + SEPARATOR + CONFIG_TARGET) + LOGGER.debug("Binding[$bindingId] currently not installed!") + return true + } catch (ex: CouldNotPerformException) { + LOGGER.debug("Binding[$bindingId] is already installed.") + return false + } + } + + @Throws(CouldNotPerformException::class) + fun uninstallBindings(bindingId: String) { + post(ADDONS_TARGET + SEPARATOR + bindingId + SEPARATOR + UNINSTALL_TARGET, "", MediaType.APPLICATION_JSON_TYPE) + } + + // ========================================================================================================================================== + // UTIL + // ========================================================================================================================================== + @Throws(CouldNotPerformException::class) + private fun jsonElementToTypedList(jsonElement: JsonElement, clazz: Class): List { + if (jsonElement.isJsonArray) { + return jsonArrayToTypedList(jsonElement.asJsonArray, clazz) + } else { + throw CouldNotPerformException("JsonElement is not a JsonArray and thus cannot be converted to a list") + } + } + + @Throws(CouldNotPerformException::class) + private fun jsonArrayToTypedList(jsonArray: JsonArray, clazz: Class): List { + val result: MutableList = ArrayList() + + for (jsonElement in jsonArray) { + result.add(jsonToClass(jsonElement, clazz)) + } + + return result + } + + @Throws(CouldNotPerformException::class) + private fun jsonToClass(jsonElement: JsonElement, clazz: Class): T { + try { + return gson.fromJson(jsonElement, clazz) + } catch (ex: JsonSyntaxException) { + throw CouldNotPerformException( + "Could not parse jsonElement into object of class[" + clazz.simpleName + "]", + ex + ) + } + } + + @Throws(CouldNotPerformException::class) + override fun testConnection() { + get(INBOX_TARGET, true) + } + + companion object { + const val ITEMS_TARGET: String = "items" + const val LINKS_TARGET: String = "links" + const val THINGS_TARGET: String = "things" + const val INBOX_TARGET: String = "inbox" + const val DISCOVERY_TARGET: String = "discovery" + const val ADDONS_TARGET: String = "addons" + const val ADDONS_BINDING_PREFIX: String = "binding-" + const val INSTALL_TARGET: String = "install" + const val UNINSTALL_TARGET: String = "uninstall" + const val BINDINGS_TARGET: String = "bindings" + const val CONFIG_TARGET: String = "config" + const val SCAN_TARGET: String = "scan" + + private val LOGGER: Logger = LoggerFactory.getLogger(OpenHABRestCommunicator::class.java) + + @JvmStatic + @get:Synchronized + var instance: OpenHABRestCommunicator? = null + get() { + if (field == null) { + try { + field = OpenHABRestCommunicator() + Shutdownable.registerShutdownHook(field) + } catch (ex: InitializationException) { + ExceptionPrinter.printHistory("Could not create OpenHABRestCommunicator", ex, LOGGER) + } catch (ex: CouldNotPerformException) { + // only thrown if instance would be null + } + } + + return field + } + private set + } +} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.java deleted file mode 100644 index 16ef2ccd5d..0000000000 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.java +++ /dev/null @@ -1,479 +0,0 @@ -package org.openbase.bco.device.openhab.communication; - -/*- - * #%L - * BCO Openhab Device Manager - * %% - * Copyright (C) 2015 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import com.google.gson.*; -import jakarta.ws.rs.ProcessingException; -import jakarta.ws.rs.client.Client; -import jakarta.ws.rs.client.ClientBuilder; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.client.WebTarget; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.sse.InboundSseEvent; -import jakarta.ws.rs.sse.SseEventSource; -import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport; -import org.openbase.bco.device.openhab.jp.JPOpenHABURI; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jps.core.JPService; -import org.openbase.jps.exception.JPNotAvailableException; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.exception.*; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.exception.printer.LogLevel; -import org.openbase.jul.extension.type.processing.LabelProcessor; -import org.openbase.jul.extension.type.processing.MetaConfigProcessor; -import org.openbase.jul.iface.Shutdownable; -import org.openbase.jul.pattern.Observable; -import org.openbase.jul.pattern.ObservableImpl; -import org.openbase.jul.pattern.Observer; -import org.openbase.jul.schedule.GlobalScheduledExecutorService; -import org.openbase.jul.schedule.SyncObject; -import org.openbase.type.domotic.state.ConnectionStateType.ConnectionState; -import org.openbase.type.domotic.state.ConnectionStateType.ConnectionState.State; -import org.openbase.type.domotic.unit.UnitConfigType; -import org.openbase.type.domotic.unit.UnitTemplateType; -import org.openbase.type.domotic.unit.gateway.GatewayClassType; -import org.openhab.core.types.CommandDescription; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -public abstract class OpenHABRestConnection implements Shutdownable { - - public static final String SEPARATOR = "/"; - public static final String REST_TARGET = "rest"; - - public static final String APPROVE_TARGET = "approve"; - public static final String EVENTS_TARGET = "events"; - - public static final String TOPIC_KEY = "topic"; - public static final String TOPIC_SEPARATOR = SEPARATOR; - - private static final Logger LOGGER = LoggerFactory.getLogger(OpenHABRestConnection.class); - - private final SyncObject topicObservableMapLock = new SyncObject("topicObservableMapLock"); - private final SyncObject connectionStateSyncLock = new SyncObject("connectionStateSyncLock"); - private final Map> topicObservableMap; - - private final Client restClient; - private final WebTarget restTarget; - private SseEventSource sseSource; - - private boolean shutdownInitiated = false; - - protected final Gson gson; - - private ScheduledFuture connectionTask; - - protected ConnectionState.State openhabConnectionState = State.DISCONNECTED; - - public OpenHABRestConnection() throws InstantiationException { - try { - this.topicObservableMap = new HashMap<>(); - this.gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() { - @Override - public boolean shouldSkipField(FieldAttributes fieldAttributes) { - return false; - } - - @Override - public boolean shouldSkipClass(Class aClass) { - // ignore Command Description because its an interface and can not be serialized without any instance creator. - if (aClass.equals(CommandDescription.class)) { - return true; - } - return false; - } - }).create(); - - this.restClient = ClientBuilder.newClient(); - try { - restClient.register(OAuth2ClientSupport.feature(getToken())); - } catch (NotAvailableException ex) { - LOGGER.warn("Could not retrieve OpenHAB token from gateway config!", ex); - } - this.restTarget = restClient.target(JPService.getProperty(JPOpenHABURI.class).getValue().resolve(SEPARATOR + REST_TARGET)); - this.setConnectState(State.CONNECTING); - } catch (JPNotAvailableException | CouldNotPerformException ex) { - throw new InstantiationException(this, ex); - } - } - - private boolean isTargetReachable() { - try { - testConnection(); - } catch (CouldNotPerformException e) { - if (e.getCause() instanceof ProcessingException) { - return false; - } - } - return true; - } - - protected abstract void testConnection() throws CouldNotPerformException; - - public void waitForConnectionState(final ConnectionState.State connectionState, final long timeout, final TimeUnit timeUnit) throws InterruptedException { - synchronized (connectionStateSyncLock) { - while (getOpenhabConnectionState() != connectionState) { - connectionStateSyncLock.wait(timeUnit.toMillis(timeout)); - } - } - } - - public void waitForConnectionState(final ConnectionState.State connectionState) throws InterruptedException { - synchronized (connectionStateSyncLock) { - while (getOpenhabConnectionState() != connectionState) { - connectionStateSyncLock.wait(); - } - } - } - - private void setConnectState(final ConnectionState.State connectState) { - synchronized (connectionStateSyncLock) { - - // filter non changing states - if (connectState == this.openhabConnectionState) { - return; - } - LOGGER.trace("Openhab Connection State changed to: " + connectState); - - // update state - this.openhabConnectionState = connectState; - - // handle state change - switch (connectState) { - case CONNECTING: - LOGGER.info("Wait for openHAB..."); - try { - connectionTask = GlobalScheduledExecutorService.scheduleWithFixedDelay(() -> { - if (isTargetReachable()) { - // set connected - setConnectState(State.CONNECTED); - - // cleanup own task - connectionTask.cancel(false); - } - }, 0, 15, TimeUnit.SECONDS); - } catch (NotAvailableException | RejectedExecutionException ex) { - // if global executor service is not available we have no chance to connect. - LOGGER.warn("Wait for openHAB...", ex); - setConnectState(State.DISCONNECTED); - } - break; - case CONNECTED: - LOGGER.info("Connection to OpenHAB established."); - initSSE(); - break; - case RECONNECTING: - LOGGER.warn("Connection to OpenHAB lost!"); - resetConnection(); - setConnectState(State.CONNECTING); - break; - case DISCONNECTED: - LOGGER.info("Connection to OpenHAB closed."); - resetConnection(); - break; - } - - // notify state change - connectionStateSyncLock.notifyAll(); - - // apply next state if required - switch (connectState) { - case RECONNECTING: - setConnectState(State.CONNECTING); - break; - } - } - } - - private void initSSE() { - // activate sse source if not already done - if (sseSource != null) { - LOGGER.warn("SSE already initialized!"); - return; - } - - final WebTarget webTarget = restTarget.path(EVENTS_TARGET); - sseSource = SseEventSource.target(webTarget).reconnectingEvery(15, TimeUnit.SECONDS).build(); - sseSource.open(); - - final Consumer evenConsumer = inboundSseEvent -> { - // dispatch event - try { - final JsonObject payload = JsonParser.parseString(inboundSseEvent.readData()).getAsJsonObject(); - for (Entry> topicObserverEntry : topicObservableMap.entrySet()) { - try { - if (payload.get(TOPIC_KEY).getAsString().matches(topicObserverEntry.getKey())) { - topicObserverEntry.getValue().notifyObservers(payload); - } - } catch (Exception ex) { - ExceptionPrinter.printHistory(new CouldNotPerformException("Could not notify listeners on topic[" + topicObserverEntry.getKey() + "]", ex), LOGGER); - } - } - } catch (Exception ex) { - ExceptionPrinter.printHistory(new CouldNotPerformException("Could not handle SSE payload!", ex), LOGGER); - } - }; - - final Consumer errorHandler = ex -> { - ExceptionPrinter.printHistory("Openhab connection error detected!", ex, LOGGER, LogLevel.DEBUG); - checkConnectionState(); - }; - - final Runnable reconnectHandler = () -> { - setConnectState(State.RECONNECTING); - }; - - sseSource.register(evenConsumer, errorHandler, reconnectHandler); - } - - public State getOpenhabConnectionState() { - return openhabConnectionState; - } - - public void checkConnectionState() { - synchronized (connectionStateSyncLock) { - // only validate if connected - if (!isConnected()) { - return; - } - - // if not reachable init a reconnect - if (!isTargetReachable()) { - setConnectState(State.RECONNECTING); - } - } - } - - public boolean isConnected() { - return getOpenhabConnectionState() == State.CONNECTED; - } - - public void addSSEObserver(Observer observer) { - addSSEObserver(observer, ""); - } - - public void addSSEObserver(final Observer observer, final String topicRegex) { - synchronized (topicObservableMapLock) { - if (topicObservableMap.containsKey(topicRegex)) { - topicObservableMap.get(topicRegex).addObserver(observer); - return; - } - - final ObservableImpl observable = new ObservableImpl<>(this); - observable.addObserver(observer); - topicObservableMap.put(topicRegex, observable); - } - } - - public void removeSSEObserver(Observer observer) { - removeSSEObserver(observer, ""); - } - - public void removeSSEObserver(Observer observer, final String topicFilter) { - synchronized (topicObservableMapLock) { - if (topicObservableMap.containsKey(topicFilter)) { - topicObservableMap.get(topicFilter).removeObserver(observer); - } - } - } - - private void resetConnection() { - // cancel ongoing connection task - if (!connectionTask.isDone()) { - connectionTask.cancel(false); - } - - // close sse - if (sseSource != null) { - sseSource.close(); - sseSource = null; - } - } - - public void validateConnection() throws CouldNotPerformException { - if (!isConnected()) { - throw new InvalidStateException("Openhab not reachable yet!"); - } - } - - private String validateResponse(final Response response) throws CouldNotPerformException, ProcessingException { - return validateResponse(response, false); - } - - private String validateResponse(final Response response, final boolean skipConnectionValidation) throws CouldNotPerformException, ProcessingException { - final String result = response.readEntity(String.class); - - if (response.getStatus() == 200 || response.getStatus() == 201 || response.getStatus() == 202) { - return result; - } else if (response.getStatus() == 404) { - if (!skipConnectionValidation) { - checkConnectionState(); - } - throw new NotAvailableException("URL"); - } else if (response.getStatus() == 503) { - if (!skipConnectionValidation) { - checkConnectionState(); - } - // throw a processing exception to indicate that openHAB is still not fully started, this is used to wait for openHAB - throw new ProcessingException("OpenHAB server not ready"); - } else { - throw new CouldNotPerformException("Response returned with ErrorCode[" + response.getStatus() + "], Result[" + result + "] and ErrorMessage[" + response.getStatusInfo().getReasonPhrase() + "]"); - } - } - - protected String get(final String target) throws CouldNotPerformException { - return get(target, false); - } - - protected String get(final String target, final boolean skipValidation) throws CouldNotPerformException { - try { - - // handle validation - if (!skipValidation) { - validateConnection(); - } - - final WebTarget webTarget = restTarget.path(target); - final Response response = webTarget.request().get(); - - return validateResponse(response, skipValidation); - } catch (CouldNotPerformException | ProcessingException ex) { - if (isShutdownInitiated()) { - ExceptionProcessor.setInitialCause(ex, new ShutdownInProgressException(this)); - } - throw new CouldNotPerformException("Could not get sub-URL[" + target + "]", ex); - } - } - - protected String delete(final String target) throws CouldNotPerformException { - try { - validateConnection(); - final WebTarget webTarget = restTarget.path(target); - final Response response = webTarget.request().delete(); - - return validateResponse(response); - } catch (CouldNotPerformException | ProcessingException ex) { - if (isShutdownInitiated()) { - ExceptionProcessor.setInitialCause(ex, new ShutdownInProgressException(this)); - } - throw new CouldNotPerformException("Could not delete sub-URL[" + target + "]", ex); - } - } - - protected String putJson(final String target, final Object value) throws CouldNotPerformException { - return put(target, gson.toJson(value), MediaType.APPLICATION_JSON_TYPE); - } - - protected String put(final String target, final String value, final MediaType mediaType) throws CouldNotPerformException { - try { - validateConnection(); - final WebTarget webTarget = restTarget.path(target); - final Response response = webTarget.request().put(Entity.entity(value, mediaType)); - - return validateResponse(response); - } catch (CouldNotPerformException | ProcessingException ex) { - if (isShutdownInitiated()) { - ExceptionProcessor.setInitialCause(ex, new ShutdownInProgressException(this)); - } - throw new CouldNotPerformException("Could not put value[" + value + "] on sub-URL[" + target + "]", ex); - } - } - - protected String postJson(final String target, final Object value) throws CouldNotPerformException { - return post(target, gson.toJson(value), MediaType.APPLICATION_JSON_TYPE); - } - - protected String post(final String target, final String value, final MediaType mediaType) throws CouldNotPerformException { - try { - validateConnection(); - final WebTarget webTarget = restTarget.path(target); - final Response response = webTarget.request().post(Entity.entity(value, mediaType)); - - return validateResponse(response); - } catch (CouldNotPerformException | ProcessingException ex) { - if (isShutdownInitiated()) { - ExceptionProcessor.setInitialCause(ex, new ShutdownInProgressException(this)); - } - throw new CouldNotPerformException("Could not post Value[" + value + "] of MediaType[" + mediaType + "] on sub-URL[" + target + "]", ex); - } - } - - public boolean isShutdownInitiated() { - return shutdownInitiated; - } - - @Override - public void shutdown() { - - // prepare shutdown - shutdownInitiated = true; - setConnectState(State.DISCONNECTED); - - // stop rest service - restClient.close(); - - // stop sse service - synchronized (topicObservableMapLock) { - for (final Observable jsonObjectObservable : topicObservableMap.values()) { - jsonObjectObservable.shutdown(); - } - - topicObservableMap.clear(); - resetConnection(); - } - } - - - private final String OPENHAB_GATEWAY_CLASS_LABEL = "OpenHAB"; - private final String META_CONFIG_TOKEN_KEY = "TOKEN"; - - private final GatewayClassType.GatewayClass findOpenHABGatewayClass() throws CouldNotPerformException { - for (GatewayClassType.GatewayClass gatewayClass : Registries.getClassRegistry().getGatewayClasses()) { - if (LabelProcessor.contains(gatewayClass.getLabel(), OPENHAB_GATEWAY_CLASS_LABEL)) { - return gatewayClass; - } - } - - throw new NotAvailableException("OpenHAB Gateway Class"); - } - - private String getToken() throws CouldNotPerformException { - final GatewayClassType.GatewayClass openHABGatewayClass = findOpenHABGatewayClass(); - - for (UnitConfigType.UnitConfig unitConfig : Registries.getUnitRegistry().getUnitConfigsByUnitType(UnitTemplateType.UnitTemplate.UnitType.GATEWAY)) { - if (unitConfig.getGatewayConfig().getGatewayClassId().equals(openHABGatewayClass.getId())) { - return MetaConfigProcessor.getValue(unitConfig.getMetaConfig(), META_CONFIG_TOKEN_KEY); - } - } - throw new NotAvailableException("token"); - } -} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.kt b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.kt new file mode 100644 index 0000000000..4cd02a14b1 --- /dev/null +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/communication/OpenHABRestConnection.kt @@ -0,0 +1,473 @@ +package org.openbase.bco.device.openhab.communication + +import com.google.gson.* +import jakarta.ws.rs.ProcessingException +import jakarta.ws.rs.client.Client +import jakarta.ws.rs.client.ClientBuilder +import jakarta.ws.rs.client.Entity +import jakarta.ws.rs.client.WebTarget +import jakarta.ws.rs.core.MediaType +import jakarta.ws.rs.core.Response +import jakarta.ws.rs.sse.InboundSseEvent +import jakarta.ws.rs.sse.SseEventSource +import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport +import org.openbase.bco.device.openhab.jp.JPOpenHABURI +import org.openbase.bco.registry.remote.Registries +import org.openbase.jps.core.JPService +import org.openbase.jps.exception.JPNotAvailableException +import org.openbase.jul.exception.* +import org.openbase.jul.exception.ExceptionProcessor.setInitialCause +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.exception.printer.LogLevel +import org.openbase.jul.extension.type.processing.LabelProcessor.contains +import org.openbase.jul.extension.type.processing.MetaConfigProcessor +import org.openbase.jul.iface.Shutdownable +import org.openbase.jul.pattern.ObservableImpl +import org.openbase.jul.pattern.Observer +import org.openbase.jul.schedule.GlobalScheduledExecutorService +import org.openbase.type.domotic.state.ConnectionStateType +import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType +import org.openbase.type.domotic.unit.gateway.GatewayClassType +import org.openhab.core.types.CommandDescription +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.net.ConnectException +import java.util.concurrent.RejectedExecutionException +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock +import java.util.function.Consumer +import kotlin.concurrent.withLock + +abstract class OpenHABRestConnection : Shutdownable { + private val topicObservableMapLock = ReentrantLock() + private val connectionStateLock = ReentrantLock() + private val connectionStateCondition = connectionStateLock.newCondition() + private var topicObservableMap: MutableMap> + + private var restClient: Client + private var restTarget: WebTarget + private var sseSource: SseEventSource? = null + + var isShutdownInitiated: Boolean = false + private set + + @JvmField + protected var gson: Gson + + private var connectionTask: ScheduledFuture<*>? = null + + var openhabConnectionState: ConnectionStateType.ConnectionState.State = + ConnectionStateType.ConnectionState.State.DISCONNECTED + protected set + + init { + try { + this.topicObservableMap = HashMap() + this.gson = GsonBuilder().setExclusionStrategies(object : ExclusionStrategy { + override fun shouldSkipField(fieldAttributes: FieldAttributes): Boolean { + return false + } + + override fun shouldSkipClass(aClass: Class<*>): Boolean { + // ignore Command Description because its an interface and can not be serialized without any instance creator. + return aClass == CommandDescription::class.java + } + }).create() + + this.restClient = ClientBuilder.newClient() + try { + restClient.register(OAuth2ClientSupport.feature(token)) + } catch (ex: NotAvailableException) { + LOGGER.warn("Could not retrieve OpenHAB token from gateway config!", ex) + } + this.restTarget = + restClient.target(JPService.getProperty(JPOpenHABURI::class.java).value.resolve(SEPARATOR + REST_TARGET)) + this.setConnectState(ConnectionStateType.ConnectionState.State.CONNECTING) + } catch (ex: JPNotAvailableException) { + throw InstantiationException(this, ex) + } catch (ex: CouldNotPerformException) { + throw InstantiationException(this, ex) + } + } + + private val isTargetReachable: Boolean + get() = runCatching { testConnection() }.isSuccess + + @Throws(CouldNotPerformException::class) + protected abstract fun testConnection() + + @Throws(InterruptedException::class) + fun waitForConnectionState( + connectionState: ConnectionStateType.ConnectionState.State, + timeout: Long, + timeUnit: TimeUnit, + ) { + connectionStateLock.withLock { + while (openhabConnectionState != connectionState) { + connectionStateCondition.await(timeout, timeUnit) + } + } + } + + @Throws(InterruptedException::class) + fun waitForConnectionState(connectionState: ConnectionStateType.ConnectionState.State) { + connectionStateLock.withLock { + while (openhabConnectionState != connectionState) { + connectionStateCondition.await() + } + } + } + + private fun setConnectState(connectState: ConnectionStateType.ConnectionState.State) { + connectionStateLock.withLock { + // filter non changing states + if (connectState == this.openhabConnectionState) { + return + } + LOGGER.trace("Openhab Connection State changed to: {}", connectState) + + // update state + this.openhabConnectionState = connectState + + when (connectState) { + ConnectionStateType.ConnectionState.State.CONNECTING -> { + LOGGER.info("Wait for openHAB...") + try { + connectionTask?.cancel(true) + connectionTask = GlobalScheduledExecutorService.scheduleWithFixedDelay({ + if (isTargetReachable) { + // set connected + setConnectState(ConnectionStateType.ConnectionState.State.CONNECTED) + + // cleanup own task + connectionTask?.cancel(false) + } + }, 0, 15, TimeUnit.SECONDS) + } catch (ex: NotAvailableException) { + // if global executor service is not available we have no chance to connect. + LOGGER.warn("Wait for openHAB...", ex) + setConnectState(ConnectionStateType.ConnectionState.State.DISCONNECTED) + } catch (ex: RejectedExecutionException) { + LOGGER.warn("Wait for openHAB...", ex) + setConnectState(ConnectionStateType.ConnectionState.State.DISCONNECTED) + } + } + + ConnectionStateType.ConnectionState.State.CONNECTED -> { + LOGGER.info("Connection to OpenHAB established.") + initSSE() + } + + ConnectionStateType.ConnectionState.State.RECONNECTING -> { + LOGGER.warn("Connection to OpenHAB lost!") + resetConnection() + setConnectState(ConnectionStateType.ConnectionState.State.CONNECTING) + } + + ConnectionStateType.ConnectionState.State.DISCONNECTED -> { + LOGGER.info("Connection to OpenHAB closed.") + resetConnection() + } + + else -> { + LOGGER.warn("Unknown connection state: $connectState") + } + } + // notify state change + connectionStateCondition.signalAll() + if (connectState == ConnectionStateType.ConnectionState.State.RECONNECTING) { + setConnectState(ConnectionStateType.ConnectionState.State.CONNECTING) + } + } + } + + private fun initSSE() { + // activate sse source if not already done + if (sseSource != null) { + LOGGER.warn("SSE already initialized!") + return + } + + sseSource = SseEventSource + .target(restTarget.path(EVENTS_TARGET)) + .reconnectingEvery(15, TimeUnit.SECONDS) + .build() + .also { it.open() } + + + val evenConsumer = Consumer { inboundSseEvent: InboundSseEvent -> + // dispatch event + try { + val payload = JsonParser.parseString(inboundSseEvent.readData()).asJsonObject + for ((key, value) in topicObservableMap) { + try { + payload[TOPIC_KEY] + ?.asString + ?.takeIf { it.matches(key.toRegex()) } + ?.run { value.notifyObservers(payload) } + } catch (ex: Exception) { + ExceptionPrinter.printHistory( + CouldNotPerformException( + "Could not notify listeners on topic[$key]", + ex + ), LOGGER + ) + } + } + } catch (ex: Exception) { + ExceptionPrinter.printHistory(CouldNotPerformException("Could not handle SSE payload!", ex), LOGGER) + } + } + + val errorHandler = Consumer { ex: Throwable -> + ExceptionPrinter.printHistory("Openhab connection error detected!", ex, LOGGER, LogLevel.DEBUG) + checkConnectionState() + } + + val reconnectHandler = Runnable { + setConnectState(ConnectionStateType.ConnectionState.State.RECONNECTING) + } + + sseSource?.register(evenConsumer, errorHandler, reconnectHandler) + } + + private fun checkConnectionState() { + connectionStateLock.withLock { + // only validate if connected + if (!isConnected) { + return + } + + // if not reachable init a reconnect + if (!isTargetReachable) { + setConnectState(ConnectionStateType.ConnectionState.State.RECONNECTING) + } + } + } + + val isConnected: Boolean + get() = openhabConnectionState == ConnectionStateType.ConnectionState.State.CONNECTED + + @JvmOverloads + fun addSSEObserver(observer: Observer, topicRegex: String = "") { + topicObservableMapLock.withLock { + topicObservableMap + .getOrPut(topicRegex) { ObservableImpl(this) } + .addObserver(observer) + } + } + + @JvmOverloads + fun removeSSEObserver(observer: Observer, topicFilter: String = "") { + topicObservableMapLock.withLock { + topicObservableMap[topicFilter]?.removeObserver(observer) + } + } + + private fun resetConnection() { + // cancel ongoing connection task + connectionTask + ?.takeIf { !it.isDone } + ?.cancel(false) + + // close sse + sseSource + ?.close() + ?.also { sseSource = null } + } + + @Throws(CouldNotPerformException::class) + fun validateConnection() { + if (!isConnected) { + throw InvalidStateException("Openhab not reachable yet!") + } + } + + @Throws(CouldNotPerformException::class, ProcessingException::class) + private fun validateResponse(response: Response, skipConnectionValidation: Boolean = false): String = + response.readEntity(String::class.java).let { result -> + when (response.status) { + 200, 201, 202 -> { + result + } + + 404 -> { + if (!skipConnectionValidation) { + checkConnectionState() + } + throw NotAvailableException("URL") + } + + 503 -> { + if (!skipConnectionValidation) { + checkConnectionState() + } + // throw a processing exception to indicate that openHAB is still not fully started, this is used to wait for openHAB + throw ProcessingException("OpenHAB server not ready") + } + + else -> { + throw CouldNotPerformException("Response returned with ErrorCode[" + response.status + "], Result[" + result + "] and ErrorMessage[" + response.statusInfo.reasonPhrase + "]") + } + } + } + + @Throws(CouldNotPerformException::class) + protected fun get(target: String, skipValidation: Boolean = false): String { + try { + // handle validation + + if (!skipValidation) { + validateConnection() + } + + val webTarget = restTarget.path(target) + val response = webTarget.request().get() + + return validateResponse(response, skipValidation) + } catch (ex: CouldNotPerformException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException("Could not get sub-URL[$target]", ex) + } catch (ex: ProcessingException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException("Could not get sub-URL[$target]", ex) + } + } + + @Throws(CouldNotPerformException::class) + protected fun delete(target: String): String { + try { + validateConnection() + val webTarget = restTarget.path(target) + val response = webTarget.request().delete() + + return validateResponse(response) + } catch (ex: CouldNotPerformException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException("Could not delete sub-URL[$target]", ex) + } catch (ex: ProcessingException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException("Could not delete sub-URL[$target]", ex) + } + } + + @Throws(CouldNotPerformException::class) + protected fun putJson(target: String, value: Any?): String { + return put(target, gson.toJson(value), MediaType.APPLICATION_JSON_TYPE) + } + + @Throws(CouldNotPerformException::class) + protected fun put(target: String, value: String, mediaType: MediaType?): String { + try { + validateConnection() + val webTarget = restTarget.path(target) + val response = webTarget.request().put(Entity.entity(value, mediaType)) + + return validateResponse(response) + } catch (ex: CouldNotPerformException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException("Could not put value[$value] on sub-URL[$target]", ex) + } catch (ex: ProcessingException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException("Could not put value[$value] on sub-URL[$target]", ex) + } catch (ex: ConnectException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException("Could not put value[$value] on sub-URL[$target]", ex) + } + } + + @Throws(CouldNotPerformException::class) + protected fun postJson(target: String, value: Any?): String { + return post(target, gson.toJson(value), MediaType.APPLICATION_JSON_TYPE) + } + + @Throws(CouldNotPerformException::class) + protected fun post(target: String, value: String, mediaType: MediaType): String { + try { + validateConnection() + val webTarget = restTarget.path(target) + val response = webTarget.request().post(Entity.entity(value, mediaType)) + + return validateResponse(response) + } catch (ex: CouldNotPerformException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException( + "Could not post Value[$value] of MediaType[$mediaType] on sub-URL[$target]", + ex + ) + } catch (ex: ProcessingException) { + if (isShutdownInitiated) { + setInitialCause(ex, ShutdownInProgressException(this)) + } + throw CouldNotPerformException( + "Could not post Value[$value] of MediaType[$mediaType] on sub-URL[$target]", + ex + ) + } + } + + override fun shutdown() { + // prepare shutdown + + isShutdownInitiated = true + setConnectState(ConnectionStateType.ConnectionState.State.DISCONNECTED) + + // stop rest service + restClient.close() + + // stop sse service + topicObservableMapLock.withLock { + topicObservableMap.values.forEach { jsonObjectObservable -> + jsonObjectObservable.shutdown() + } + topicObservableMap.clear() + resetConnection() + } + } + + @Throws(CouldNotPerformException::class) + private fun findOpenHABGatewayClass(): GatewayClassType.GatewayClass? = + Registries.getClassRegistry().gatewayClasses.find { contains(it.label, OPENHAB_GATEWAY_CLASS_LABEL) } + + private val token: String? + get() = findOpenHABGatewayClass()?.let { openHABGatewayClass -> + Registries.getUnitRegistry() + .getUnitConfigsByUnitType(UnitType.GATEWAY) + .find { it.gatewayConfig.gatewayClassId == openHABGatewayClass.id } + ?.let { MetaConfigProcessor.getValue(it.metaConfig, META_CONFIG_TOKEN_KEY) } + } + + companion object { + + private const val OPENHAB_GATEWAY_CLASS_LABEL = "OpenHAB" + private const val META_CONFIG_TOKEN_KEY = "TOKEN" + + const val SEPARATOR: String = "/" + const val REST_TARGET: String = "rest" + + const val APPROVE_TARGET: String = "approve" + const val EVENTS_TARGET: String = "events" + + const val TOPIC_KEY: String = "topic" + const val TOPIC_SEPARATOR: String = SEPARATOR + + private val LOGGER: Logger = LoggerFactory.getLogger(OpenHABRestConnection::class.java) + } +} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABConfiguration.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABConfiguration.java deleted file mode 100644 index 4340dc83ae..0000000000 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABConfiguration.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.openbase.bco.device.openhab.jp; - -/*- - * #%L - * BCO Openhab Device Manager - * %% - * Copyright (C) 2015 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.jps.core.JPService; -import org.openbase.jps.exception.JPNotAvailableException; -import org.openbase.jps.preset.AbstractJPDirectory; -import org.openbase.jps.tools.FileHandler; - -import java.io.File; - -/** - * @author Divine Threepwood - */ -public class JPOpenHABConfiguration extends AbstractJPDirectory { - - public static final String[] COMMAND_IDENTIFIERS = {"--openhab-config"}; - public static final String SYSTEM_VARIABLE_OPENHAB_CONF = "OPENHAB_CONF"; - public static final String DEFAULT_PATH = "/etc/openhab2"; - - public JPOpenHABConfiguration() { - super(COMMAND_IDENTIFIERS, FileHandler.ExistenceHandling.Must, FileHandler.AutoMode.Off); - } - - @Override - protected File getPropertyDefaultValue() throws JPNotAvailableException { - - // use system variable if defined - String systemDefinedPath = System.getenv(SYSTEM_VARIABLE_OPENHAB_CONF); - if (systemDefinedPath != null) { - return new File(systemDefinedPath); - } - - return new File(DEFAULT_PATH); - } - - @Override - public String getDescription() { - return "Defines the openhab configuration directory. This property is based on the system variable " + SYSTEM_VARIABLE_OPENHAB_CONF; - } - -} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABConfiguration.kt b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABConfiguration.kt new file mode 100644 index 0000000000..5678799ec6 --- /dev/null +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABConfiguration.kt @@ -0,0 +1,34 @@ +package org.openbase.bco.device.openhab.jp + +import org.openbase.jps.exception.JPNotAvailableException +import org.openbase.jps.preset.AbstractJPDirectory +import org.openbase.jps.tools.FileHandler +import java.io.File + +/** + * @author [Divine Threepwood](mailto:divine@openbase.org) + */ +class JPOpenHABConfiguration : + AbstractJPDirectory(COMMAND_IDENTIFIERS, FileHandler.ExistenceHandling.Must, FileHandler.AutoMode.Off) { + @Throws(JPNotAvailableException::class) + override fun getPropertyDefaultValue(): File { + // use system variable if defined + + val systemDefinedPath = System.getenv(SYSTEM_VARIABLE_OPENHAB_CONF) + if (systemDefinedPath != null) { + return File(systemDefinedPath) + } + + return File(DEFAULT_PATH) + } + + override fun getDescription(): String { + return "Defines the openhab configuration directory. This property is based on the system variable " + SYSTEM_VARIABLE_OPENHAB_CONF + } + + companion object { + val COMMAND_IDENTIFIERS: Array = arrayOf("--openhab-config") + const val SYSTEM_VARIABLE_OPENHAB_CONF: String = "OPENHAB_CONF" + const val DEFAULT_PATH: String = "/etc/openhab2" + } +} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABSitemap.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABSitemap.java deleted file mode 100644 index add2497857..0000000000 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABSitemap.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.openbase.bco.device.openhab.jp; - -/*- - * #%L - * BCO Openhab Device Manager - * %% - * Copyright (C) 2015 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.jps.core.JPService; -import org.openbase.jps.exception.JPNotAvailableException; -import org.openbase.jps.preset.AbstractJPDirectory; -import org.openbase.jps.preset.AbstractJPFile; -import org.openbase.jps.tools.FileHandler; - -import java.io.File; - -public class JPOpenHABSitemap extends AbstractJPDirectory { - private static final String[] COMMAND_IDENTIFIERS = {"--sitemap"}; - - public JPOpenHABSitemap() { - super(COMMAND_IDENTIFIERS, FileHandler.ExistenceHandling.Must, FileHandler.AutoMode.Off); - registerDependingProperty(JPOpenHABConfiguration.class); - } - - @Override - protected File getPropertyDefaultValue() throws JPNotAvailableException { - return new File(JPService.getProperty(JPOpenHABConfiguration.class).getValue(), "sitemaps"); - } - - @Override - public String getDescription() { - return "Defines the path to the openhab sitemap directory."; - } - -} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABSitemap.kt b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABSitemap.kt new file mode 100644 index 0000000000..8683203b59 --- /dev/null +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABSitemap.kt @@ -0,0 +1,27 @@ +package org.openbase.bco.device.openhab.jp + +import org.openbase.jps.core.JPService +import org.openbase.jps.exception.JPNotAvailableException +import org.openbase.jps.preset.AbstractJPDirectory +import org.openbase.jps.tools.FileHandler +import java.io.File + +class JPOpenHABSitemap : + AbstractJPDirectory(COMMAND_IDENTIFIERS, FileHandler.ExistenceHandling.Must, FileHandler.AutoMode.Off) { + init { + registerDependingProperty(JPOpenHABConfiguration::class.java) + } + + @Throws(JPNotAvailableException::class) + override fun getPropertyDefaultValue(): File { + return File(JPService.getProperty(JPOpenHABConfiguration::class.java).value, "sitemaps") + } + + override fun getDescription(): String { + return "Defines the path to the openhab sitemap directory." + } + + companion object { + private val COMMAND_IDENTIFIERS = arrayOf("--sitemap") + } +} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java deleted file mode 100644 index f579407fe0..0000000000 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.openbase.bco.device.openhab.jp; - -/*- - * #%L - * BCO Openhab Device Manager - * %% - * Copyright (C) 2015 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.jps.core.AbstractJavaProperty; - -import java.net.URI; -import java.util.List; - -/** - * @author Tamino Huxohl - */ -public class JPOpenHABURI extends AbstractJavaProperty { - - private static final String[] ARGUMENT_IDENTIFIERS = {"URI"}; - private static final String[] COMMAND_IDENTIFIERS = {"--openhab-url", "--openhab-uri", "--uri"}; - - private static final String DEFAULT_URI = "http://openhab:8080"; - - public static final String SYSTEM_VARIABLE_OPENHAB_PORT = "OPENHAB_HTTP_PORT"; - - public JPOpenHABURI() { - super(COMMAND_IDENTIFIERS); - } - - @Override - protected String[] generateArgumentIdentifiers() { - return ARGUMENT_IDENTIFIERS; - } - - @Override - protected URI getPropertyDefaultValue() { - // URI.create does not throw an exception which is fine for the default value - - // use system variable if defined - String systemDefinedPort = System.getenv(SYSTEM_VARIABLE_OPENHAB_PORT); - if (systemDefinedPort != null) { - return URI.create("http://localhost:" + systemDefinedPort); - } - - return URI.create(DEFAULT_URI); - } - - @Override - protected URI parse(final List list) throws Exception { - String uri = getOneArgumentResult(); - // make sure that the uri always starts with http ot https - if (!uri.startsWith("http")) { - uri = "http://" + uri; - } - // create a new uri, this will throw an exception if the uri is not valid - return new URI(uri); - } - - @Override - public String getDescription() { - return "Define the URI of the OpenHAB 2 instance this app should connect to."; - } -} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.kt b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.kt new file mode 100644 index 0000000000..762ecb0e34 --- /dev/null +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/jp/JPOpenHABURI.kt @@ -0,0 +1,50 @@ +package org.openbase.bco.device.openhab.jp + +import org.openbase.jps.core.AbstractJavaProperty +import java.net.URI + +/** + * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +class JPOpenHABURI : AbstractJavaProperty(COMMAND_IDENTIFIERS) { + override fun generateArgumentIdentifiers(): Array { + return ARGUMENT_IDENTIFIERS + } + + override fun getPropertyDefaultValue(): URI { + // URI.create does not throw an exception which is fine for the default value + + // use system variable if defined + + val systemDefinedPort = System.getenv(SYSTEM_VARIABLE_OPENHAB_PORT) + if (systemDefinedPort != null) { + return URI.create("http://localhost:$systemDefinedPort") + } + + return URI.create(DEFAULT_URI) + } + + @Throws(Exception::class) + override fun parse(list: List): URI { + var uri = oneArgumentResult + // make sure that the uri always starts with http ot https + if (!uri.startsWith("http")) { + uri = "http://$uri" + } + // create a new uri, this will throw an exception if the uri is not valid + return URI(uri) + } + + override fun getDescription(): String { + return "Define the URI of the OpenHAB 2 instance this app should connect to." + } + + companion object { + private val ARGUMENT_IDENTIFIERS = arrayOf("URI") + private val COMMAND_IDENTIFIERS = arrayOf("--openhab-url", "--openhab-uri", "--uri") + + private const val DEFAULT_URI = "http://openhab:8080" + + const val SYSTEM_VARIABLE_OPENHAB_PORT: String = "OPENHAB_HTTP_PORT" + } +} diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/BlindStateServiceImpl.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/BlindStateServiceImpl.java index 09c6e05659..bedc53054b 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/BlindStateServiceImpl.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/BlindStateServiceImpl.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,7 +24,6 @@ import org.openbase.bco.dal.lib.layer.service.operation.BlindStateOperationService; import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; @@ -32,8 +31,6 @@ import java.util.concurrent.Future; -// todo: class possibly not used anymore, please validate and cleanup. -@Deprecated public class BlindStateServiceImpl> extends OpenHABService implements BlindStateOperationService { public BlindStateServiceImpl(ST unit) throws InstantiationException { diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/BrightnessStateServiceImpl.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/BrightnessStateServiceImpl.java index f12773be1b..55f323b12b 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/BrightnessStateServiceImpl.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/BrightnessStateServiceImpl.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,7 +24,6 @@ import org.openbase.bco.dal.lib.layer.service.operation.BrightnessStateOperationService; import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; @@ -32,8 +31,6 @@ import java.util.concurrent.Future; -// todo: class possibly not used anymore, please validate and cleanup. -@Deprecated public class BrightnessStateServiceImpl> extends OpenHABService implements BrightnessStateOperationService { public BrightnessStateServiceImpl(final ST unit) throws InstantiationException { diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/ColorStateServiceImpl.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/ColorStateServiceImpl.java index 3debef69b3..42a932d213 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/ColorStateServiceImpl.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/ColorStateServiceImpl.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,19 +24,16 @@ import org.openbase.bco.dal.lib.layer.service.operation.ColorStateOperationService; import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; +import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; +import org.openbase.type.domotic.state.ColorStateType.ColorState; import org.openbase.type.vision.ColorType.Color; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; -import org.openbase.type.domotic.state.ColorStateType.ColorState; import java.util.concurrent.Future; -// todo: class possibly not used anymore, please validate and cleanup. -@Deprecated public class ColorStateServiceImpl> extends OpenHABService implements ColorStateOperationService { private static final Logger LOGGER = LoggerFactory.getLogger(ColorStateServiceImpl.class); diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/PowerStateServiceImpl.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/PowerStateServiceImpl.java index f636448083..68e5e85668 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/PowerStateServiceImpl.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/PowerStateServiceImpl.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,7 +24,6 @@ import org.openbase.bco.dal.lib.layer.service.operation.PowerStateOperationService; import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; @@ -32,9 +31,6 @@ import java.util.concurrent.Future; -// todo: class possibly not used anymore, please validate and cleanup. -// otherwise, implement CustomOpenhabService and handle more generic and check why activation state was not required? -@Deprecated public class PowerStateServiceImpl> extends OpenHABService implements PowerStateOperationService { public PowerStateServiceImpl(final ST unit) throws InstantiationException { diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/StandbyStateServiceImpl.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/StandbyStateServiceImpl.java index e1d11ce227..22e10e6c81 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/StandbyStateServiceImpl.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/StandbyStateServiceImpl.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,7 +24,6 @@ import org.openbase.bco.dal.lib.layer.service.operation.StandbyStateOperationService; import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; @@ -32,8 +31,6 @@ import java.util.concurrent.Future; -// todo: class possibly not used anymore, please validate and cleanup. -@Deprecated public class StandbyStateServiceImpl> extends OpenHABService implements StandbyStateOperationService { public StandbyStateServiceImpl(ST unit) throws InstantiationException { diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/TargetTemperatureStateServiceImpl.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/TargetTemperatureStateServiceImpl.java index e8dbe54f42..5fa8fccc47 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/TargetTemperatureStateServiceImpl.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/manager/service/TargetTemperatureStateServiceImpl.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -24,7 +24,6 @@ import org.openbase.bco.dal.lib.layer.service.operation.TargetTemperatureStateOperationService; import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; @@ -32,8 +31,6 @@ import java.util.concurrent.Future; -// todo: class possibly not used anymore, please validate and cleanup. -@Deprecated public class TargetTemperatureStateServiceImpl> extends OpenHABService implements TargetTemperatureStateOperationService { public TargetTemperatureStateServiceImpl(ST unit) throws InstantiationException { diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/OpenHABConfigSynchronizer.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/OpenHABConfigSynchronizer.java index 21b6c0de08..bce68b50bc 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/OpenHABConfigSynchronizer.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/OpenHABConfigSynchronizer.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -39,7 +39,7 @@ public class OpenHABConfigSynchronizer implements Launchable, VoidInitiali private static final Logger LOGGER = LoggerFactory.getLogger(OpenHABConfigSynchronizer.class); - private final SyncObject synchronizationLock = new SyncObject("SyncLock"); + private final static SyncObject synchronizationLock = new SyncObject("SyncLock"); private final InboxApprover inboxApprover; diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/InboxApprover.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/InboxApprover.java index 8bb65f4e8d..fcfc78b2b0 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/InboxApprover.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/InboxApprover.java @@ -84,7 +84,7 @@ public InboxApprover() { // ignore not supported things } catch (NotAvailableException ex) { // no matching gateway or device class found so ignore it for now - ExceptionPrinter.printHistory("Ignore discovered thing[" + discoveryResultDTO.thingUID + "].", ex, LOGGER, LogLevel.WARN); + ExceptionPrinter.printHistory("Ignore discovered thing[" + discoveryResultDTO.thingUID + "].", ex, LOGGER, LogLevel.INFO); } }; } diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ItemUnitSynchronization.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ItemUnitSynchronization.java index 9ee6ebd6f1..c648aef2aa 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ItemUnitSynchronization.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ItemUnitSynchronization.java @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * @author Tamino Huxohl @@ -51,7 +52,7 @@ public ItemUnitSynchronization(final SyncObject synchronizationLock) throws Inst @Override public void activate() throws CouldNotPerformException, InterruptedException { - OpenHABRestCommunicator.getInstance().waitForConnectionState(ConnectionState.State.CONNECTED); + Objects.requireNonNull(OpenHABRestCommunicator.getInstance()).waitForConnectionState(ConnectionState.State.CONNECTED); super.activate(); } diff --git a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingUnitSynchronization.java b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingUnitSynchronization.java index 2d0a0cb97c..af04fe1491 100644 --- a/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingUnitSynchronization.java +++ b/module/device/openhab/src/main/java/org/openbase/bco/device/openhab/registry/synchronizer/ThingUnitSynchronization.java @@ -38,11 +38,11 @@ import org.openhab.core.io.rest.core.thing.EnrichedThingDTO; import org.openhab.core.items.dto.ItemDTO; import org.openhab.core.thing.dto.ChannelDTO; -import org.openhab.core.thing.dto.ThingDTO; import org.openhab.core.thing.link.dto.ItemChannelLinkDTO; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.ExecutionException; /** @@ -63,7 +63,7 @@ public ThingUnitSynchronization(final SyncObject synchronizationLock) throws Ins @Override public void activate() throws CouldNotPerformException, InterruptedException { - OpenHABRestCommunicator.getInstance().waitForConnectionState(ConnectionState.State.CONNECTED); + Objects.requireNonNull(OpenHABRestCommunicator.getInstance()).waitForConnectionState(ConnectionState.State.CONNECTED); super.activate(); }