From 705f5c577c7af69496341c45044902ceb1d6d156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Lange?= Date: Tue, 25 May 2021 22:06:49 +0200 Subject: [PATCH] [mielecloud] Initial contribution of the Miele Cloud binding (#9146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also-by: Bert Plonus Also-by: Martin Lepsy Also-by: Benjamin Bolte Signed-off-by: Björn Lange --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + bundles/org.openhab.binding.mielecloud/NOTICE | 13 + .../org.openhab.binding.mielecloud/README.md | 623 + .../doc/account-overview-empty.png | Bin 0 -> 50757 bytes .../doc/account-overview-with-bridge.png | Bin 0 -> 86965 bytes .../doc/miele-login.png | Bin 0 -> 16608 bytes .../doc/pair-account.png | Bin 0 -> 59350 bytes .../doc/pairing-success.png | Bin 0 -> 78313 bytes .../org.openhab.binding.mielecloud/pom.xml | 17 + .../src/main/feature/feature.xml | 23 + .../internal/MieleCloudBindingConstants.java | 238 + .../internal/auth/OAuthException.java | 33 + .../auth/OAuthTokenRefreshListener.java | 30 + .../internal/auth/OAuthTokenRefresher.java | 74 + .../auth/OpenHabOAuthTokenRefresher.java | 138 + .../config/MieleCloudConfigService.java | 222 + .../config/OAuthAuthorizationHandler.java | 80 + .../config/OAuthAuthorizationHandlerImpl.java | 213 + .../config/ThingsTemplateGenerator.java | 121 + .../BridgeCreationFailedException.java | 25 + .../BridgeReconfigurationFailedException.java | 29 + .../NoOngoingAuthorizationException.java | 29 + .../OngoingAuthorizationException.java | 50 + .../servlet/AbstractRedirectionServlet.java | 62 + .../servlet/AbstractShowPageServlet.java | 93 + .../servlet/AccountOverviewServlet.java | 182 + .../config/servlet/CreateBridgeServlet.java | 217 + .../config/servlet/FailureServlet.java | 116 + .../config/servlet/ForwardToLoginServlet.java | 148 + .../config/servlet/MieleHttpException.java | 35 + .../config/servlet/PairAccountServlet.java | 124 + .../config/servlet/ResourceLoader.java | 86 + .../config/servlet/ResultServlet.java | 96 + .../internal/config/servlet/ServletUtil.java | 57 + .../config/servlet/SuccessServlet.java | 212 + .../discovery/ThingDiscoveryService.java | 214 + .../discovery/ThingInformationExtractor.java | 93 + .../handler/AbstractMieleThingHandler.java | 293 + .../handler/CoffeeSystemThingHandler.java | 75 + .../handler/CoolingDeviceThingHandler.java | 91 + .../handler/DishWarmerDeviceThingHandler.java | 88 + .../handler/DishwasherDeviceThingHandler.java | 75 + .../handler/DryerDeviceThingHandler.java | 79 + .../handler/HobDeviceThingHandler.java | 70 + .../handler/HoodDeviceThingHandler.java | 73 + .../internal/handler/MieleBridgeHandler.java | 362 + .../internal/handler/MieleHandlerFactory.java | 123 + .../handler/OvenDeviceThingHandler.java | 80 + ...oboticVacuumCleanerDeviceThingHandler.java | 86 + .../handler/WashingDeviceThingHandler.java | 80 + .../WineStorageDeviceThingHandler.java | 75 + .../handler/channel/ActionsChannelState.java | 68 + .../handler/channel/ChannelTypeUtil.java | 73 + .../handler/channel/DeviceChannelState.java | 269 + .../channel/TransitionChannelState.java | 47 + .../internal/util/EmailValidator.java | 35 + .../internal/util/LocaleValidator.java | 45 + .../webservice/ActionStateFetcher.java | 81 + .../internal/webservice/ConnectionError.java | 35 + .../webservice/ConnectionStatusListener.java | 36 + .../webservice/DefaultMieleWebservice.java | 355 + .../DefaultMieleWebserviceFactory.java | 28 + .../internal/webservice/DeviceCache.java | 49 + .../webservice/DeviceStateDispatcher.java | 109 + .../webservice/DeviceStateListener.java | 46 + .../internal/webservice/HttpUtil.java | 94 + .../internal/webservice/MieleWebservice.java | 142 + .../MieleWebserviceConfiguration.java | 134 + .../webservice/MieleWebserviceFactory.java | 31 + .../UnavailableMieleWebservice.java | 118 + .../internal/webservice/api/ActionsState.java | 176 + .../api/CoolingDeviceTemperatureState.java | 103 + .../internal/webservice/api/DeviceState.java | 558 + .../internal/webservice/api/PowerStatus.java | 50 + .../webservice/api/ProgramStatus.java | 50 + .../webservice/api/TransitionState.java | 147 + .../WineStorageDeviceTemperatureState.java | 206 + .../internal/webservice/api/json/Actions.java | 137 + .../internal/webservice/api/json/Device.java | 65 + .../webservice/api/json/DeviceCollection.java | 97 + .../webservice/api/json/DeviceIdentLabel.java | 92 + .../webservice/api/json/DeviceType.java | 129 + .../webservice/api/json/DryingStep.java | 78 + .../webservice/api/json/ErrorMessage.java | 76 + .../internal/webservice/api/json/Ident.java | 79 + .../internal/webservice/api/json/Light.java | 74 + .../api/json/MieleSyntaxException.java | 34 + .../webservice/api/json/PlateStep.java | 78 + .../webservice/api/json/ProcessAction.java | 51 + .../webservice/api/json/ProgramId.java | 78 + .../webservice/api/json/ProgramPhase.java | 78 + .../webservice/api/json/ProgramType.java | 78 + .../webservice/api/json/RemoteEnable.java | 65 + .../webservice/api/json/SpinningSpeed.java | 77 + .../internal/webservice/api/json/State.java | 238 + .../webservice/api/json/StateType.java | 70 + .../internal/webservice/api/json/Status.java | 78 + .../webservice/api/json/Temperature.java | 77 + .../internal/webservice/api/json/Type.java | 78 + .../webservice/api/json/VentilationStep.java | 78 + .../webservice/api/json/XkmIdentLabel.java | 65 + .../AuthorizationFailedException.java | 29 + ...MieleWebserviceDisconnectSseException.java | 25 + .../exception/MieleWebserviceException.java | 45 + ...ieleWebserviceInitializationException.java | 29 + .../MieleWebserviceTransientException.java | 43 + .../exception/TooManyRequestsException.java | 90 + .../language/CombiningLanguageProvider.java | 71 + .../language/JvmLanguageProvider.java | 31 + .../webservice/language/LanguageProvider.java | 32 + .../language/OpenHabLanguageProvider.java | 37 + .../webservice/request/RequestFactory.java | 64 + .../request/RequestFactoryImpl.java | 113 + .../AuthorizationFailedRetryStrategy.java | 93 + .../webservice/retry/NTimesRetryStrategy.java | 72 + .../webservice/retry/RetryStrategy.java | 62 + .../retry/RetryStrategyCombiner.java | 47 + .../webservice/sse/BackoffStrategy.java | 50 + .../sse/ExponentialBackoffWithJitter.java | 101 + .../webservice/sse/ServerSentEvent.java | 62 + .../webservice/sse/SseConnection.java | 240 + .../internal/webservice/sse/SseListener.java | 39 + .../webservice/sse/SseRequestFactory.java | 36 + .../webservice/sse/SseStreamParser.java | 102 + .../main/resources/OH-INF/binding/binding.xml | 8 + .../OH-INF/config/configDescription.xml | 13 + .../OH-INF/i18n/mielecloud.properties | 253 + .../main/resources/OH-INF/thing/bridge.xml | 32 + .../resources/OH-INF/thing/channelTypes.xml | 448 + .../resources/OH-INF/thing/coffeeSystem.xml | 44 + .../OH-INF/thing/dishWarmerDevice.xml | 42 + .../OH-INF/thing/dishwasherDevice.xml | 46 + .../resources/OH-INF/thing/dryerDevice.xml | 50 + .../main/resources/OH-INF/thing/freezer.xml | 36 + .../main/resources/OH-INF/thing/fridge.xml | 36 + .../resources/OH-INF/thing/fridgeFreezer.xml | 40 + .../main/resources/OH-INF/thing/hobDevice.xml | 42 + .../resources/OH-INF/thing/hoodDevice.xml | 40 + .../resources/OH-INF/thing/ovenDevice.xml | 51 + .../thing/roboticVacuumCleanerDevice.xml | 40 + .../resources/OH-INF/thing/washerDryer.xml | 53 + .../resources/OH-INF/thing/washingMachine.xml | 51 + .../OH-INF/thing/wineStorageDevice.xml | 43 + .../internal/config/assets/css/main.css | 15023 +++++++++++++ .../internal/config/assets/css/rtl.css | 10 + .../config/assets/img/OpenHAB_logo.svg | 1 + .../internal/config/assets/img/favicon.ico | Bin 0 -> 32038 bytes .../internal/config/assets/img/miele.png | Bin 0 -> 34870 bytes .../internal/config/assets/js/main.js | 17768 ++++++++++++++++ .../internal/config/assets/js/main.js.map | 1 + .../mielecloud/internal/config/failure.html | 48 + .../mielecloud/internal/config/index.html | 51 + .../mielecloud/internal/config/pairing.html | 68 + .../mielecloud/internal/config/success.html | 90 + .../MieleCloudBindingTestConstants.java | 30 + .../auth/OpenHabOAuthTokenRefresherTest.java | 334 + .../OAuthAuthorizationHandlerImplTest.java | 358 + .../config/ThingsTemplateGeneratorTest.java | 270 + .../ThingInformationExtractorTest.java | 106 + .../internal/util/LocaleValidatorTest.java | 72 + .../mielecloud/internal/util/MockUtil.java | 94 + .../internal/util/ReflectionUtil.java | 142 + .../internal/util/ResourceUtil.java | 46 + .../webservice/ActionStateFetcherTest.java | 178 + .../DefaultMieleWebserviceTest.java | 742 + .../internal/webservice/DeviceCacheTest.java | 94 + .../webservice/DeviceStateDispatcherTest.java | 257 + .../internal/webservice/HttpUtilTest.java | 50 + .../webservice/RequestFactoryImplTest.java | 241 + .../webservice/api/ActionsStateTest.java | 299 + .../CoolingDeviceTemperatureStateTest.java | 144 + .../webservice/api/DeviceStateTest.java | 2130 ++ .../webservice/api/TransitionStateTest.java | 576 + ...WineStorageDeviceTemperatureStateTest.java | 455 + .../webservice/api/json/ActionsTest.java | 103 + .../api/json/DeviceCollectionTest.java | 290 + .../api/json/DeviceIdentLabelTest.java | 41 + .../webservice/api/json/ErrorMessageTest.java | 48 + .../webservice/api/json/LightTest.java | 69 + .../webservice/api/json/StateTest.java | 90 + .../webservice/api/json/StatusTest.java | 56 + .../webservice/api/json/TypeTest.java | 56 + .../TooManyRequestsExceptionTest.java | 108 + .../CombiningLanguageProviderTest.java | 98 + .../language/OpenHabLanguageProviderTest.java | 74 + .../AuthorizationFailedRetryStrategyTest.java | 203 + .../retry/NTimesRetryStrategyTest.java | 220 + .../retry/RetryStrategyCombinerTest.java | 98 + .../sse/ExponentialBackoffWithJitterTest.java | 202 + .../webservice/sse/SseConnectionTest.java | 600 + .../webservice/sse/SseStreamParserTest.java | 182 + .../webservice/api/json/deviceCollection.json | 123 + ...ionWithFloatingPointTargetTemperature.json | 13 + .../deviceCollectionWithLargeProgramID.json | 73 + ...viceCollectionWithSpinningSpeedObject.json | 11 + .../api/json/invalidDeviceCollection.json | 73 + bundles/pom.xml | 1 + .../NOTICE | 13 + .../itest.bndrun | 88 + .../pom.xml | 25 + .../internal/config/ConfigFlowTest.java | 134 + .../servlet/AccountOverviewServletTest.java | 105 + .../servlet/CreateBridgeServletTest.java | 242 + .../servlet/ForwardToLoginServletTest.java | 289 + .../servlet/PairAccountServletTest.java | 57 + .../config/servlet/ResultServletTest.java | 190 + .../config/servlet/SuccessServletTest.java | 146 + .../discovery/ThingDiscoveryTest.java | 406 + .../AbstractMieleThingHandlerTest.java | 578 + .../handler/CoffeeDeviceThingHandlerTest.java | 211 + .../CoolingDeviceThingHandlerTest.java | 252 + .../DishWarmerDeviceThingHandlerTest.java | 232 + .../DishwasherDeviceThingHandlerTest.java | 227 + .../handler/DryerDeviceThingHandlerTest.java | 241 + .../handler/HobDeviceThingHandlerTest.java | 122 + .../handler/HoodDeviceThingHandlerTest.java | 131 + .../handler/MieleBridgeHandlerTest.java | 562 + .../handler/MieleHandlerFactoryTest.java | 310 + .../handler/OvenDeviceThingHandlerTest.java | 248 + ...icVacuumCleanerDeviceThingHandlerTest.java | 169 + .../WashingDeviceThingHandlerTest.java | 248 + .../WineStorageDeviceThingHandlerTest.java | 142 + .../internal/util/AbstractConfigFlowTest.java | 113 + ...eCloudBindingIntegrationTestConstants.java | 72 + .../internal/util/OpenHabOsgiTest.java | 102 + .../internal/util/ReflectionUtil.java | 167 + .../mielecloud/internal/util/Website.java | 113 + .../internal/util/WebsiteCrawler.java | 47 + itests/pom.xml | 1 + 230 files changed, 61858 insertions(+) create mode 100644 bundles/org.openhab.binding.mielecloud/NOTICE create mode 100644 bundles/org.openhab.binding.mielecloud/README.md create mode 100644 bundles/org.openhab.binding.mielecloud/doc/account-overview-empty.png create mode 100644 bundles/org.openhab.binding.mielecloud/doc/account-overview-with-bridge.png create mode 100644 bundles/org.openhab.binding.mielecloud/doc/miele-login.png create mode 100644 bundles/org.openhab.binding.mielecloud/doc/pair-account.png create mode 100644 bundles/org.openhab.binding.mielecloud/doc/pairing-success.png create mode 100644 bundles/org.openhab.binding.mielecloud/pom.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingConstants.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthTokenRefreshListener.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthTokenRefresher.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OpenHabOAuthTokenRefresher.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/MieleCloudConfigService.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandlerImpl.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/ThingsTemplateGenerator.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/BridgeCreationFailedException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/BridgeReconfigurationFailedException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/NoOngoingAuthorizationException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/OngoingAuthorizationException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AbstractRedirectionServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AbstractShowPageServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AccountOverviewServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/CreateBridgeServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/FailureServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ForwardToLoginServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/MieleHttpException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/PairAccountServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResourceLoader.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResultServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ServletUtil.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/SuccessServlet.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/discovery/ThingDiscoveryService.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/discovery/ThingInformationExtractor.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/AbstractMieleThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoffeeSystemThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoolingDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishWarmerDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/HobDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/HoodDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/MieleBridgeHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/MieleHandlerFactory.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/OvenDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/RoboticVacuumCleanerDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/WineStorageDeviceThingHandler.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/ActionsChannelState.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtil.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/DeviceChannelState.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/TransitionChannelState.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/util/EmailValidator.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/util/LocaleValidator.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/ActionStateFetcher.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/ConnectionError.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/ConnectionStatusListener.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/DefaultMieleWebservice.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/DefaultMieleWebserviceFactory.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/DeviceCache.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/DeviceStateDispatcher.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/DeviceStateListener.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/HttpUtil.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/MieleWebservice.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/MieleWebserviceConfiguration.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/MieleWebserviceFactory.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/UnavailableMieleWebservice.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/ActionsState.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/CoolingDeviceTemperatureState.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceState.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/PowerStatus.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/ProgramStatus.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/TransitionState.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/WineStorageDeviceTemperatureState.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/Actions.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/Device.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceCollection.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceIdentLabel.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceType.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DryingStep.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/ErrorMessage.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/Ident.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/Light.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/MieleSyntaxException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/PlateStep.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/ProcessAction.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/ProgramId.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/ProgramPhase.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/ProgramType.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/RemoteEnable.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/SpinningSpeed.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/State.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/StateType.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/Status.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/Temperature.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/Type.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/VentilationStep.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/XkmIdentLabel.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/exception/AuthorizationFailedException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/exception/MieleWebserviceDisconnectSseException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/exception/MieleWebserviceException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/exception/MieleWebserviceInitializationException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/exception/MieleWebserviceTransientException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/exception/TooManyRequestsException.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/language/CombiningLanguageProvider.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/language/JvmLanguageProvider.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/language/LanguageProvider.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/language/OpenHabLanguageProvider.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/request/RequestFactory.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/request/RequestFactoryImpl.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/retry/AuthorizationFailedRetryStrategy.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/retry/NTimesRetryStrategy.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/retry/RetryStrategy.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/retry/RetryStrategyCombiner.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/sse/BackoffStrategy.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/sse/ExponentialBackoffWithJitter.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/sse/ServerSentEvent.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/sse/SseConnection.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/sse/SseListener.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/sse/SseRequestFactory.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/sse/SseStreamParser.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/config/configDescription.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/i18n/mielecloud.properties create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/bridge.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/channelTypes.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/coffeeSystem.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dishWarmerDevice.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dishwasherDevice.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dryerDevice.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/freezer.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/fridge.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/fridgeFreezer.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/hobDevice.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/hoodDevice.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/ovenDevice.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/roboticVacuumCleanerDevice.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washerDryer.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washingMachine.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/wineStorageDevice.xml create mode 100755 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/assets/css/main.css create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/assets/css/rtl.css create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/assets/img/OpenHAB_logo.svg create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/assets/img/favicon.ico create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/assets/img/miele.png create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/assets/js/main.js create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/assets/js/main.js.map create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/failure.html create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/index.html create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/pairing.html create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/org/openhab/binding/mielecloud/internal/config/success.html create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingTestConstants.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/auth/OpenHabOAuthTokenRefresherTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandlerImplTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/config/ThingsTemplateGeneratorTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/discovery/ThingInformationExtractorTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/util/LocaleValidatorTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/util/MockUtil.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/util/ReflectionUtil.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/util/ResourceUtil.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/ActionStateFetcherTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/DefaultMieleWebserviceTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/DeviceCacheTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/DeviceStateDispatcherTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/HttpUtilTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/RequestFactoryImplTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/ActionsStateTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/CoolingDeviceTemperatureStateTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceStateTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/TransitionStateTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/WineStorageDeviceTemperatureStateTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/ActionsTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceCollectionTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceIdentLabelTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/ErrorMessageTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/LightTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/StateTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/StatusTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/TypeTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/exception/TooManyRequestsExceptionTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/language/CombiningLanguageProviderTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/language/OpenHabLanguageProviderTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/retry/AuthorizationFailedRetryStrategyTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/retry/NTimesRetryStrategyTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/retry/RetryStrategyCombinerTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/sse/ExponentialBackoffWithJitterTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/sse/SseConnectionTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/sse/SseStreamParserTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollection.json create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithFloatingPointTargetTemperature.json create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithLargeProgramID.json create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithSpinningSpeedObject.json create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/invalidDeviceCollection.json create mode 100644 itests/org.openhab.binding.mielecloud.tests/NOTICE create mode 100644 itests/org.openhab.binding.mielecloud.tests/itest.bndrun create mode 100644 itests/org.openhab.binding.mielecloud.tests/pom.xml create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/config/ConfigFlowTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AccountOverviewServletTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/CreateBridgeServletTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ForwardToLoginServletTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/PairAccountServletTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResultServletTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/SuccessServletTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/discovery/ThingDiscoveryTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/AbstractMieleThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoffeeDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoolingDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishWarmerDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HobDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HoodDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/MieleBridgeHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/MieleHandlerFactoryTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/OvenDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/RoboticVacuumCleanerDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WineStorageDeviceThingHandlerTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/util/AbstractConfigFlowTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/util/MieleCloudBindingIntegrationTestConstants.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/util/OpenHabOsgiTest.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/util/ReflectionUtil.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/util/Website.java create mode 100644 itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/util/WebsiteCrawler.java diff --git a/CODEOWNERS b/CODEOWNERS index 65619f4bc..bed0f4478 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -165,6 +165,7 @@ /bundles/org.openhab.binding.meteoblue/ @9037568 /bundles/org.openhab.binding.meteostick/ @cdjackson /bundles/org.openhab.binding.miele/ @kgoderis +/bundles/org.openhab.binding.mielecloud/ @BjoernLange /bundles/org.openhab.binding.mihome/ @pboos /bundles/org.openhab.binding.miio/ @marcelrv /bundles/org.openhab.binding.milight/ @davidgraeff diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index a3396a3a3..b183a255f 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -811,6 +811,11 @@ org.openhab.binding.miele ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.mielecloud + ${project.version} + org.openhab.addons.bundles org.openhab.binding.mihome diff --git a/bundles/org.openhab.binding.mielecloud/NOTICE b/bundles/org.openhab.binding.mielecloud/NOTICE new file mode 100644 index 000000000..38d625e34 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/NOTICE @@ -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 diff --git a/bundles/org.openhab.binding.mielecloud/README.md b/bundles/org.openhab.binding.mielecloud/README.md new file mode 100644 index 000000000..9da358d69 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/README.md @@ -0,0 +1,623 @@ +# Miele Cloud Binding + +This binding integrates [Miele@home](https://www.miele.de/brand/smarthome-42801.htm) appliances via a cloud connection. +A Miele cloud account and a set of developer credentials is required to use the binding. +The latter can be requested from the [Miele Developer Portal](https://www.miele.com/f/com/en/register_api.aspx). + +## Supported Things + +Most Miele appliances that directly connect to the cloud via a Wi-Fi module are supported. +Appliances connecting to the XGW3000 gateway via ZigBee are also supported when registered with the cloud account. +However they might be better supported by the [gateway-based Miele binding](https://www.openhab.org/addons/bindings/miele/). +Depending on the age of your appliance the functionality of the binding might be limited. +Appliances from recent generations will support all functionality. + +The following types of appliances are supported: + +| Appliance type | Thing type | +| -------------------------------- | ------------------------ | +| Coffee Machine | `coffee_system` | +| Dishwasher | `dishwasher` | +| Dish Warmer | `dish_warmer` | +| Freezer | `freezer` | +| Fridge | `fridge` | +| Fridge-Freezer Combination | `fridge_freezer` | +| Hob | `hob` | +| Hood | `hood` | +| Microwave Oven | `oven` | +| Oven | `oven` | +| Robotic Vacuum Cleaner | `robotic_vacuum_cleaner` | +| Tumble Dryer | `dryer` | +| Washer Dryer | `washer_dryer` | +| Washing Machine | `washing_machine` | +| Wine Cabinet | `wine_storage` | +| Wine Cabinet Freezer Combination | `wine_storage` | + +## Discovery + +Please take the following steps prior to using the binding. Create a Miele cloud account in the Miele@mobile app for [Android](https://play.google.com/store/apps/details?id=de.miele.infocontrol&hl=en_US) or [iOS](https://apps.apple.com/de/app/miele-mobile/id930406907?l=en) (if not already done). +Afterwards, pair your appliances. +Once your appliances are set up, register at the [Miele Developer Portal](https://www.miele.com/f/com/en/register_api.aspx). +You will receive a pair of client ID and client secret which will be used to pair your Miele cloud account to the Miele cloud openHAB binding. +Keep these credentials to yourself and treat them like a password! +It may take some time until the registration e-mail arrives. + +There is no auto discovery for the Miele cloud account. +The account is paired using OAuth2 with your Miele login and the developer credentials obtained from the [Miele Developer Portal](https://www.miele.com/f/com/en/register_api.aspx). +To pair the account go to the binding's configuration UI at `https:///mielecloud`. +For a standard openHABian Pi installation the address is [https://openhabianpi:8443/mielecloud](https://openhabianpi:8443/mielecloud). +Note that your browser will file a warning that the certificate is self-signed. +This is fine and you can safely continue. +It is also possible to use an unsecured connection for pairing but it is strongly recommended to use a secured connection because your credentials will otherwise be transferred without encryption over the local network. +For more information on this topic, see [Securing access to openHAB](https://www.openhab.org/docs/installation/security.html#encrypted-communication). +For a detailed walk through the account configuration, see [Account Configuration Example](#account-configuration-example). + +Once a Miele account is paired, all supported appliances are automatically discovered as individual things and placed in the inbox. +They can then be paired with your favorite management UI. +As an alternative, the binding configuration UI provides a things-file template per paired account that can be used to pair the appliances. + +## Thing Configuration + +A Miele cloud account needs to be configured to get access to your appliances. +After that appliances can be configured. + +### Account Configuration + +The Miele cloud account must be paired via the binding configuration UI before a bridge that relies on it can be configured in openHAB. +For details on the configuration UI see [Discovery](#discovery) and [Account Configuration Example](#account-configuration-example). +The account serves as a bridge for the things representing your appliances. +On success the configuration assistant will directly configure the account without requiring further actions. +As an alternative, it provides a things-file template. + +The account has the following parameters: + +| Name | Type | Description | +| ----------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| email | required | E-mail address identifying this account. This exists only to distinguish accounts. If the address is changed after authorization then the account needs to be authorized again. | +| locale | optional | The locale to use for full text channels of things from this account. Possible values are `en`, `de`, `da`, `es`, `fr`, `it`, `nl`, `nb`. Default is `en`. | + + +### Appliance Configuration + +The binding configuration UI will show a things-file template containing things for all supported appliances from the paired account. +This can be used as a starting point for a custom things-file. + +All Miele cloud appliance things have the following parameters: + +| Name | Type | Description | +| ---------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| deviceIdentifier | required | Technical device identifier uniquely identifying the Miele appliance. Use the discovery result or the things-file template to obtain it. | + + +## Channels + +The following table lists all available channels. +See the following chapters for detailed information about which appliance supports which channels. +Depending on the exact appliance configuration not all channels might be supported, e.g. a hob with four plates will only fill the channels for plates 1-4. +Channel ID and channel type ID match unless noted. + +| Channel Type ID | Item Type | Description | Read only | +| ----------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| remote_control_can_be_started | Switch | Indicates if this device can be started remotely. | Yes | +| remote_control_can_be_stopped | Switch | Indicates if this device can be stopped remotely. | Yes | +| remote_control_can_be_paused | Switch | Indicates if this device can be paused remotely. | Yes | +| remote_control_can_be_switched_on | Switch | Indicates if the device can be switched on remotely. | Yes | +| remote_control_can_be_switched_off | Switch | Indicates if the device can be switched off remotely. | Yes | +| remote_control_can_set_program_active | Switch | Indicates if the active program of the device can be set remotely. | Yes | +| spinning_speed | String | The spinning speed of the active program. | Yes | +| spinning_speed_raw | Number | The raw spinning speed of the active program. | Yes | +| program_active | String | The active program of the device. | Yes | +| program_active_raw | Number | The raw active program of the device. | Yes | +| dish_warmer_program_active | String | The active program of the device. | No | +| vacuum_cleaner_program_active | String | The active program of the device. | No | +| program_phase | String | The phase of the active program. | Yes | +| program_phase_raw | Number | The raw phase of the active program. | Yes | +| operation_state | String | The operation state of the device. | Yes | +| operation_state_raw | Number | The raw operation state of the device. | Yes | +| program_start | Switch | Starts the currently selected program. | No | +| program_stop | Switch | Stops the currently selected program. | No | +| program_start_stop | String | Starts or stops the currently selected program. | No | +| program_start_stop_pause | String | Starts, stops or pauses the currently selected program. | No | +| power_state_on_off | String | Switches the device On or Off. | No | +| finish_state | Switch | Indicates whether the most recent program finished. | Yes | +| delayed_start_time | Number | The delayed start time of the selected program. | Yes | +| program_remaining_time | Number | The remaining time of the active program. | Yes | +| program_elapsed_time | Number | The elapsed time of the active program. | Yes | +| program_progress | Number | The progress of the active program. | Yes | +| drying_target | String | The target drying step of the laundry. | Yes | +| drying_target_raw | Number | The raw target drying step of the laundry. | Yes | +| pre_heat_finished | Switch | Indicates whether the pre-heating finished. | Yes | +| temperature_target | Number | The target temperature of the device. | Yes | +| temperature_current | Number | The currently measured temperature of the device. | Yes | +| ventilation_power | String | The current ventilation power of the hood. | Yes | +| ventilation_power_raw | Number | The current raw ventilation power of the hood. | Yes | +| error_state | Switch | Indication flag which signals an error state for the device. | Yes | +| info_state | Switch | Indication flag which signals an information of the device. | Yes | +| fridge_super_cool | Switch | Start the super cooling mode of the fridge. | No | +| freezer_super_freeze | Switch | Start the super freezing mode of the freezer. | No | +| super_cool_can_be_controlled | Switch | Indicates if super cooling can be toggled. | Yes | +| super_freeze_can_be_controlled | Switch | Indicates if super freezing can be toggled | Yes | +| fridge_temperature_target | Number | The target temperature of the fridge. | Yes | +| fridge_temperature_current | Number | The currently measured temperature of the fridge. | Yes | +| freezer_temperature_target | Number | The target temperature of the freezer. | Yes | +| freezer_temperature_current | Number | The currently measured temperature of the freezer. | Yes | +| top_temperature_target | Number | The target temperature of the top area. | Yes | +| top_temperature_current | Number | The currently measured temperature of the top area. | Yes | +| middle_temperature_target | Number | The target temperature of the middle area. | Yes | +| middle_temperature_current | Number | The currently measured temperature of the middle area. | Yes | +| bottom_temperature_target | Number | The target temperature of the bottom area. | Yes | +| bottom_temperature_current | Number | The currently measured temperature of the bottom area. | Yes | +| light_switch | Switch | Indicates if the light of the device is enabled. | No | +| light_can_be_controlled | Switch | Indicates if the light of the device can be controlled. | Yes | +| plate_power_step | String | The power level of the heating plate. | Yes | +| plate_power_step_raw | Number | The raw power level of the heating plate. | Yes | +| door_state | Switch | Indicates if the door of the device is open. | Yes | +| door_alarm | Switch | Indicates if the door alarm of the device is active. | Yes | +| battery_level | Number | The battery level of the robotic vacuum cleaner. | Yes | + +### Coffee System + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- program_active +- program_active_raw +- program_phase +- program_phase_raw +- operation_state +- operation_state_raw +- finish_state +- power_state_on_off +- program_remaining_time +- program_elapsed_time +- error_state +- info_state +- light_switch +- light_can_be_controlled + +### Dish Warmer + +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- dish_warmer_program_active +- program_active_raw +- operation_state +- operation_state_raw +- power_state_on_off +- finish_state +- program_remaining_time +- program_elapsed_time +- program_progress +- temperature_target +- temperature_current +- error_state +- info_state +- door_state + +### Dishwasher + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- program_active +- program_active_raw +- program_phase +- program_phase_raw +- operation_state +- operation_state_raw +- program_start_stop +- finish_state +- power_state_on_off +- delayed_start_time +- program_remaining_time +- program_elapsed_time +- program_progress +- error_state +- info_state +- door_state + +### Tumble Dryer + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- program_active +- program_active_raw +- program_phase +- program_phase_raw +- operation_state +- operation_state_raw +- program_start_stop +- finish_state +- power_state_on_off +- delayed_start_time +- program_remaining_time +- program_elapsed_time +- program_progress +- drying_target +- drying_target_raw +- error_state +- info_state +- light_switch +- light_can_be_controlled +- door_state + +### Freezer + +- operation_state +- operation_state_raw +- error_state +- info_state +- freezer_super_freeze +- super_freeze_can_be_controlled +- freezer_temperature_target +- freezer_temperature_current +- door_state +- door_alarm + +### Fridge + +- operation_state +- operation_state_raw +- error_state +- info_state +- fridge_super_cool +- super_cool_can_be_controlled +- fridge_temperature_target +- fridge_temperature_current +- door_state +- door_alarm + +### Fridge Freezer + +- operation_state +- operation_state_raw +- error_state +- info_state +- fridge_super_cool +- freezer_super_freeze +- super_cool_can_be_controlled +- super_freeze_can_be_controlled +- fridge_temperature_target +- fridge_temperature_current +- freezer_temperature_target +- freezer_temperature_current +- door_state +- door_alarm + +### Hob + +- operation_state +- operation_state_raw +- error_state +- info_state +- plate_1_power_step to plate_6_power_step with channel type ID plate_power_step +- plate_1_power_step_raw to plate_6_power_step_raw with channel type ID plate_power_step_raw + +### Hood + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- program_phase +- program_phase_raw +- operation_state +- operation_state_raw +- power_state_on_off +- ventilation_power +- ventilation_power_raw +- error_state +- info_state +- light_switch +- light_can_be_controlled + +### Oven + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- program_active +- program_active_raw +- program_phase +- program_phase_raw +- operation_state +- operation_state_raw +- program_start_stop +- finish_state +- power_state_on_off +- delayed_start_time +- program_remaining_time +- program_elapsed_time +- program_progress +- pre_heat_finished +- temperature_target +- temperature_current +- error_state +- info_state +- light_switch +- light_can_be_controlled +- door_state + +### Robotic Vacuum Cleaner + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_paused +- remote_control_can_set_program_active +- vacuum_cleaner_program_active +- program_active_raw +- operation_state +- operation_state_raw +- finish_state +- program_start_stop_pause +- power_state_on_off +- error_state +- info_state +- battery_level + +### Washer Dryer + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- spinning_speed +- spinning_speed_raw +- program_active +- program_active_raw +- program_phase +- program_phase_raw +- operation_state +- operation_state_raw +- program_start_stop +- finish_state +- power_state_on_off +- delayed_start_time +- program_remaining_time +- program_elapsed_time +- program_progress +- drying_target +- drying_target_raw +- error_state +- info_state +- temperature_target +- light_switch +- light_can_be_controlled +- door_state + +### Washing Machine + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- spinning_speed +- spinning_speed_raw +- program_active +- program_active_raw +- program_phase +- program_phase_raw +- operation_state +- operation_state_raw +- program_start_stop +- finish_state +- power_state_on_off +- delayed_start_time +- program_remaining_time +- program_elapsed_time +- program_progress +- error_state +- info_state +- temperature_target +- light_switch +- light_can_be_controlled +- door_state + +### Wine Storage + +- remote_control_can_be_started +- remote_control_can_be_stopped +- remote_control_can_be_switched_on +- remote_control_can_be_switched_off +- operation_state +- operation_state_raw +- power_state_on_off +- error_state +- info_state +- temperature_target +- temperature_current +- top_temperature_target +- top_temperature_current +- middle_temperature_target +- middle_temperature_current +- bottom_temperature_target +- bottom_temperature_current + +### Note on plate_power_step channels + +Hob things have an additional property `plateCount` that indicates the number of plates present on the appliance. +Only the channels `plate_1_power_step` to `plate_x_power_step` will be populated by the binding where `x` is the value of the `plateCount` property. + +The plate numbers do not represent the physical layout of the plates on the appliance, but always start with the `plate_1_power_step` channel. +This means that a hob with two plates will have `plate_1_power_step` and `plate_2_power_step` populated and all other `plate_x_power_step` channels empty. + +The `plate_x_power_step` channels show the current power step of the according plate. +**Please note that some hobs may use dynamic numbering for plates.** +Hobs that use dynamic numbering will use the first power step channel that is currently at a power step of zero when the plate is turned on. +Additionally, when a plate is turned off all other plates with higher numbers will decrease their number by one. +For example if plate 1, 2 and 3 are active and plate 1 is turned off then plate 2 will become plate 1, plate 3 will become plate 2 and plate 3 will have a power step of zero. +This behavior is a fixed part of the affected appliances and cannot be changed. + +### Note on door_state channel + +The `door_state` channel might not always provide a value matching the actual state. +For example, a washing machine will not provide a valid `door_state` when the appliance is turned off. +A valid door state can be expected when the appliance is in one of the following raw operation states, compare the `operation_state_raw` channel: + +- `3`: Program selected +- `4`: Program selected, waiting to start +- `5`: Running +- `6`: Paused + +## Properties + +The following chapters list the properties offered by appliances. + +### Common Properties + +| Property Name | Description | +| ------------- | ----------------------------------------------------------------------------- | +| serialNumber | Serial number of the appliance, only present for physical appliances | +| modelId | Model ID of the appliance | +| vendor | Always "Miele" | + +### Account + +| Property Name | Description | +| ------------- | ----------------------------------------------------------------------------- | +| connection | Type of connection used by the account, always "INTERNET" | +| accessToken | The currently used OAuth 2 access token for accessing the Miele 3rd Party API | + +### Hob + +| Property Name | Description | +| ------------- | ----------------------------------------------------------------------------- | +| plateCount | Number of plates offered by the appliance | + +## Full Example + +### demo.things: + +``` +Bridge mielecloud:account:home [ email="me@openhab.org", locale="en" ] { + Thing coffee_system 000703261234 "Coffee machine CVA7440" [ deviceIdentifier="000703261234" ] + Thing hob 000160102345 "Cooktop KM7677" [ deviceIdentifier="000160102345" ] +} +``` + +### demo.items: + +``` +// Coffee system +Switch coffee_system_remote_control_can_be_started { channel="mielecloud:coffee_system:home:000703261234:remote_control_can_be_started" } +Switch coffee_system_remote_control_can_be_stopped { channel="mielecloud:coffee_system:home:000703261234:remote_control_can_be_stopped" } +Switch coffee_system_remote_control_can_be_switched_on { channel="mielecloud:coffee_system:home:000703261234:remote_control_can_be_switched_on" } +Switch coffee_system_remote_control_can_be_switched_off { channel="mielecloud:coffee_system:home:000703261234:remote_control_can_be_switched_off" } +String coffee_system_program_active { channel="mielecloud:coffee_system:home:000703261234:program_active" } +String coffee_system_program_phase { channel="mielecloud:coffee_system:home:000703261234:program_phase" } +String coffee_system_power_state_on_off { channel="mielecloud:coffee_system:home:000703261234:power_state_on_off" } +String coffee_system_operation_state { channel="mielecloud:coffee_system:home:000703261234:operation_state" } +Switch coffee_system_finish_state { channel="mielecloud:coffee_system:home:000703261234:finish_state" } +Number coffee_system_program_remaining_time { channel="mielecloud:coffee_system:home:000703261234:program_remaining_time" } +Switch coffee_system_error_state { channel="mielecloud:coffee_system:home:000703261234:error_state" } +Switch coffee_system_info_state { channel="mielecloud:coffee_system:home:000703261234:info_state" } +Switch coffee_system_light_switch { channel="mielecloud:coffee_system:home:000703261234:light_switch" } +Switch coffee_system_light_can_be_controlled { channel="mielecloud:coffee_system:home:000703261234:light_can_be_controlled" } + +// Hob +Switch hob_remote_control_can_be_started { channel="mielecloud:hob:home:000160102345:remote_control_can_be_started" } +Switch hob_remote_control_can_be_stopped { channel="mielecloud:hob:home:000160102345:remote_control_can_be_stopped" } +String hob_operation_state { channel="mielecloud:hob:home:000160102345:operation_state" } +Switch hob_error_state { channel="mielecloud:hob:home:000160102345:error_state" } +Switch hob_info_state { channel="mielecloud:hob:home:000160102345:info_state" } +Switch hob_plate_1_is_present { channel="mielecloud:hob:home:000160102345:plate_1_is_present" } +String hob_plate_1_power_step { channel="mielecloud:hob:home:000160102345:plate_1_power_step" } +Switch hob_plate_2_is_present { channel="mielecloud:hob:home:000160102345:plate_2_is_present" } +String hob_plate_2_power_step { channel="mielecloud:hob:home:000160102345:plate_2_power_step" } +Switch hob_plate_3_is_present { channel="mielecloud:hob:home:000160102345:plate_3_is_present" } +String hob_plate_3_power_step { channel="mielecloud:hob:home:000160102345:plate_3_power_step" } +Switch hob_plate_4_is_present { channel="mielecloud:hob:home:000160102345:plate_4_is_present" } +String hob_plate_4_power_step { channel="mielecloud:hob:home:000160102345:plate_4_power_step" } +Switch hob_plate_5_is_present { channel="mielecloud:hob:home:000160102345:plate_5_is_present" } +String hob_plate_5_power_step { channel="mielecloud:hob:home:000160102345:plate_5_power_step" } +Switch hob_plate_6_is_present { channel="mielecloud:hob:home:000160102345:plate_6_is_present" } +String hob_plate_6_power_step { channel="mielecloud:hob:home:000160102345:plate_6_power_step" } +``` + +### demo.sitemap: + +``` +sitemap demo label="Kitchen" +{ + Frame { + // Coffee system + Text item=coffee_system_program_active + Text item=coffee_system_program_phase + Text item=coffee_system_power_state_on_off + Text item=coffee_system_operation_state + Switch item=coffee_system_finish_state + Default item=coffee_system_program_remaining_time + Switch item=coffee_system_error_state + Switch item=coffee_system_info_state + Switch item=coffee_system_light_switch + + // Hob + Text item=hob_operation_state + Switch item=hob_error_state + Switch item=hob_info_state + Text item=hob_plate_1_power_step + Text item=hob_plate_2_power_step + Text item=hob_plate_3_power_step + Text item=hob_plate_4_power_step + Text item=hob_plate_5_power_step + Text item=hob_plate_6_power_step + } +} +``` + +## Account Configuration Example + +The configuration UI is accessible at `https:///mielecloud`. +See [Discovery](#discovery) for a detailed description of how to open the configuration UI in a browser. + +When first opening the configuration UI no account will be paired. + +![Empty Account Overview](doc/account-overview-empty.png) + +We strongly recommend to use a secure connection for pairing, details on this topic can also be found in the [Discovery](#discovery) section. +Click `Pair Account` to start the pairing process. +If not already done, go to the [Miele Developer Portal](https://www.miele.com/f/com/en/register_api.aspx), register there and wait for the confirmation e-mail. +Obtain your client ID and client secret according to the instructions presented there. +Once you obtained your client ID and client secret continue pairing by filling in your client ID, client secret, bridge ID and an e-mail address that you wish to use for identifying the account. +You may choose any bridge ID you like as long as you only use letters, numbers, underscores and dashes. +The e-mail address does not need to match the e-mail address used for your Miele Cloud Account. +If you need to change the e-mail address later then you will need to authorize the account again. + +![Pair Account](doc/pair-account.png) + +A click on `Pair Account` will take you to the Miele cloud service login form where you need to log in with the same account as you used for the Miele@mobile app. + +![Miele Login Form](doc/miele-login.png) + +When this is the first time you pair an account, you will need to allow openHAB to access your account. + +When everything worked, you are presented with a page stating that pairing was successful. +Select the locale which should be used to display localized texts in openHAB channels. +From here, you have two options: +Either let the binding automatically configure a bridge instance or copy the presented things-file template to a things-file and return to the overview page. + +![Pairing Successful](doc/pairing-success.png) + +Once the bridge instance is `ONLINE`, you can either pair things for all appliances via your favorite management UI or use a things-file. +The account overview provides a things-file template that is shown when you expand the account. +This can serve as a starting point for your own things-file. + +![Account Overview With Bridge](doc/account-overview-with-bridge.png) + +## Rule Ideas + +Here are some ideas on what could be done with this binding. You have more ideas or even an example? Great! Feel free to contribute! + +- Notify yourself of a finished dishwasher, tumble dryer, washer dryer or washing machine, e.g. by changing the lighting +- Control the supercooler / superfreezer of your freezer, fridge or fridge-freezer combination with a voice assistant +- Notify yourself when the oven has finished pre-heating + +## Acknowledgements + +The development of this binding was initiated and sponsored by Miele & Cie. KG. + diff --git a/bundles/org.openhab.binding.mielecloud/doc/account-overview-empty.png b/bundles/org.openhab.binding.mielecloud/doc/account-overview-empty.png new file mode 100644 index 0000000000000000000000000000000000000000..7b733af0be8bcef7f3564ff17333b8e1bdca1dd3 GIT binary patch literal 50757 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

mb4_}5UJ>TM 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 JrDXo~zX9^bA(sFE literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.mielecloud/doc/account-overview-with-bridge.png b/bundles/org.openhab.binding.mielecloud/doc/account-overview-with-bridge.png new file mode 100644 index 0000000000000000000000000000000000000000..2a9361b595f9433dd9ddb78b31d2cee132cb8aca GIT binary patch literal 86965 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 Qr6|C^U>7VmTjHbs4SGv76951J literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.mielecloud/doc/pairing-success.png b/bundles/org.openhab.binding.mielecloud/doc/pairing-success.png new file mode 100644 index 0000000000000000000000000000000000000000..f10bf6284bdc92c8da2831df52049e2a02393629 GIT binary patch literal 78313 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 smMPJnc=E!1kN=~qughR<#+FY0A)pn + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.1.0-SNAPSHOT + + + org.openhab.binding.mielecloud + + openHAB Add-ons :: Bundles :: Miele Cloud Binding + + diff --git a/bundles/org.openhab.binding.mielecloud/src/main/feature/feature.xml b/bundles/org.openhab.binding.mielecloud/src/main/feature/feature.xml new file mode 100644 index 000000000..4024caa71 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/feature/feature.xml @@ -0,0 +1,23 @@ + + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.mielecloud/${project.version} + + diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingConstants.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingConstants.java new file mode 100644 index 000000000..59b1426ad --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingConstants.java @@ -0,0 +1,238 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link MieleCloudBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Roland Edelhoff - Initial contribution + * @author Björn Lange - Added locale config parameter, added i18n key collection + * @author Benjamin Bolte - Add pre-heat finished and plate step channels, door state and door alarm channels, info + * state channel and map signal flags from API + * @author Björn Lange - Add elapsed time channel, dish warmer thing + */ +@NonNullByDefault +public final class MieleCloudBindingConstants { + + private MieleCloudBindingConstants() { + } + + /** + * ID of the binding. + */ + public static final String BINDING_ID = "mielecloud"; + + /** + * Thing type ID of Miele cloud bridges / accounts. + */ + public static final String BRIDGE_TYPE_ID = "account"; + + /** + * The {@link ThingTypeUID} of Miele cloud bridges / accounts. + */ + public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, BRIDGE_TYPE_ID); + + /** + * The {@link ThingTypeUID} of Miele washing machines. + */ + public static final ThingTypeUID THING_TYPE_WASHING_MACHINE = new ThingTypeUID(BINDING_ID, "washing_machine"); + + /** + * The {@link ThingTypeUID} of Miele washer-dryers. + */ + public static final ThingTypeUID THING_TYPE_WASHER_DRYER = new ThingTypeUID(BINDING_ID, "washer_dryer"); + + /** + * The {@link ThingTypeUID} of Miele coffee machines. + */ + public static final ThingTypeUID THING_TYPE_COFFEE_SYSTEM = new ThingTypeUID(BINDING_ID, "coffee_system"); + + /** + * The {@link ThingTypeUID} of Miele fridge-freezers. + */ + public static final ThingTypeUID THING_TYPE_FRIDGE_FREEZER = new ThingTypeUID(BINDING_ID, "fridge_freezer"); + + /** + * The {@link ThingTypeUID} of Miele fridges. + */ + public static final ThingTypeUID THING_TYPE_FRIDGE = new ThingTypeUID(BINDING_ID, "fridge"); + + /** + * The {@link ThingTypeUID} of Miele freezers. + */ + public static final ThingTypeUID THING_TYPE_FREEZER = new ThingTypeUID(BINDING_ID, "freezer"); + + /** + * The {@link ThingTypeUID} of Miele ovens. + */ + public static final ThingTypeUID THING_TYPE_OVEN = new ThingTypeUID(BINDING_ID, "oven"); + + /** + * The {@link ThingTypeUID} of Miele hobs. + */ + public static final ThingTypeUID THING_TYPE_HOB = new ThingTypeUID(BINDING_ID, "hob"); + + /** + * The {@link ThingTypeUID} of Miele wine storages. + */ + public static final ThingTypeUID THING_TYPE_WINE_STORAGE = new ThingTypeUID(BINDING_ID, "wine_storage"); + + /** + * The {@link ThingTypeUID} of Miele dishwashers. + */ + public static final ThingTypeUID THING_TYPE_DISHWASHER = new ThingTypeUID(BINDING_ID, "dishwasher"); + + /** + * The {@link ThingTypeUID} of Miele dryers. + */ + public static final ThingTypeUID THING_TYPE_DRYER = new ThingTypeUID(BINDING_ID, "dryer"); + + /** + * The {@link ThingTypeUID} of Miele hoods. + */ + public static final ThingTypeUID THING_TYPE_HOOD = new ThingTypeUID(BINDING_ID, "hood"); + + /** + * The {@link ThingTypeUID} of Miele dish warmers. + */ + public static final ThingTypeUID THING_TYPE_DISH_WARMER = new ThingTypeUID(BINDING_ID, "dish_warmer"); + + /** + * The {@link ThingTypeUID} of Miele robotic vacuum cleaners. + */ + public static final ThingTypeUID THING_TYPE_ROBOTIC_VACUUM_CLEANER = new ThingTypeUID(BINDING_ID, + "robotic_vacuum_cleaner"); + + /** + * Name of the property storing the OAuth2 access token. + */ + public static final String PROPERTY_ACCESS_TOKEN = "accessToken"; + + /** + * Name of the configuration parameter for the e-mail address. + */ + public static final String CONFIG_PARAM_EMAIL = "email"; + + /** + * Name of the configuration parameter for the device identifier uniquely identifying a Miele device. + */ + public static final String CONFIG_PARAM_DEVICE_IDENTIFIER = "deviceIdentifier"; + + /** + * Name of the configuration parameter for the locale. The locale is stored as a 2-letter language code. + */ + public static final String CONFIG_PARAM_LOCALE = "locale"; + + /** + * Name of the property storing the number of plates for hobs. + */ + public static final String PROPERTY_PLATE_COUNT = "plateCount"; + + /** + * Constants for all channels. + */ + public static final class Channels { + private Channels() { + } + + public static final String REMOTE_CONTROL_CAN_BE_STARTED = "remote_control_can_be_started"; + public static final String REMOTE_CONTROL_CAN_BE_STOPPED = "remote_control_can_be_stopped"; + public static final String REMOTE_CONTROL_CAN_BE_PAUSED = "remote_control_can_be_paused"; + public static final String REMOTE_CONTROL_CAN_BE_SWITCHED_ON = "remote_control_can_be_switched_on"; + public static final String REMOTE_CONTROL_CAN_BE_SWITCHED_OFF = "remote_control_can_be_switched_off"; + public static final String REMOTE_CONTROL_CAN_SET_PROGRAM_ACTIVE = "remote_control_can_set_program_active"; + public static final String SPINNING_SPEED = "spinning_speed"; + public static final String SPINNING_SPEED_RAW = "spinning_speed_raw"; + public static final String PROGRAM_ACTIVE = "program_active"; + public static final String PROGRAM_ACTIVE_RAW = "program_active_raw"; + public static final String DISH_WARMER_PROGRAM_ACTIVE = "dish_warmer_program_active"; + public static final String VACUUM_CLEANER_PROGRAM_ACTIVE = "vacuum_cleaner_program_active"; + public static final String PROGRAM_PHASE = "program_phase"; + public static final String PROGRAM_PHASE_RAW = "program_phase_raw"; + public static final String OPERATION_STATE = "operation_state"; + public static final String OPERATION_STATE_RAW = "operation_state_raw"; + public static final String PROGRAM_START_STOP = "program_start_stop"; + public static final String PROGRAM_START_STOP_PAUSE = "program_start_stop_pause"; + public static final String POWER_ON_OFF = "power_state_on_off"; + public static final String FINISH_STATE = "finish_state"; + public static final String DELAYED_START_TIME = "delayed_start_time"; + public static final String PROGRAM_REMAINING_TIME = "program_remaining_time"; + public static final String PROGRAM_ELAPSED_TIME = "program_elapsed_time"; + public static final String PROGRAM_PROGRESS = "program_progress"; + public static final String DRYING_TARGET = "drying_target"; + public static final String DRYING_TARGET_RAW = "drying_target_raw"; + public static final String PRE_HEAT_FINISHED = "pre_heat_finished"; + public static final String TEMPERATURE_TARGET = "temperature_target"; + public static final String TEMPERATURE_CURRENT = "temperature_current"; + public static final String TEMPERATURE_CORE_TARGET = "temperature_core_target"; + public static final String TEMPERATURE_CORE_CURRENT = "temperature_core_current"; + public static final String VENTILATION_POWER = "ventilation_power"; + public static final String VENTILATION_POWER_RAW = "ventilation_power_raw"; + public static final String ERROR_STATE = "error_state"; + public static final String INFO_STATE = "info_state"; + public static final String FRIDGE_SUPER_COOL = "fridge_super_cool"; + public static final String FREEZER_SUPER_FREEZE = "freezer_super_freeze"; + public static final String SUPER_COOL_CAN_BE_CONTROLLED = "super_cool_can_be_controlled"; + public static final String SUPER_FREEZE_CAN_BE_CONTROLLED = "super_freeze_can_be_controlled"; + public static final String FRIDGE_TEMPERATURE_TARGET = "fridge_temperature_target"; + public static final String FRIDGE_TEMPERATURE_CURRENT = "fridge_temperature_current"; + public static final String FREEZER_TEMPERATURE_TARGET = "freezer_temperature_target"; + public static final String FREEZER_TEMPERATURE_CURRENT = "freezer_temperature_current"; + public static final String TOP_TEMPERATURE_TARGET = "top_temperature_target"; + public static final String TOP_TEMPERATURE_CURRENT = "top_temperature_current"; + public static final String MIDDLE_TEMPERATURE_TARGET = "middle_temperature_target"; + public static final String MIDDLE_TEMPERATURE_CURRENT = "middle_temperature_current"; + public static final String BOTTOM_TEMPERATURE_TARGET = "bottom_temperature_target"; + public static final String BOTTOM_TEMPERATURE_CURRENT = "bottom_temperature_current"; + public static final String LIGHT_SWITCH = "light_switch"; + public static final String LIGHT_CAN_BE_CONTROLLED = "light_can_be_controlled"; + public static final String PLATE_1_POWER_STEP = "plate_1_power_step"; + public static final String PLATE_1_POWER_STEP_RAW = "plate_1_power_step_raw"; + public static final String PLATE_2_POWER_STEP = "plate_2_power_step"; + public static final String PLATE_2_POWER_STEP_RAW = "plate_2_power_step_raw"; + public static final String PLATE_3_POWER_STEP = "plate_3_power_step"; + public static final String PLATE_3_POWER_STEP_RAW = "plate_3_power_step_raw"; + public static final String PLATE_4_POWER_STEP = "plate_4_power_step"; + public static final String PLATE_4_POWER_STEP_RAW = "plate_4_power_step_raw"; + public static final String PLATE_5_POWER_STEP = "plate_5_power_step"; + public static final String PLATE_5_POWER_STEP_RAW = "plate_5_power_step_raw"; + public static final String PLATE_6_POWER_STEP = "plate_6_power_step"; + public static final String PLATE_6_POWER_STEP_RAW = "plate_6_power_step_raw"; + public static final String DOOR_STATE = "door_state"; + public static final String DOOR_ALARM = "door_alarm"; + public static final String BATTERY_LEVEL = "battery_level"; + } + + /** + * Constants for i18n keys. + */ + public static final class I18NKeys { + private I18NKeys() { + } + + public static final String BRIDGE_STATUS_DESCRIPTION_ACCESS_TOKEN_NOT_CONFIGURED = "@text/mielecloud.bridge.status.access.token.not.configured"; + public static final String BRIDGE_STATUS_DESCRIPTION_ACCOUNT_NOT_AUTHORIZED = "@text/mielecloud.bridge.status.account.not.authorized"; + public static final String BRIDGE_STATUS_DESCRIPTION_ACCESS_TOKEN_REFRESH_FAILED = "@text/mielecloud.bridge.status.access.token.refresh.failed"; + public static final String BRIDGE_STATUS_DESCRIPTION_INVALID_EMAIL = "@text/mielecloud.bridge.status.invalid.email"; + public static final String BRIDGE_STATUS_DESCRIPTION_TRANSIENT_HTTP_ERROR = "@text/mielecloud.bridge.status.transient.http.error"; + + public static final String THING_STATUS_DESCRIPTION_WEBSERVICE_MISSING = "@text/mielecloud.thing.status.webservice.missing"; + public static final String THING_STATUS_DESCRIPTION_REMOVED = "@text/mielecloud.thing.status.removed"; + public static final String THING_STATUS_DESCRIPTION_RATELIMIT = "@text/mielecloud.thing.status.ratelimit"; + public static final String THING_STATUS_DESCRIPTION_DISCONNECTED = "@text/mielecloud.thing.status.disconnected"; + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthException.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthException.java new file mode 100644 index 000000000..e607db6d2 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthException.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.auth; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Indicates an error in the OAuth2 authorization process. + * + * @author Roland Edelhoff - Initial contribution + */ +@NonNullByDefault +public class OAuthException extends RuntimeException { + private static final long serialVersionUID = -1863609233382694104L; + + public OAuthException(final String message) { + super(message); + } + + public OAuthException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthTokenRefreshListener.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthTokenRefreshListener.java new file mode 100644 index 000000000..94b723bdb --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthTokenRefreshListener.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.auth; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Listener that is invoked when an OAuth 2 access token was refreshed. + * + * @author Björn Lange - Initial contribution + */ +@NonNullByDefault +public interface OAuthTokenRefreshListener { + /** + * Invoked when a new access token becomes available. + * + * @param accessToken The new access token. + */ + public void onNewAccessToken(String accessToken); +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthTokenRefresher.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthTokenRefresher.java new file mode 100644 index 000000000..c5cbd3f35 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OAuthTokenRefresher.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.auth; + +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * An {@link OAuthTokenRefresher} offers convenient access to OAuth 2 authentication related functionality, + * especially refreshing the access token. + * + * @author Roland Edelhoff - Initial contribution + * @author Björn Lange - Allow removing tokens from the storage + */ +@NonNullByDefault +public interface OAuthTokenRefresher { + /** + * Sets the listener that is called when the access token was refreshed. + * + * @param listener The listener to register. + * @param serviceHandle The service handle identifying the internal OAuth configuration. + * @throws OAuthException if the listener needs to be registered at an underlying service which is not available + * because the account has not yet been authorized + */ + public void setRefreshListener(OAuthTokenRefreshListener listener, String serviceHandle); + + /** + * Unsets a listener. + * + * @param serviceHandle The service handle identifying the internal OAuth configuration. + */ + public void unsetRefreshListener(String serviceHandle); + + /** + * Refreshes the access and refresh tokens for the given service handle. If an {@link OAuthTokenRefreshListener} is + * registered for the service handle then it is notified after the refresh has completed. + * + * This call will succeed if the access token is still valid or a valid refresh token exists, which can be used to + * refresh the expired access token. If refreshing fails, an {@link OAuthException} is thrown. + * + * @param serviceHandle The service handle identifying the internal OAuth configuration. + * @throws OAuthException if the token cannot be obtained or refreshed + */ + public void refreshToken(String serviceHandle); + + /** + * Gets the currently stored access token from persistent storage. + * + * @param serviceHandle The service handle identifying the internal OAuth configuration. + * @return The currently stored access token or an empty {@link Optional} if there is no stored token. + */ + public Optional getAccessTokenFromStorage(String serviceHandle); + + /** + * Removes the tokens from persistent storage. + * + * Note: Calling this method will force the user to run through the pairing process again in order to obtain a + * working bridge. + * + * @param serviceHandle The service handle identifying the internal OAuth configuration. + */ + public void removeTokensFromStorage(String serviceHandle); +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OpenHabOAuthTokenRefresher.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OpenHabOAuthTokenRefresher.java new file mode 100644 index 000000000..a947da391 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/auth/OpenHabOAuthTokenRefresher.java @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.auth; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener; +import org.openhab.core.auth.client.oauth2.AccessTokenResponse; +import org.openhab.core.auth.client.oauth2.OAuthClientService; +import org.openhab.core.auth.client.oauth2.OAuthFactory; +import org.openhab.core.auth.client.oauth2.OAuthResponseException; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles refreshing of OAuth2 tokens managed by the openHAB runtime. + * + * @author Björn Lange - Initial contribution + */ +@Component +@NonNullByDefault +public final class OpenHabOAuthTokenRefresher implements OAuthTokenRefresher { + private final Logger logger = LoggerFactory.getLogger(OpenHabOAuthTokenRefresher.class); + + private final OAuthFactory oauthFactory; + private Map listenerByServiceHandle = new HashMap<>(); + + @Activate + public OpenHabOAuthTokenRefresher(@Reference OAuthFactory oauthFactory) { + this.oauthFactory = oauthFactory; + } + + @Override + public void setRefreshListener(OAuthTokenRefreshListener listener, String serviceHandle) { + final AccessTokenRefreshListener refreshListener = tokenResponse -> { + final String accessToken = tokenResponse.getAccessToken(); + if (accessToken == null) { + // Fail without exception to ensure that the OAuthClientService notifies all listeners. + logger.warn("Ignoring access token response without access token."); + } else { + listener.onNewAccessToken(accessToken); + } + }; + + OAuthClientService clientService = getOAuthClientService(serviceHandle); + clientService.addAccessTokenRefreshListener(refreshListener); + listenerByServiceHandle.put(serviceHandle, refreshListener); + } + + @Override + public void unsetRefreshListener(String serviceHandle) { + final AccessTokenRefreshListener refreshListener = listenerByServiceHandle.get(serviceHandle); + if (refreshListener != null) { + try { + OAuthClientService clientService = getOAuthClientService(serviceHandle); + clientService.removeAccessTokenRefreshListener(refreshListener); + } catch (OAuthException e) { + logger.warn("Failed to remove refresh listener: OAuth client service is unavailable. Cause: {}", + e.getMessage()); + } + } + listenerByServiceHandle.remove(serviceHandle); + } + + @Override + public void refreshToken(String serviceHandle) { + if (listenerByServiceHandle.get(serviceHandle) == null) { + logger.warn("Token refreshing was requested but there is no token refresh listener registered!"); + return; + } + + OAuthClientService clientService = getOAuthClientService(serviceHandle); + refreshAccessToken(clientService); + } + + private OAuthClientService getOAuthClientService(String serviceHandle) { + final OAuthClientService clientService = oauthFactory.getOAuthClientService(serviceHandle); + if (clientService == null) { + throw new OAuthException("OAuth client service is not available."); + } + return clientService; + } + + private void refreshAccessToken(OAuthClientService clientService) { + try { + final AccessTokenResponse accessTokenResponse = clientService.refreshToken(); + final String accessToken = accessTokenResponse.getAccessToken(); + if (accessToken == null) { + throw new OAuthException("Access token is not available."); + } + } catch (org.openhab.core.auth.client.oauth2.OAuthException e) { + throw new OAuthException("An error occured during token refresh: " + e.getMessage(), e); + } catch (IOException e) { + throw new OAuthException("A network error occured during token refresh: " + e.getMessage(), e); + } catch (OAuthResponseException e) { + throw new OAuthException("Miele cloud service returned an illegal response: " + e.getMessage(), e); + } + } + + @Override + public Optional getAccessTokenFromStorage(String serviceHandle) { + try { + AccessTokenResponse tokenResponse = getOAuthClientService(serviceHandle).getAccessTokenResponse(); + if (tokenResponse == null) { + return Optional.empty(); + } else { + return Optional.of(tokenResponse.getAccessToken()); + } + } catch (OAuthException | org.openhab.core.auth.client.oauth2.OAuthException | IOException + | OAuthResponseException e) { + logger.debug("Cannot obtain access token from persistent storage.", e); + return Optional.empty(); + } + } + + @Override + public void removeTokensFromStorage(String serviceHandle) { + oauthFactory.deleteServiceAndAccessToken(serviceHandle); + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/MieleCloudConfigService.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/MieleCloudConfigService.java new file mode 100644 index 000000000..a2b15cc09 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/MieleCloudConfigService.java @@ -0,0 +1,222 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config; + +import java.util.Hashtable; +import java.util.Map; + +import javax.servlet.ServletException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mielecloud.internal.config.servlet.AccountOverviewServlet; +import org.openhab.binding.mielecloud.internal.config.servlet.CreateBridgeServlet; +import org.openhab.binding.mielecloud.internal.config.servlet.FailureServlet; +import org.openhab.binding.mielecloud.internal.config.servlet.ForwardToLoginServlet; +import org.openhab.binding.mielecloud.internal.config.servlet.PairAccountServlet; +import org.openhab.binding.mielecloud.internal.config.servlet.ResourceLoader; +import org.openhab.binding.mielecloud.internal.config.servlet.ResultServlet; +import org.openhab.binding.mielecloud.internal.config.servlet.SuccessServlet; +import org.openhab.binding.mielecloud.internal.webservice.language.CombiningLanguageProvider; +import org.openhab.binding.mielecloud.internal.webservice.language.JvmLanguageProvider; +import org.openhab.binding.mielecloud.internal.webservice.language.LanguageProvider; +import org.openhab.binding.mielecloud.internal.webservice.language.OpenHabLanguageProvider; +import org.openhab.core.auth.client.oauth2.OAuthFactory; +import org.openhab.core.common.ThreadPoolManager; +import org.openhab.core.config.discovery.inbox.Inbox; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.thing.ThingRegistry; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.http.HttpContext; +import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles the lifecycle of the Miele Cloud binding's configuration UI. + * + * @author Björn Lange - Initial Contribution + */ +@Component(service = MieleCloudConfigService.class, immediate = true, configurationPid = "binding.mielecloud.configService") +@NonNullByDefault +public final class MieleCloudConfigService { + private static final String ROOT_ALIAS = "/mielecloud"; + private static final String PAIR_ALIAS = ROOT_ALIAS + "/pair"; + private static final String FORWARD_TO_LOGIN_ALIAS = ROOT_ALIAS + "/forwardToLogin"; + private static final String RESULT_ALIAS = ROOT_ALIAS + "/result"; + private static final String SUCCESS_ALIAS = ROOT_ALIAS + "/success"; + private static final String CREATE_BRIDGE_THING_ALIAS = ROOT_ALIAS + "/createBridgeThing"; + private static final String FAILURE_ALIAS = ROOT_ALIAS + "/failure"; + private static final String CSS_ALIAS = ROOT_ALIAS + "/assets/css"; + private static final String JS_ALIAS = ROOT_ALIAS + "/assets/js"; + private static final String IMG_ALIAS = ROOT_ALIAS + "/assets/img"; + + private static final String WEBSITE_RESOURCE_BASE_PATH = "org/openhab/binding/mielecloud/internal/config"; + private static final String WEBSITE_CSS_RESOURCE_PATH = WEBSITE_RESOURCE_BASE_PATH + "/assets/css"; + private static final String WEBSITE_JS_RESOURCE_PATH = WEBSITE_RESOURCE_BASE_PATH + "/assets/js"; + private static final String WEBSITE_IMG_RESOURCE_PATH = WEBSITE_RESOURCE_BASE_PATH + "/assets/img"; + + private final Logger logger = LoggerFactory.getLogger(MieleCloudConfigService.class); + + private HttpService httpService; + private OAuthFactory oauthFactory; + private Inbox inbox; + private ThingRegistry thingRegistry; + private LocaleProvider localeProvider; + + /** + * For integration test purposes only. + */ + @Nullable + private AccountOverviewServlet accountOverviewServlet; + + /** + * For integration test purposes only. + */ + @Nullable + private ForwardToLoginServlet forwardToLoginServlet; + + /** + * For integration test purposes only. + */ + @Nullable + private ResultServlet resultServlet; + + /** + * For integration test purposes only. + */ + @Nullable + private SuccessServlet successServlet; + + /** + * For integration test purposes only. + */ + @Nullable + private CreateBridgeServlet createBridgeServlet; + + @Activate + public MieleCloudConfigService(@Reference HttpService httpService, @Reference OAuthFactory oauthFactory, + @Reference Inbox inbox, @Reference ThingRegistry thingRegistry, @Reference LocaleProvider localeProvider) { + this.httpService = httpService; + this.oauthFactory = oauthFactory; + this.inbox = inbox; + this.thingRegistry = thingRegistry; + this.localeProvider = localeProvider; + } + + @Nullable + public AccountOverviewServlet getAccountOverviewServlet() { + return accountOverviewServlet; + } + + @Nullable + public ForwardToLoginServlet getForwardToLoginServlet() { + return forwardToLoginServlet; + } + + @Nullable + public ResultServlet getResultServlet() { + return resultServlet; + } + + @Nullable + public SuccessServlet getSuccessServlet() { + return successServlet; + } + + @Nullable + public CreateBridgeServlet getCreateBridgeServlet() { + return createBridgeServlet; + } + + @Activate + protected void activate(ComponentContext componentContext, Map properties) { + registerWebsite(componentContext.getBundleContext()); + } + + private void registerWebsite(BundleContext bundleContext) { + ResourceLoader resourceLoader = new ResourceLoader(WEBSITE_RESOURCE_BASE_PATH, bundleContext); + OAuthAuthorizationHandler authorizationHandler = new OAuthAuthorizationHandlerImpl(oauthFactory, + ThreadPoolManager.getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON)); + + try { + HttpContext httpContext = httpService.createDefaultHttpContext(); + httpService.registerServlet(ROOT_ALIAS, + accountOverviewServlet = new AccountOverviewServlet(resourceLoader, thingRegistry, inbox), + new Hashtable<>(), httpContext); + httpService.registerServlet(PAIR_ALIAS, new PairAccountServlet(resourceLoader), new Hashtable<>(), + httpContext); + httpService.registerServlet(FORWARD_TO_LOGIN_ALIAS, + forwardToLoginServlet = new ForwardToLoginServlet(authorizationHandler), new Hashtable<>(), + httpContext); + httpService.registerServlet(RESULT_ALIAS, resultServlet = new ResultServlet(authorizationHandler), + new Hashtable<>(), httpContext); + httpService.registerServlet(SUCCESS_ALIAS, + successServlet = new SuccessServlet(resourceLoader, createLanguageProvider()), new Hashtable<>(), + httpContext); + httpService.registerServlet(CREATE_BRIDGE_THING_ALIAS, + createBridgeServlet = new CreateBridgeServlet(inbox, thingRegistry), new Hashtable<>(), + httpContext); + httpService.registerServlet(FAILURE_ALIAS, new FailureServlet(resourceLoader), new Hashtable<>(), + httpContext); + httpService.registerResources(CSS_ALIAS, WEBSITE_CSS_RESOURCE_PATH, httpContext); + httpService.registerResources(JS_ALIAS, WEBSITE_JS_RESOURCE_PATH, httpContext); + httpService.registerResources(IMG_ALIAS, WEBSITE_IMG_RESOURCE_PATH, httpContext); + logger.debug("Registered Miele Cloud binding website at /mielecloud"); + } catch (NamespaceException | ServletException e) { + logger.warn( + "Failed to register Miele Cloud binding website. Miele Cloud binding website will not be available.", + e); + unregisterWebsite(); + } + } + + private LanguageProvider createLanguageProvider() { + return new CombiningLanguageProvider(new OpenHabLanguageProvider(localeProvider), new JvmLanguageProvider()); + } + + @Deactivate + protected void deactivate() { + unregisterWebsite(); + } + + private void unregisterWebsite() { + unregisterWebResource(ROOT_ALIAS); + unregisterWebResource(PAIR_ALIAS); + unregisterWebResource(FORWARD_TO_LOGIN_ALIAS); + unregisterWebResource(RESULT_ALIAS); + unregisterWebResource(SUCCESS_ALIAS); + unregisterWebResource(CREATE_BRIDGE_THING_ALIAS); + unregisterWebResource(CSS_ALIAS); + unregisterWebResource(JS_ALIAS); + unregisterWebResource(IMG_ALIAS); + forwardToLoginServlet = null; + resultServlet = null; + createBridgeServlet = null; + logger.debug("Unregistered Miele Cloud binding website at /mielecloud"); + } + + private void unregisterWebResource(String alias) { + try { + httpService.unregister(alias); + } catch (IllegalArgumentException e) { + logger.warn("Failed to unregister Miele Cloud binding website alias {}", alias, e); + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandler.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandler.java new file mode 100644 index 000000000..9acf5030a --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandler.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.mielecloud.internal.config.exception.NoOngoingAuthorizationException; +import org.openhab.binding.mielecloud.internal.config.exception.OngoingAuthorizationException; +import org.openhab.core.thing.ThingUID; + +/** + * Handles OAuth 2 authorization processes. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public interface OAuthAuthorizationHandler { + /** + * Begins the authorization process after the user provided client ID, client secret and a bridge ID. + * + * @param clientId Client ID. + * @param clientSecret Client secret. + * @param bridgeUid The UID of the bridge to authorize. + * @param email E-mail address identifying the account to authorize. + * @throws OngoingAuthorizationException if there already is an ongoing authorization. + */ + void beginAuthorization(String clientId, String clientSecret, ThingUID bridgeUid, String email); + + /** + * Creates the authorization URL for the ongoing authorization. + * + * @param redirectUri The URI to which the user is redirected after a successful login. This should point to our own + * service. + * @return The authorization URL to which the user is redirected for the log in. + * @throws NoOngoingAuthorizationException if there is no ongoing authorization. + * @throws OAuthException if the authorization URL cannot be determined. In this case the ongoing authorization is + * cancelled. + */ + String getAuthorizationUrl(String redirectUri); + + /** + * Gets the UID of the bridge that is currently being authorized. + */ + ThingUID getBridgeUid(); + + /** + * Gets the e-mail address associated with the account that is currently being authorized. + */ + String getEmail(); + + /** + * Completes the authorization by extracting the authorization code from the given redirection URL, fetching the + * access token response and persisting it. After this method succeeded the access token can be read from the + * persistent storage. + * + * @param redirectUrlWithParameters The URL the remote service redirected the user to. This is the URL our servlet + * was called with. + * @throws NoOngoingAuthorizationException if there is no ongoing authorization. + * @throws OAuthException if the authorization failed. In this case the ongoing authorization is cancelled. + */ + void completeAuthorization(String redirectUrlWithParameters); + + /** + * Gets the access token from persistent storage. + * + * @param email E-mail address for which the access token is requested. + * @return The access token. + * @throws OAuthException if the access token cannot be obtained. + */ + String getAccessToken(String email); +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandlerImpl.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandlerImpl.java new file mode 100644 index 000000000..86ef742af --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/OAuthAuthorizationHandlerImpl.java @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mielecloud.internal.auth.OAuthException; +import org.openhab.binding.mielecloud.internal.config.exception.NoOngoingAuthorizationException; +import org.openhab.binding.mielecloud.internal.config.exception.OngoingAuthorizationException; +import org.openhab.binding.mielecloud.internal.webservice.DefaultMieleWebservice; +import org.openhab.core.auth.client.oauth2.AccessTokenResponse; +import org.openhab.core.auth.client.oauth2.OAuthClientService; +import org.openhab.core.auth.client.oauth2.OAuthFactory; +import org.openhab.core.auth.client.oauth2.OAuthResponseException; +import org.openhab.core.thing.ThingUID; + +/** + * {@link OAuthAuthorizationHandler} implementation handling the OAuth 2 authorization via openHAB services. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class OAuthAuthorizationHandlerImpl implements OAuthAuthorizationHandler { + private static final String TOKEN_URL = DefaultMieleWebservice.THIRD_PARTY_ENDPOINTS_BASENAME + "/token"; + private static final String AUTHORIZATION_URL = DefaultMieleWebservice.THIRD_PARTY_ENDPOINTS_BASENAME + "/login"; + + private static final long AUTHORIZATION_TIMEOUT_IN_MINUTES = 5; + + private final OAuthFactory oauthFactory; + private final ScheduledExecutorService scheduler; + + @Nullable + private OAuthClientService oauthClientService; + @Nullable + private ThingUID bridgeUid; + @Nullable + private String email; + @Nullable + private String redirectUri; + @Nullable + private ScheduledFuture timer; + @Nullable + private LocalDateTime timerExpiryTimestamp; + + /** + * Creates a new {@link OAuthAuthorizationHandlerImpl}. + * + * @param oauthFactory Factory for accessing the {@link OAuthClientService}. + * @param scheduler System-wide scheduler. + */ + public OAuthAuthorizationHandlerImpl(OAuthFactory oauthFactory, ScheduledExecutorService scheduler) { + this.oauthFactory = oauthFactory; + this.scheduler = scheduler; + } + + @Override + public synchronized void beginAuthorization(String clientId, String clientSecret, ThingUID bridgeUid, + String email) { + if (this.oauthClientService != null) { + throw new OngoingAuthorizationException("There is already an ongoing authorization!", timerExpiryTimestamp); + } + + this.oauthClientService = oauthFactory.createOAuthClientService(email, TOKEN_URL, AUTHORIZATION_URL, clientId, + clientSecret, null, false); + this.bridgeUid = bridgeUid; + this.email = email; + redirectUri = null; + timer = null; + timerExpiryTimestamp = null; + } + + @Override + public synchronized String getAuthorizationUrl(String redirectUri) { + final OAuthClientService oauthClientService = this.oauthClientService; + if (oauthClientService == null) { + throw new NoOngoingAuthorizationException("There is no ongoing authorization!"); + } + + this.redirectUri = redirectUri; + try { + timer = scheduler.schedule(this::cancelAuthorization, AUTHORIZATION_TIMEOUT_IN_MINUTES, TimeUnit.MINUTES); + timerExpiryTimestamp = LocalDateTime.now().plusMinutes(AUTHORIZATION_TIMEOUT_IN_MINUTES); + return oauthClientService.getAuthorizationUrl(redirectUri, null, null); + } catch (org.openhab.core.auth.client.oauth2.OAuthException e) { + abortTimer(); + cancelAuthorization(); + throw new OAuthException("Failed to determine authorization URL: " + e.getMessage(), e); + } + } + + @Override + public ThingUID getBridgeUid() { + final ThingUID bridgeUid = this.bridgeUid; + if (bridgeUid == null) { + throw new NoOngoingAuthorizationException("There is no ongoing authorization."); + } + return bridgeUid; + } + + @Override + public String getEmail() { + final String email = this.email; + if (email == null) { + throw new NoOngoingAuthorizationException("There is no ongoing authorization."); + } + return email; + } + + @Override + public synchronized void completeAuthorization(String redirectUrlWithParameters) { + abortTimer(); + + final OAuthClientService oauthClientService = this.oauthClientService; + if (oauthClientService == null) { + throw new NoOngoingAuthorizationException("There is no ongoing authorization."); + } + + try { + String authorizationCode = oauthClientService.extractAuthCodeFromAuthResponse(redirectUrlWithParameters); + + // Although this method is called "get" it actually fetches and stores the token response as a side effect. + oauthClientService.getAccessTokenResponseByAuthorizationCode(authorizationCode, redirectUri); + } catch (IOException e) { + throw new OAuthException("Network error while retrieving token response: " + e.getMessage(), e); + } catch (OAuthResponseException e) { + throw new OAuthException("Failed to retrieve token response: " + e.getMessage(), e); + } catch (org.openhab.core.auth.client.oauth2.OAuthException e) { + throw new OAuthException("Error while processing Miele service response: " + e.getMessage(), e); + } finally { + this.oauthClientService = null; + this.bridgeUid = null; + this.email = null; + this.redirectUri = null; + } + } + + /** + * Aborts the timer. + * + * Note: All calls to this method must be {@code synchronized} to ensure thread-safety. Also note that + * {@link #cancelAuthorization()} is {@code synchronized} so the execution of this method and + * {@link #cancelAuthorization()} cannot overlap. Therefore, this method is an atomic operation from the timer's + * perspective. + */ + private void abortTimer() { + final ScheduledFuture timer = this.timer; + if (timer == null) { + return; + } + + if (!timer.isDone()) { + timer.cancel(false); + } + this.timer = null; + timerExpiryTimestamp = null; + } + + private synchronized void cancelAuthorization() { + oauthClientService = null; + bridgeUid = null; + email = null; + redirectUri = null; + final ScheduledFuture timer = this.timer; + if (timer != null) { + timer.cancel(false); + this.timer = null; + timerExpiryTimestamp = null; + } + } + + @Override + public String getAccessToken(String email) { + OAuthClientService clientService = oauthFactory.getOAuthClientService(email); + if (clientService == null) { + throw new OAuthException("There is no access token registered for '" + email + "'"); + } + + try { + AccessTokenResponse response = clientService.getAccessTokenResponse(); + if (response == null) { + throw new OAuthException( + "There is no access token in the persistent storage or it already expired and could not be refreshed"); + } else { + return response.getAccessToken(); + } + } catch (org.openhab.core.auth.client.oauth2.OAuthException e) { + throw new OAuthException("Failed to read access token from persistent storage: " + e.getMessage(), e); + } catch (IOException e) { + throw new OAuthException( + "Network error during token refresh or error while reading from persistent storage: " + + e.getMessage(), + e); + } catch (OAuthResponseException e) { + throw new OAuthException("Failed to retrieve token response: " + e.getMessage(), e); + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/ThingsTemplateGenerator.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/ThingsTemplateGenerator.java new file mode 100644 index 000000000..613466ce6 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/ThingsTemplateGenerator.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; + +/** + * Generator for templates which can be copy-pasted into .things files by the user. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public class ThingsTemplateGenerator { + /** + * Creates a template for the bridge. + * + * @param bridgeId Id of the bridge (last part of the thing UID). + * @param locale Locale for accessing the Miele cloud service. + * @return The template. + */ + public String createBridgeConfigurationTemplate(String bridgeId, String email, String locale) { + var builder = new StringBuilder(); + builder.append("Bridge "); + builder.append(MieleCloudBindingConstants.THING_TYPE_BRIDGE.getAsString()); + builder.append(":"); + builder.append(bridgeId); + builder.append(" [ email=\""); + builder.append(email); + builder.append("\", locale=\""); + builder.append(locale); + builder.append("\" ]"); + return builder.toString(); + } + + /** + * Creates a complete template containing the bridge and all paired devices. + * + * @param bridge The bridge which is used to pair the things. + * @param pairedThings The paired things. + * @param discoveryResults The discovery results which can be paired. + * @return The template. + */ + public String createBridgeAndThingConfigurationTemplate(Bridge bridge, List pairedThings, + List discoveryResults) { + StringBuilder result = new StringBuilder(); + result.append(createBridgeConfigurationTemplate(bridge.getUID().getId(), + bridge.getConfiguration().get(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL).toString(), + getLocale(bridge))); + result.append(" {\n"); + + for (Thing thing : pairedThings) { + result.append(" ").append(createThingConfigurationTemplate(thing)).append("\n"); + } + + for (DiscoveryResult discoveryResult : discoveryResults) { + result.append(" ").append(createThingConfigurationTemplate(discoveryResult)).append("\n"); + } + + result.append("}"); + return result.toString(); + } + + private String getLocale(Bridge bridge) { + var locale = bridge.getConfiguration().get(MieleCloudBindingConstants.CONFIG_PARAM_LOCALE); + if (locale instanceof String) { + return (String) locale; + } else { + return "en"; + } + } + + private String createThingConfigurationTemplate(Thing thing) { + StringBuilder result = new StringBuilder(); + result.append("Thing ").append(thing.getThingTypeUID().getId()).append(" ").append(thing.getUID().getId()) + .append(" "); + + final String label = thing.getLabel(); + if (label != null) { + result.append("\"").append(label).append("\" "); + } + + result.append("[ "); + result.append("deviceIdentifier=\""); + result.append( + thing.getConfiguration().get(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER).toString()); + result.append("\""); + result.append(" ]"); + return result.toString(); + } + + private String createThingConfigurationTemplate(DiscoveryResult discoveryResult) { + return "Thing " + discoveryResult.getThingTypeUID().getId() + " " + discoveryResult.getThingUID().getId() + + " \"" + discoveryResult.getLabel() + "\" [ deviceIdentifier=\"" + + getProperty(discoveryResult, MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER) + "\" ]"; + } + + private String getProperty(DiscoveryResult discoveryResult, String propertyName) { + var value = discoveryResult.getProperties().get(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER); + if (value == null) { + return ""; + } else { + return value.toString(); + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/BridgeCreationFailedException.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/BridgeCreationFailedException.java new file mode 100644 index 000000000..54c226367 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/BridgeCreationFailedException.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.exception; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Exception thrown when a bridge cannot be created in the configuration flow. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class BridgeCreationFailedException extends RuntimeException { + private static final long serialVersionUID = -6150154333256723312L; +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/BridgeReconfigurationFailedException.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/BridgeReconfigurationFailedException.java new file mode 100644 index 000000000..97f66aae2 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/BridgeReconfigurationFailedException.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.exception; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Exception thrown when reconfiguring an existing bridge fails. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public class BridgeReconfigurationFailedException extends RuntimeException { + private static final long serialVersionUID = -6341258448724364940L; + + public BridgeReconfigurationFailedException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/NoOngoingAuthorizationException.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/NoOngoingAuthorizationException.java new file mode 100644 index 000000000..2ba3768b7 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/NoOngoingAuthorizationException.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.exception; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Exception thrown when no authorization is ongoing. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public class NoOngoingAuthorizationException extends RuntimeException { + private static final long serialVersionUID = 3074275827393542416L; + + public NoOngoingAuthorizationException(String message) { + super(message); + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/OngoingAuthorizationException.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/OngoingAuthorizationException.java new file mode 100644 index 000000000..e232ee50d --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/exception/OngoingAuthorizationException.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.exception; + +import java.time.LocalDateTime; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Exception thrown when there already is an ongoing authorization process. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class OngoingAuthorizationException extends RuntimeException { + private static final long serialVersionUID = -6742384930140134244L; + + @Nullable + private final LocalDateTime ongoingAuthorizationExpiryTimestamp; + + /** + * Creates a new {@link OngoingAuthorizationException}. + * + * @param message Exception message. + * @param ongoingAuthorizationExpiryTimestamp Timestamp when the ongoing authorization will expire. + */ + public OngoingAuthorizationException(String message, @Nullable LocalDateTime ongoingAuthorizationExpiryTimestamp) { + super(message); + this.ongoingAuthorizationExpiryTimestamp = ongoingAuthorizationExpiryTimestamp; + } + + /** + * Gets the timestamp representing when the ongoing authorization will expire. + */ + @Nullable + public LocalDateTime getOngoingAuthorizationExpiryTimestamp() { + return ongoingAuthorizationExpiryTimestamp; + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AbstractRedirectionServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AbstractRedirectionServlet.java new file mode 100644 index 000000000..d8a409087 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AbstractRedirectionServlet.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for servlets that have no visible frontend and just serve the purpose of redirecting the user to another + * website. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public abstract class AbstractRedirectionServlet extends HttpServlet { + private static final long serialVersionUID = 4280026301732437523L; + + private final Logger logger = LoggerFactory.getLogger(AbstractRedirectionServlet.class); + + @Override + protected void doGet(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response) + throws ServletException, IOException { + if (response == null) { + logger.warn("Ignoring received request without response."); + return; + } + if (request == null) { + logger.warn("Ignoring illegal request."); + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + response.sendRedirect(getRedirectionDestination(request)); + } + + /** + * Gets the redirection destination. This can be a relative or absolute path or a link to another website. + * + * @param request The original request sent by the browser. + * @return The redirection destination. + */ + protected abstract String getRedirectionDestination(HttpServletRequest request); +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AbstractShowPageServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AbstractShowPageServlet.java new file mode 100644 index 000000000..8faa7bb2c --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AbstractShowPageServlet.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for servlets that show a visible frontend in the browser. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public abstract class AbstractShowPageServlet extends HttpServlet { + private static final long serialVersionUID = 3820684716753275768L; + + private static final String CONTENT_TYPE = "text/html;charset=UTF-8"; + + private final Logger logger = LoggerFactory.getLogger(AbstractShowPageServlet.class); + + private final ResourceLoader resourceLoader; + + protected ResourceLoader getResourceLoader() { + return resourceLoader; + } + + /** + * Creates a new {@link AbstractShowPageServlet}. + * + * @param resourceLoader Loader for resource files. + */ + public AbstractShowPageServlet(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + @Override + protected void doGet(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response) + throws ServletException, IOException { + if (response == null) { + logger.warn("Ignoring received request without response."); + return; + } + if (request == null) { + logger.warn("Ignoring illegal request."); + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + try { + String html = handleGetRequest(request, response); + response.setContentType(CONTENT_TYPE); + response.getWriter().write(html); + response.getWriter().close(); + } catch (MieleHttpException e) { + response.sendError(e.getHttpErrorCode()); + } catch (IOException e) { + logger.warn("Failed to load resources.", e); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + /** + * Handles a GET request. + * + * @param request The request. + * @param response The response. + * @return A rendered HTML body to be displayed in the browser. The body will be framed by the binding's frontend + * layout. + * @throws MieleHttpException if an error occurs that should be handled by sending a default error response. + * @throws IOException if an error occurs while loading resources. + */ + protected abstract String handleGetRequest(HttpServletRequest request, HttpServletResponse response) + throws MieleHttpException, IOException; +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AccountOverviewServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AccountOverviewServlet.java new file mode 100644 index 000000000..8944e0d3f --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/AccountOverviewServlet.java @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants; +import org.openhab.binding.mielecloud.internal.config.ThingsTemplateGenerator; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.inbox.Inbox; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingRegistry; +import org.openhab.core.thing.ThingStatus; + +/** + * Servlet showing the account overview page. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class AccountOverviewServlet extends AbstractShowPageServlet { + private static final long serialVersionUID = -4551210904923220429L; + private static final String ACCOUNTS_SKELETON = "index.html"; + + private static final String BRIDGES_TITLE_PLACEHOLDER = ""; + private static final String BRIDGES_PLACEHOLDER = ""; + private static final String NO_SSL_WARNING_PLACEHOLDER = ""; + + private final ThingRegistry thingRegistry; + private final Inbox inbox; + private final ThingsTemplateGenerator templateGenerator; + + /** + * Creates a new {@link AccountOverviewServlet}. + * + * @param resourceLoader Loader to use for resources. + * @param thingRegistry openHAB thing registry. + * @param inbox openHAB inbox for discovery results. + */ + public AccountOverviewServlet(ResourceLoader resourceLoader, ThingRegistry thingRegistry, Inbox inbox) { + super(resourceLoader); + this.thingRegistry = thingRegistry; + this.inbox = inbox; + this.templateGenerator = new ThingsTemplateGenerator(); + } + + @Override + protected String handleGetRequest(HttpServletRequest request, HttpServletResponse response) + throws MieleHttpException, IOException { + String skeleton = getResourceLoader().loadResourceAsString(ACCOUNTS_SKELETON); + skeleton = renderBridges(skeleton); + skeleton = renderSslWarning(request, skeleton); + return skeleton; + } + + private String renderBridges(String skeleton) { + List bridges = thingRegistry.stream().filter(this::isMieleCloudBridge).collect(Collectors.toList()); + if (bridges.isEmpty()) { + return renderNoBridges(skeleton); + } else { + return renderBridgesIntoSkeleton(skeleton, bridges); + } + } + + private String renderNoBridges(String skeleton) { + return skeleton.replace(BRIDGES_TITLE_PLACEHOLDER, "There is no account paired at the moment.") + .replace(BRIDGES_PLACEHOLDER, ""); + } + + private String renderBridgesIntoSkeleton(String skeleton, List bridges) { + StringBuilder builder = new StringBuilder(); + + int index = 0; + Iterator bridgeIterator = bridges.iterator(); + while (bridgeIterator.hasNext()) { + builder.append(renderBridge(bridgeIterator.next(), index)); + index++; + } + + return skeleton.replace(BRIDGES_TITLE_PLACEHOLDER, "The following bridges are paired") + .replace(BRIDGES_PLACEHOLDER, builder.toString()); + } + + private String renderBridge(Thing bridge, int index) { + StringBuilder builder = new StringBuilder(); + builder.append("

  • \n"); + + String thingUid = bridge.getUID().getAsString(); + String thingId = bridge.getUID().getId(); + builder.append(" "); + builder.append(thingUid.substring(0, thingUid.length() - thingId.length())); + builder.append(" "); + builder.append(thingId); + builder.append(" "); + builder.append(bridge.getConfiguration().get(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL).toString()); + builder.append("\n"); + + builder.append(" "); + builder.append(status.toString()); + builder.append("\n"); + + builder.append(" \n"); + + builder.append(" \n"); + + builder.append("
    \n"); + builder.append( + " You can use this things-file template to pair all available devices:\n"); + builder.append("
    \n"); + builder.append( + " Copy\n"); + builder.append(" \n"); + builder.append("
    \n"); + builder.append("
    \n"); + builder.append("
  • "); + + return builder.toString(); + } + + private String generateConfigurationTemplate(Bridge bridge) { + List pairedThings = thingRegistry.stream().filter(thing -> isConnectedVia(thing, bridge)) + .collect(Collectors.toList()); + List discoveryResults = inbox.stream() + .filter(discoveryResult -> willConnectVia(discoveryResult, bridge)).collect(Collectors.toList()); + + return templateGenerator.createBridgeAndThingConfigurationTemplate(bridge, pairedThings, discoveryResults); + } + + private boolean isConnectedVia(Thing thing, Bridge bridge) { + return bridge.getUID().equals(thing.getBridgeUID()); + } + + private boolean willConnectVia(DiscoveryResult discoveryResult, Bridge bridge) { + return bridge.getUID().equals(discoveryResult.getBridgeUID()); + } + + private boolean isMieleCloudBridge(Thing thing) { + return MieleCloudBindingConstants.THING_TYPE_BRIDGE.equals(thing.getThingTypeUID()); + } + + private String renderSslWarning(HttpServletRequest request, String skeleton) { + if (!request.isSecure()) { + return skeleton.replace(NO_SSL_WARNING_PLACEHOLDER, "
    \n" + + " Warning: We strongly advice to proceed only with SSL enabled for a secure data exchange.\n" + + " See Securing access to openHAB for details.\n" + + "
    "); + } else { + return skeleton.replace(NO_SSL_WARNING_PLACEHOLDER, ""); + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/CreateBridgeServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/CreateBridgeServlet.java new file mode 100644 index 000000000..3b667ce18 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/CreateBridgeServlet.java @@ -0,0 +1,217 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants; +import org.openhab.binding.mielecloud.internal.config.exception.BridgeCreationFailedException; +import org.openhab.binding.mielecloud.internal.config.exception.BridgeReconfigurationFailedException; +import org.openhab.binding.mielecloud.internal.handler.MieleBridgeHandler; +import org.openhab.binding.mielecloud.internal.util.EmailValidator; +import org.openhab.binding.mielecloud.internal.util.LocaleValidator; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.inbox.Inbox; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingRegistry; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Servlet that automatically creates a bridge and then redirects the browser to the account overview page. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class CreateBridgeServlet extends AbstractRedirectionServlet { + private static final String MIELE_CLOUD_BRIDGE_NAME = "Cloud Connector"; + private static final String MIELE_CLOUD_BRIDGE_LABEL = "Miele@home Account"; + + private static final String LOCALE_PARAMETER_NAME = "locale"; + public static final String BRIDGE_UID_PARAMETER_NAME = "bridgeUid"; + public static final String EMAIL_PARAMETER_NAME = "email"; + + private static final long serialVersionUID = -2912042079128722887L; + + private static final String DEFAULT_LOCALE = "en"; + + private static final long ONLINE_WAIT_TIMEOUT_IN_MILLISECONDS = 5000; + private static final long DISCOVERY_COMPLETION_TIMEOUT_IN_MILLISECONDS = 5000; + private static final long CHECK_INTERVAL_IN_MILLISECONDS = 100; + + private final Logger logger = LoggerFactory.getLogger(CreateBridgeServlet.class); + + private final Inbox inbox; + private final ThingRegistry thingRegistry; + + /** + * Creates a new {@link CreateBridgeServlet}. + * + * @param inbox openHAB inbox for discovery results. + * @param thingRegistry openHAB thing registry. + */ + public CreateBridgeServlet(Inbox inbox, ThingRegistry thingRegistry) { + this.inbox = inbox; + this.thingRegistry = thingRegistry; + } + + @Override + protected String getRedirectionDestination(HttpServletRequest request) { + String bridgeUidString = request.getParameter(BRIDGE_UID_PARAMETER_NAME); + if (bridgeUidString == null || bridgeUidString.isEmpty()) { + logger.warn("Cannot create bridge: Bridge UID is missing."); + return "/mielecloud/failure?" + FailureServlet.MISSING_BRIDGE_UID_PARAMETER_NAME + "=true"; + } + + String email = request.getParameter(EMAIL_PARAMETER_NAME); + if (email == null || email.isEmpty()) { + logger.warn("Cannot create bridge: E-mail address is missing."); + return "/mielecloud/failure?" + FailureServlet.MISSING_EMAIL_PARAMETER_NAME + "=true"; + } + + ThingUID bridgeUid = null; + try { + bridgeUid = new ThingUID(bridgeUidString); + } catch (IllegalArgumentException e) { + logger.warn("Cannot create bridge: Bridge UID '{}' is malformed.", bridgeUid); + return "/mielecloud/failure?" + FailureServlet.MALFORMED_BRIDGE_UID_PARAMETER_NAME + "=true"; + } + + if (!EmailValidator.isValid(email)) { + logger.warn("Cannot create bridge: E-mail address '{}' is malformed.", email); + return "/mielecloud/failure?" + FailureServlet.MALFORMED_EMAIL_PARAMETER_NAME + "=true"; + } + + String locale = getValidLocale(request.getParameter(LOCALE_PARAMETER_NAME)); + + logger.debug("Auto configuring Miele account using locale '{}' (requested locale was '{}')", locale, + request.getParameter(LOCALE_PARAMETER_NAME)); + try { + Thing bridge = pairOrReconfigureBridge(locale, bridgeUid, email); + waitForBridgeToComeOnline(bridge); + return "/mielecloud"; + } catch (BridgeReconfigurationFailedException e) { + logger.warn("{}", e.getMessage()); + return "/mielecloud/success?" + SuccessServlet.BRIDGE_RECONFIGURATION_FAILED_PARAMETER_NAME + "=true&" + + SuccessServlet.BRIDGE_UID_PARAMETER_NAME + "=" + bridgeUidString + "&" + + SuccessServlet.EMAIL_PARAMETER_NAME + "=" + email; + } catch (BridgeCreationFailedException e) { + logger.warn("Thing creation failed because there was no binding available that supports the thing."); + return "/mielecloud/success?" + SuccessServlet.BRIDGE_CREATION_FAILED_PARAMETER_NAME + "=true&" + + SuccessServlet.BRIDGE_UID_PARAMETER_NAME + "=" + bridgeUidString + "&" + + SuccessServlet.EMAIL_PARAMETER_NAME + "=" + email; + } + } + + private Thing pairOrReconfigureBridge(String locale, ThingUID bridgeUid, String email) { + DiscoveryResult result = DiscoveryResultBuilder.create(bridgeUid) + .withRepresentationProperty(Thing.PROPERTY_MODEL_ID).withLabel(MIELE_CLOUD_BRIDGE_LABEL) + .withProperty(Thing.PROPERTY_MODEL_ID, MIELE_CLOUD_BRIDGE_NAME) + .withProperty(MieleCloudBindingConstants.CONFIG_PARAM_LOCALE, locale) + .withProperty(MieleCloudBindingConstants.CONFIG_PARAM_EMAIL, email).build(); + if (inbox.add(result)) { + return pairBridge(bridgeUid); + } else { + return reconfigureBridge(bridgeUid, locale, email); + } + } + + private Thing pairBridge(ThingUID thingUid) { + Thing thing = inbox.approve(thingUid, MIELE_CLOUD_BRIDGE_LABEL, null); + if (thing == null) { + throw new BridgeCreationFailedException(); + } + + logger.debug("Successfully created bridge {}", thingUid); + return thing; + } + + private Thing reconfigureBridge(ThingUID thingUid, String locale, String email) { + logger.debug("Thing already exists. Modifying configuration."); + Thing thing = thingRegistry.get(thingUid); + if (thing == null) { + throw new BridgeReconfigurationFailedException( + "Cannot modify non existing bridge: Could neither add bridge via inbox nor find existing bridge."); + } + + ThingHandler handler = thing.getHandler(); + if (handler == null) { + throw new BridgeReconfigurationFailedException("Bridge exists but has no handler."); + } + if (!(handler instanceof MieleBridgeHandler)) { + throw new BridgeReconfigurationFailedException("Bridge handler is of wrong type, expected '" + + MieleBridgeHandler.class.getSimpleName() + "' but got '" + handler.getClass().getName() + "'."); + } + + MieleBridgeHandler bridgeHandler = (MieleBridgeHandler) handler; + bridgeHandler.disposeWebservice(); + bridgeHandler.initializeWebservice(); + + return thing; + } + + private String getValidLocale(@Nullable String localeParameterValue) { + if (localeParameterValue == null || localeParameterValue.isEmpty() + || !LocaleValidator.isValidLanguage(localeParameterValue)) { + return DEFAULT_LOCALE; + } else { + return localeParameterValue; + } + } + + private void waitForBridgeToComeOnline(Thing bridge) { + try { + waitForConditionWithTimeout(() -> bridge.getStatus() == ThingStatus.ONLINE, + ONLINE_WAIT_TIMEOUT_IN_MILLISECONDS); + waitForConditionWithTimeout(new DiscoveryResultCountDoesNotChangeCondition(), + DISCOVERY_COMPLETION_TIMEOUT_IN_MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private void waitForConditionWithTimeout(BooleanSupplier condition, long timeoutInMilliseconds) + throws InterruptedException { + long remainingWaitTime = timeoutInMilliseconds; + while (!condition.getAsBoolean() && remainingWaitTime > 0) { + TimeUnit.MILLISECONDS.sleep(CHECK_INTERVAL_IN_MILLISECONDS); + remainingWaitTime -= CHECK_INTERVAL_IN_MILLISECONDS; + } + } + + private class DiscoveryResultCountDoesNotChangeCondition implements BooleanSupplier { + private long previousDiscoveryResultCount = 0; + + @Override + public boolean getAsBoolean() { + var discoveryResultCount = countOwnDiscoveryResults(); + var discoveryResultCountUnchanged = previousDiscoveryResultCount == discoveryResultCount; + previousDiscoveryResultCount = discoveryResultCount; + return discoveryResultCountUnchanged; + } + + private long countOwnDiscoveryResults() { + return inbox.stream().map(DiscoveryResult::getBindingId) + .filter(MieleCloudBindingConstants.BINDING_ID::equals).count(); + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/FailureServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/FailureServlet.java new file mode 100644 index 000000000..a24802b3b --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/FailureServlet.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Servlet showing a failure page. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public class FailureServlet extends AbstractShowPageServlet { + private static final long serialVersionUID = -5195984256535664942L; + + public static final String OAUTH2_ERROR_PARAMETER_NAME = "oauth2Error"; + public static final String ILLEGAL_RESPONSE_PARAMETER_NAME = "illegalResponse"; + public static final String NO_ONGOING_AUTHORIZATION_PARAMETER_NAME = "noOngoingAuthorization"; + public static final String FAILED_TO_COMPLETE_AUTHORIZATION_PARAMETER_NAME = "failedToCompleteAuthorization"; + public static final String MISSING_BRIDGE_UID_PARAMETER_NAME = "missingBridgeUid"; + public static final String MISSING_EMAIL_PARAMETER_NAME = "missingEmail"; + public static final String MALFORMED_BRIDGE_UID_PARAMETER_NAME = "malformedBridgeUid"; + public static final String MALFORMED_EMAIL_PARAMETER_NAME = "malformedEmail"; + public static final String MISSING_REQUEST_URL_PARAMETER_NAME = "missingRequestUrl"; + + public static final String OAUTH2_ERROR_ACCESS_DENIED = "access_denied"; + public static final String OAUTH2_ERROR_INVALID_REQUEST = "invalid_request"; + public static final String OAUTH2_ERROR_UNAUTHORIZED_CLIENT = "unauthorized_client"; + public static final String OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE = "unsupported_response_type"; + public static final String OAUTH2_ERROR_INVALID_SCOPE = "invalid_scope"; + public static final String OAUTH2_ERROR_SERVER_ERROR = "server_error"; + public static final String OAUTH2_ERROR_TEMPORARY_UNAVAILABLE = "temporarily_unavailable"; + + private static final String ERROR_MESSAGE_TEXT_PLACEHOLDER = ""; + + /** + * Creates a new {@link FailureServlet}. + * + * @param resourceLoader Loader to use for resources. + */ + public FailureServlet(ResourceLoader resourceLoader) { + super(resourceLoader); + } + + @Override + protected String handleGetRequest(HttpServletRequest request, HttpServletResponse response) + throws MieleHttpException, IOException { + return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, + getErrorMessage(request)); + } + + private String getErrorMessage(HttpServletRequest request) { + String oauth2Error = request.getParameter(OAUTH2_ERROR_PARAMETER_NAME); + if (oauth2Error != null) { + return getOAuth2ErrorMessage(oauth2Error); + } else if (ServletUtil.isParameterEnabled(request, ILLEGAL_RESPONSE_PARAMETER_NAME)) { + return "Miele cloud service returned an illegal response."; + } else if (ServletUtil.isParameterEnabled(request, NO_ONGOING_AUTHORIZATION_PARAMETER_NAME)) { + return "There is no ongoing authorization. Please start an authorization first."; + } else if (ServletUtil.isParameterEnabled(request, FAILED_TO_COMPLETE_AUTHORIZATION_PARAMETER_NAME)) { + return "Completing the final authorization request failed. Please try the config flow again."; + } else if (ServletUtil.isParameterEnabled(request, MISSING_BRIDGE_UID_PARAMETER_NAME)) { + return "Missing bridge UID."; + } else if (ServletUtil.isParameterEnabled(request, MISSING_EMAIL_PARAMETER_NAME)) { + return "Missing e-mail address."; + } else if (ServletUtil.isParameterEnabled(request, MALFORMED_BRIDGE_UID_PARAMETER_NAME)) { + return "Malformed bridge UID."; + } else if (ServletUtil.isParameterEnabled(request, MALFORMED_EMAIL_PARAMETER_NAME)) { + return "Malformed e-mail address."; + } else if (ServletUtil.isParameterEnabled(request, MISSING_REQUEST_URL_PARAMETER_NAME)) { + return "Missing request URL. Please try the config flow again."; + } else { + return "Unknown error."; + } + } + + private String getOAuth2ErrorMessage(String oauth2Error) { + return "OAuth2 authentication with Miele cloud service failed: " + getOAuth2ErrorDetailMessage(oauth2Error); + } + + private String getOAuth2ErrorDetailMessage(String oauth2Error) { + switch (oauth2Error) { + case OAUTH2_ERROR_ACCESS_DENIED: + return "Access denied."; + case OAUTH2_ERROR_INVALID_REQUEST: + return "Malformed request."; + case OAUTH2_ERROR_UNAUTHORIZED_CLIENT: + return "Account not authorized to request authorization code."; + case OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE: + return "Obtaining an authorization code is not supported."; + case OAUTH2_ERROR_INVALID_SCOPE: + return "Invalid scope."; + case OAUTH2_ERROR_SERVER_ERROR: + return "Unexpected server error."; + case OAUTH2_ERROR_TEMPORARY_UNAVAILABLE: + return "Authorization server temporarily unavailable."; + default: + return "Unknown error code \"" + oauth2Error + "\"."; + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ForwardToLoginServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ForwardToLoginServlet.java new file mode 100644 index 000000000..e817463ad --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ForwardToLoginServlet.java @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants; +import org.openhab.binding.mielecloud.internal.auth.OAuthException; +import org.openhab.binding.mielecloud.internal.config.OAuthAuthorizationHandler; +import org.openhab.binding.mielecloud.internal.config.exception.NoOngoingAuthorizationException; +import org.openhab.binding.mielecloud.internal.config.exception.OngoingAuthorizationException; +import org.openhab.binding.mielecloud.internal.util.EmailValidator; +import org.openhab.core.thing.ThingUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Servlet gathers and processes required information to perform an authorization with the Miele cloud service + * and create a bridge afterwards. Required parameters are the client ID, client secret, an ID for the bridge and an + * e-mail address. If the given parameters are valid, the browser is redirected to the Miele service login. Otherwise, + * the browser is redirected to the previous page with an according error message. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class ForwardToLoginServlet extends AbstractRedirectionServlet { + private static final long serialVersionUID = -9094642228439994183L; + + public static final String CLIENT_ID_PARAMETER_NAME = "clientId"; + public static final String CLIENT_SECRET_PARAMETER_NAME = "clientSecret"; + public static final String BRIDGE_ID_PARAMETER_NAME = "bridgeId"; + public static final String EMAIL_PARAMETER_NAME = "email"; + + private final Logger logger = LoggerFactory.getLogger(ForwardToLoginServlet.class); + + private final OAuthAuthorizationHandler authorizationHandler; + + /** + * Creates a new {@link ForwardToLoginServlet}. + * + * @param authorizationHandler Handler implementing the OAuth authorization process. + */ + public ForwardToLoginServlet(OAuthAuthorizationHandler authorizationHandler) { + this.authorizationHandler = authorizationHandler; + } + + @Override + protected String getRedirectionDestination(HttpServletRequest request) { + String clientId = request.getParameter(CLIENT_ID_PARAMETER_NAME); + String clientSecret = request.getParameter(CLIENT_SECRET_PARAMETER_NAME); + String bridgeId = request.getParameter(BRIDGE_ID_PARAMETER_NAME); + String email = request.getParameter(EMAIL_PARAMETER_NAME); + + if (clientId == null || clientId.isEmpty()) { + logger.warn("Request is missing client ID."); + return getErrorRedirectionUrl(PairAccountServlet.MISSING_CLIENT_ID_PARAMETER_NAME); + } + if (clientSecret == null || clientSecret.isEmpty()) { + logger.warn("Request is missing client secret."); + return getErrorRedirectionUrl(PairAccountServlet.MISSING_CLIENT_SECRET_PARAMETER_NAME); + } + if (bridgeId == null || bridgeId.isEmpty()) { + logger.warn("Request is missing bridge ID."); + return getErrorRedirectionUrl(PairAccountServlet.MISSING_BRIDGE_ID_PARAMETER_NAME); + } + if (email == null || email.isEmpty()) { + logger.warn("Request is missing e-mail address."); + return getErrorRedirectionUrl(PairAccountServlet.MISSING_EMAIL_PARAMETER_NAME); + } + + ThingUID bridgeUid = null; + try { + bridgeUid = new ThingUID(MieleCloudBindingConstants.THING_TYPE_BRIDGE, bridgeId); + } catch (IllegalArgumentException e) { + logger.warn("Passed bridge ID '{}' is invalid.", bridgeId); + return getErrorRedirectionUrl(PairAccountServlet.MALFORMED_BRIDGE_ID_PARAMETER_NAME); + } + + if (!EmailValidator.isValid(email)) { + logger.warn("Passed e-mail address '{}' is invalid.", email); + return getErrorRedirectionUrl(PairAccountServlet.MALFORMED_EMAIL_PARAMETER_NAME); + } + + try { + authorizationHandler.beginAuthorization(clientId, clientSecret, bridgeUid, email); + } catch (OngoingAuthorizationException e) { + logger.warn("Cannot begin new authorization process while another one is still running."); + return getErrorRedirectUrlWithExpiryTime(e.getOngoingAuthorizationExpiryTimestamp()); + } + + StringBuffer requestUrl = request.getRequestURL(); + if (requestUrl == null) { + return getErrorRedirectionUrl(PairAccountServlet.MISSING_REQUEST_URL_PARAMETER_NAME); + } + + try { + return authorizationHandler.getAuthorizationUrl(deriveRedirectUri(requestUrl.toString())); + } catch (NoOngoingAuthorizationException e) { + logger.warn( + "Failed to create authorization URL: There was no ongoing authorization although we just started one."); + return getErrorRedirectionUrl(PairAccountServlet.NO_ONGOING_AUTHORIZATION_IN_STEP2_PARAMETER_NAME); + } catch (OAuthException e) { + logger.warn("Failed to create authorization URL.", e); + return getErrorRedirectionUrl(PairAccountServlet.FAILED_TO_DERIVE_REDIRECT_URL_PARAMETER_NAME); + } + } + + private String getErrorRedirectUrlWithExpiryTime(@Nullable LocalDateTime ongoingAuthorizationExpiryTimestamp) { + if (ongoingAuthorizationExpiryTimestamp == null) { + return getErrorRedirectionUrl( + PairAccountServlet.ONGOING_AUTHORIZATION_IN_STEP1_EXPIRES_IN_MINUTES_PARAMETER_NAME, + PairAccountServlet.ONGOING_AUTHORIZATION_UNKNOWN_EXPIRY_TIME); + } + + long minutesUntilExpiry = ChronoUnit.MINUTES.between(LocalDateTime.now(), ongoingAuthorizationExpiryTimestamp) + + 1; + return getErrorRedirectionUrl( + PairAccountServlet.ONGOING_AUTHORIZATION_IN_STEP1_EXPIRES_IN_MINUTES_PARAMETER_NAME, + Long.toString(minutesUntilExpiry)); + } + + private String getErrorRedirectionUrl(String errorCode) { + return getErrorRedirectionUrl(errorCode, "true"); + } + + private String getErrorRedirectionUrl(String errorCode, String parameterValue) { + return "/mielecloud/pair?" + errorCode + "=" + parameterValue; + } + + private String deriveRedirectUri(String requestUrl) { + return requestUrl + "/../result"; + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/MieleHttpException.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/MieleHttpException.java new file mode 100644 index 000000000..c5eff7bc6 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/MieleHttpException.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Exception wrapping a HTTP error code for further processing. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class MieleHttpException extends Exception { + private static final long serialVersionUID = 1825214275413952809L; + + private final int httpErrorCode; + + public MieleHttpException(int httpErrorCode) { + this.httpErrorCode = httpErrorCode; + } + + public int getHttpErrorCode() { + return httpErrorCode; + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/PairAccountServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/PairAccountServlet.java new file mode 100644 index 000000000..79d872a7c --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/PairAccountServlet.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.io.IOException; +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Servlet showing the pair account page. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class PairAccountServlet extends AbstractShowPageServlet { + private static final long serialVersionUID = 6565378471951635420L; + + public static final String CLIENT_ID_PARAMETER_NAME = "clientId"; + public static final String CLIENT_SECRET_PARAMETER_NAME = "clientSecret"; + + public static final String MISSING_CLIENT_ID_PARAMETER_NAME = "missingClientId"; + public static final String MISSING_CLIENT_SECRET_PARAMETER_NAME = "missingClientSecret"; + public static final String MISSING_BRIDGE_ID_PARAMETER_NAME = "missingBridgeId"; + public static final String MISSING_EMAIL_PARAMETER_NAME = "missingEmail"; + public static final String MALFORMED_BRIDGE_ID_PARAMETER_NAME = "malformedBridgeId"; + public static final String MALFORMED_EMAIL_PARAMETER_NAME = "malformedEmail"; + public static final String FAILED_TO_DERIVE_REDIRECT_URL_PARAMETER_NAME = "failedToDeriveRedirectUrl"; + public static final String ONGOING_AUTHORIZATION_IN_STEP1_EXPIRES_IN_MINUTES_PARAMETER_NAME = "ongoingAuthorizationInStep1ExpiresInMinutes"; + public static final String ONGOING_AUTHORIZATION_UNKNOWN_EXPIRY_TIME = "unknown"; + public static final String NO_ONGOING_AUTHORIZATION_IN_STEP2_PARAMETER_NAME = "noOngoingAuthorizationInStep2"; + public static final String MISSING_REQUEST_URL_PARAMETER_NAME = "missingRequestUrl"; + + private static final String PAIR_ACCOUNT_SKELETON = "pairing.html"; + + private static final String CLIENT_ID_PLACEHOLDER = ""; + private static final String CLIENT_SECRET_PLACEHOLDER = ""; + private static final String ERROR_MESSAGE_PLACEHOLDER = ""; + + /** + * Creates a new {@link PairAccountServlet}. + * + * @param resourceLoader Loader for resources. + */ + public PairAccountServlet(ResourceLoader resourceLoader) { + super(resourceLoader); + } + + @Override + protected String handleGetRequest(HttpServletRequest request, HttpServletResponse response) + throws MieleHttpException, IOException { + String skeleton = getResourceLoader().loadResourceAsString(PAIR_ACCOUNT_SKELETON); + skeleton = renderClientIdAndClientSecret(request, skeleton); + skeleton = renderErrorMessage(request, skeleton); + return skeleton; + } + + private String renderClientIdAndClientSecret(HttpServletRequest request, String skeleton) { + String prefilledClientId = Optional.ofNullable(request.getParameter(CLIENT_ID_PARAMETER_NAME)).orElse(""); + String prefilledClientSecret = Optional.ofNullable(request.getParameter(CLIENT_SECRET_PARAMETER_NAME)) + .orElse(""); + return skeleton.replace(CLIENT_ID_PLACEHOLDER, prefilledClientId).replace(CLIENT_SECRET_PLACEHOLDER, + prefilledClientSecret); + } + + private String renderErrorMessage(HttpServletRequest request, String skeleton) { + if (ServletUtil.isParameterEnabled(request, MISSING_CLIENT_ID_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    Missing client ID.
    "); + } else if (ServletUtil.isParameterEnabled(request, MISSING_CLIENT_SECRET_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + + "
    Missing client secret.
    "); + } else if (ServletUtil.isParameterEnabled(request, MISSING_BRIDGE_ID_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    Missing bridge ID.
    "); + } else if (ServletUtil.isParameterEnabled(request, MISSING_EMAIL_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    Missing e-mail address.
    "); + } else if (ServletUtil.isParameterEnabled(request, MALFORMED_BRIDGE_ID_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    Malformed bridge ID. A bridge ID may only contain letters, numbers, '-' and '_'!
    "); + } else if (ServletUtil.isParameterEnabled(request, MALFORMED_EMAIL_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    Malformed e-mail address.
    "); + } else if (ServletUtil.isParameterEnabled(request, FAILED_TO_DERIVE_REDIRECT_URL_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    Failed to derive redirect URL.
    "); + } else if (ServletUtil.isParameterPresent(request, + ONGOING_AUTHORIZATION_IN_STEP1_EXPIRES_IN_MINUTES_PARAMETER_NAME)) { + String minutesUntilExpiry = request + .getParameter(ONGOING_AUTHORIZATION_IN_STEP1_EXPIRES_IN_MINUTES_PARAMETER_NAME); + if (ONGOING_AUTHORIZATION_UNKNOWN_EXPIRY_TIME.equals(minutesUntilExpiry)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    There is an authorization ongoing at the moment. Please complete that authorization prior to starting a new one or try again later.
    "); + } else { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    There is an authorization ongoing at the moment. Please complete that authorization prior to starting a new one or try again in " + + minutesUntilExpiry + " minutes.
    "); + } + } else if (ServletUtil.isParameterEnabled(request, NO_ONGOING_AUTHORIZATION_IN_STEP2_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    Failed to start auhtorization process. Are you trying to perform multiple authorizations at the same time?
    "); + } else if (ServletUtil.isParameterEnabled(request, MISSING_REQUEST_URL_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, + "
    Missing request URL. Please try again.
    "); + } else { + return skeleton.replace(ERROR_MESSAGE_PLACEHOLDER, ""); + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResourceLoader.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResourceLoader.java new file mode 100644 index 000000000..d93a3c999 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResourceLoader.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Scanner; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.osgi.framework.BundleContext; + +/** + * Provides access to resource files for servlets. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class ResourceLoader { + private static final String BEGINNING_OF_INPUT = "\\A"; + + private final String basePath; + private final BundleContext bundleContext; + + /** + * Creates a new {@link ResourceLoader}. + * + * @param basePath The base path to use for loading. A trailing {@code "/"} is removed. + * @param bundleContext {@link BundleContext} to load from. + */ + public ResourceLoader(String basePath, BundleContext bundleContext) { + this.basePath = removeTrailingSlashes(basePath); + this.bundleContext = bundleContext; + } + + private String removeTrailingSlashes(String value) { + String ret = value; + while (ret.endsWith("/")) { + ret = ret.substring(0, ret.length() - 1); + } + return ret; + } + + /** + * Opens a resource relative to the base path. + * + * @param filename The filename of the resource to load. + * @return A stream reading from the resource file. + * @throws FileNotFoundException If the requested resource file cannot be found. + * @throws IOException If an error occurs while opening a stream to the resource. + */ + public InputStream openResource(String filename) throws IOException { + URL url = bundleContext.getBundle().getEntry(basePath + "/" + filename); + if (url == null) { + throw new FileNotFoundException("Cannot find '" + filename + "' relative to '" + basePath + "'"); + } + + return url.openStream(); + } + + /** + * Loads the contents of a resource file as UTF-8 encoded {@link String}. + * + * @param filename The filename of the resource to load. + * @return The contents of the file. + * @throws FileNotFoundException If the requested resource file cannot be found. + * @throws IOException If an error occurs while opening a stream to the resource or reading from it. + */ + public String loadResourceAsString(String filename) throws IOException { + try (Scanner scanner = new Scanner(openResource(filename), StandardCharsets.UTF_8.name())) { + return scanner.useDelimiter(BEGINNING_OF_INPUT).next(); + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResultServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResultServlet.java new file mode 100644 index 000000000..5a5db0909 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ResultServlet.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.mielecloud.internal.auth.OAuthException; +import org.openhab.binding.mielecloud.internal.config.OAuthAuthorizationHandler; +import org.openhab.binding.mielecloud.internal.config.exception.NoOngoingAuthorizationException; +import org.openhab.core.thing.ThingUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Servlet processing the response by the Miele service after a login. This servlet is called as a result of a + * completed login to the Miele service and assumes that the OAuth 2 parameters are passed. Depending on the parameters + * and whether the token response can be fetched either the browser is redirected to the success or the failure page. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class ResultServlet extends AbstractRedirectionServlet { + private static final long serialVersionUID = 2157912755568949550L; + + public static final String CODE_PARAMETER_NAME = "code"; + public static final String STATE_PARAMETER_NAME = "state"; + public static final String ERROR_PARAMETER_NAME = "error"; + + private final Logger logger = LoggerFactory.getLogger(ResultServlet.class); + + private final OAuthAuthorizationHandler authorizationHandler; + + /** + * Creates a new {@link ResultServlet}. + * + * @param authorizationHandler Handler implementing the OAuth authorization. + */ + public ResultServlet(OAuthAuthorizationHandler authorizationHandler) { + this.authorizationHandler = authorizationHandler; + } + + @Override + protected String getRedirectionDestination(HttpServletRequest request) { + String error = request.getParameter(ERROR_PARAMETER_NAME); + if (error != null) { + logger.warn("Received error response: {}", error); + return "/mielecloud/failure?" + FailureServlet.OAUTH2_ERROR_PARAMETER_NAME + "=" + error; + } + + String code = request.getParameter(CODE_PARAMETER_NAME); + if (code == null) { + logger.warn("Code is null"); + return "/mielecloud/failure?" + FailureServlet.ILLEGAL_RESPONSE_PARAMETER_NAME + "=true"; + } + String state = request.getParameter(STATE_PARAMETER_NAME); + if (state == null) { + logger.warn("State is null"); + return "/mielecloud/failure?" + FailureServlet.ILLEGAL_RESPONSE_PARAMETER_NAME + "=true"; + } + + try { + ThingUID bridgeId = authorizationHandler.getBridgeUid(); + String email = authorizationHandler.getEmail(); + + StringBuffer requestUrl = request.getRequestURL(); + if (requestUrl == null) { + return "/mielecloud/failure?" + FailureServlet.MISSING_REQUEST_URL_PARAMETER_NAME + "=true"; + } + + try { + authorizationHandler.completeAuthorization(requestUrl.toString() + "?" + request.getQueryString()); + } catch (OAuthException e) { + logger.warn("Failed to complete authorization.", e); + return "/mielecloud/failure?" + FailureServlet.FAILED_TO_COMPLETE_AUTHORIZATION_PARAMETER_NAME + + "=true"; + } + + return "/mielecloud/success?" + SuccessServlet.BRIDGE_UID_PARAMETER_NAME + "=" + bridgeId.getAsString() + + "&" + SuccessServlet.EMAIL_PARAMETER_NAME + "=" + email; + } catch (NoOngoingAuthorizationException e) { + logger.warn("Failed to complete authorization: There is no ongoing authorization or it timed out"); + return "/mielecloud/failure?" + FailureServlet.NO_ONGOING_AUTHORIZATION_PARAMETER_NAME + "=true"; + } + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ServletUtil.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ServletUtil.java new file mode 100644 index 000000000..4441aca3d --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/ServletUtil.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Utility class for common servlet tasks. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public final class ServletUtil { + private ServletUtil() { + throw new UnsupportedOperationException(); + } + + /** + * Gets the value of a request parameter or returns a default if the parameter is not present. + */ + public static String getParameterValueOrDefault(HttpServletRequest request, String parameterName, + String defaultValue) { + String parameterValue = request.getParameter(parameterName); + if (parameterValue == null) { + return defaultValue; + } else { + return parameterValue; + } + } + + /** + * Checks whether a request parameter is enabled. + */ + public static boolean isParameterEnabled(HttpServletRequest request, String parameterName) { + return "true".equalsIgnoreCase(getParameterValueOrDefault(request, parameterName, "false")); + } + + /** + * Checks whether a parameter is present in a request. + */ + public static boolean isParameterPresent(HttpServletRequest request, String parameterName) { + String parameterValue = request.getParameter(parameterName); + return parameterValue != null && !parameterValue.trim().isEmpty(); + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/SuccessServlet.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/SuccessServlet.java new file mode 100644 index 000000000..d240f215e --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/config/servlet/SuccessServlet.java @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2010-2021 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.mielecloud.internal.config.servlet; + +import java.io.IOException; +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.mielecloud.internal.config.ThingsTemplateGenerator; +import org.openhab.binding.mielecloud.internal.util.EmailValidator; +import org.openhab.binding.mielecloud.internal.webservice.language.LanguageProvider; +import org.openhab.core.thing.ThingUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Servlet showing the success page. + * + * @author Björn Lange - Initial Contribution + */ +@NonNullByDefault +public class SuccessServlet extends AbstractShowPageServlet { + private static final long serialVersionUID = 7013060161686096950L; + + public static final String BRIDGE_UID_PARAMETER_NAME = "bridgeUid"; + public static final String EMAIL_PARAMETER_NAME = "email"; + + public static final String BRIDGE_CREATION_FAILED_PARAMETER_NAME = "bridgeCreationFailed"; + public static final String BRIDGE_RECONFIGURATION_FAILED_PARAMETER_NAME = "bridgeReconfigurationFailed"; + + private static final String ERROR_MESSAGE_TEXT_PLACEHOLDER = ""; + private static final String BRIDGE_UID_PLACEHOLDER = ""; + private static final String EMAIL_PLACEHOLDER = ""; + private static final String THINGS_TEMPLATE_CODE_PLACEHOLDER = ""; + + private static final String LOCALE_OPTIONS_PLACEHOLDER = ""; + + private static final String DEFAULT_LANGUAGE = "en"; + private static final Set SUPPORTED_LANGUAGES = Set.of("da", "nl", "en", "fr", "de", "it", "nb", "es"); + + private final Logger logger = LoggerFactory.getLogger(SuccessServlet.class); + + private final LanguageProvider languageProvider; + private final ThingsTemplateGenerator templateGenerator; + + /** + * Creates a new {@link SuccessServlet}. + * + * @param resourceLoader Loader for resources. + * @param languageProvider Provider for the language to use as default selection. + */ + public SuccessServlet(ResourceLoader resourceLoader, LanguageProvider languageProvider) { + super(resourceLoader); + this.languageProvider = languageProvider; + this.templateGenerator = new ThingsTemplateGenerator(); + } + + @Override + protected String handleGetRequest(HttpServletRequest request, HttpServletResponse response) + throws MieleHttpException, IOException { + String bridgeUidString = request.getParameter(BRIDGE_UID_PARAMETER_NAME); + if (bridgeUidString == null || bridgeUidString.isEmpty()) { + logger.warn("Success page is missing bridge UID."); + return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, + "Missing bridge UID."); + } + + String email = request.getParameter(EMAIL_PARAMETER_NAME); + if (email == null || email.isEmpty()) { + logger.warn("Success page is missing e-mail address."); + return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, + "Missing e-mail address."); + } + + ThingUID bridgeUid = null; + try { + bridgeUid = new ThingUID(bridgeUidString); + } catch (IllegalArgumentException e) { + logger.warn("Success page received malformed bridge UID '{}'.", bridgeUidString); + return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, + "Malformed bridge UID."); + } + + if (!EmailValidator.isValid(email)) { + logger.warn("Success page received malformed e-mail address '{}'.", email); + return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, + "Malformed e-mail address."); + } + + String skeleton = getResourceLoader().loadResourceAsString("success.html"); + skeleton = renderErrorMessage(request, skeleton); + skeleton = renderBridgeUid(skeleton, bridgeUid); + skeleton = renderEmail(skeleton, email); + skeleton = renderLocaleSelection(skeleton); + skeleton = renderBridgeConfigurationTemplate(skeleton, bridgeUid, email); + return skeleton; + } + + private String renderErrorMessage(HttpServletRequest request, String skeleton) { + if (ServletUtil.isParameterEnabled(request, BRIDGE_CREATION_FAILED_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, + "
    Could not auto configure the bridge. Failed to approve the bridge from the inbox. Please try the configuration flow again.
    "); + } else if (ServletUtil.isParameterEnabled(request, BRIDGE_RECONFIGURATION_FAILED_PARAMETER_NAME)) { + return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, + "
    Could not auto reconfigure the bridge. Bridge thing or thing handler is not available. Please try the configuration flow again.
    "); + } else { + return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, ""); + } + } + + private String renderBridgeUid(String skeleton, ThingUID bridgeUid) { + return skeleton.replace(BRIDGE_UID_PLACEHOLDER, bridgeUid.getAsString()); + } + + private String renderEmail(String skeleton, String email) { + return skeleton.replace(EMAIL_PLACEHOLDER, email); + } + + private String renderLocaleSelection(String skeleton) { + String preSelectedLanguage = languageProvider.getLanguage().filter(SUPPORTED_LANGUAGES::contains) + .orElse(DEFAULT_LANGUAGE); + + return skeleton.replace(LOCALE_OPTIONS_PLACEHOLDER, + SUPPORTED_LANGUAGES.stream().map(Language::fromCode).filter(Optional::isPresent).map(Optional::get) + .sorted() + .map(language -> createOptionTag(language, preSelectedLanguage.equals(language.getCode()))) + .collect(Collectors.joining("\n"))); + } + + private String createOptionTag(Language language, boolean selected) { + String firstPart = "