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 65619f4bc71..bed0f44786a 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 a3396a3a3dd..b183a255f05 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 00000000000..38d625e3492 --- /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 00000000000..9da358d69b6 --- /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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 H9{PU(^2Q;T 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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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.mielecloud/doc/miele-login.png b/bundles/org.openhab.binding.mielecloud/doc/miele-login.png new file mode 100644 index 0000000000000000000000000000000000000000..f6a14dbd9999dd232c0bdfa201f735a1df2b0b88 GIT binary patch literal 16608 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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.mielecloud/doc/pair-account.png b/bundles/org.openhab.binding.mielecloud/doc/pair-account.png new file mode 100644 index 0000000000000000000000000000000000000000..54932216e1539122ba6609022450b1a6f6f86bf7 GIT binary patch literal 59350 zcmeFXWl&u0(k?u>y9Em{NN^ZjLa@PtySuv$?oM!bm*DOmoDej)yK8U>PauR6BM92UD08L5~tONkS-~#~AX^8Lu0N~u& zt^)v|xb;-gfG8Qdkvllqn_1eJl0!TkOvz2%EzJM`_vN`PO;?h(1n{4_2tDY1L*93j z9{h)U_nhDaY)um>Re3?C6iIq^Ho${{-M&!L(x2yCWfjp{`$S2JUrW{7M&^(l-}5NF8t^!zNf(coYa7yT@I%rg^u^z`vAhX8Or%@1;dv zPjm0~{6k6bZyiJTtwPZC^CXMD+mZE7ruBBNIQsQ%=AQ@8m~}tBiIoj!HO_-ma~A>-M6*!+jaEIsuQ~NR^P;A@_yQi z?tb}FV{Z?M5n1OY#98P#A#+2U;MnE$IiM%mp3o*Kw%%;1ne^LT@JE>8bT$oGi#foj zY*V+Pi|KQLZ1xRIeqhqktRXf44#~+3!F@DnK>2QJ9(g~=d#dqE{y;pr=obt@YtnC{ zluw~?j&ip|nUlC~HMiddS%NfqfbrsR3yo91RACpxbFmpfv&xd7;v{7$&Zb%C5l~5% z^LOYTJ&`Ze$vU6Qij(z3qdDhvq^rI*FK60AImj@z{Is&Ja)UoxZM#02fa}5#>Daa#GK|xy zHYc>h;%W)vJ6uo~OG~bqP4+<&X=YZ|!?SM3*5{slIY&4I#z7nl7Unl=AXbg6?Ip$@ zh?*cW%F1d7*G^Mh*O!j{8ObtSwYpI?jqz`>8JAqD;arHkpW5z)C1>uXbB{(hB#Dd2Z^vHF12jny_r|~ilrnUR& zr!x8TJIc|# zNKk%jgHSo=$$jd3$8h*wy>K(-V?}J{mik>0k%#uevayW>BZIv)4IikHm~*FNag>Ad z?9h3&Hed$Z5>+KQ{`gb?>3CElOvb_5X^+VFyACCGevva^#bFdyPIB2n%egMG;<2^S z!20Dd5Qnip-e6H!dIkTv|LfX!rqgw3gG>p;D2)>K_9Q&C`IH~|dP`zs-21uT9Sd@j zOp&~bec*F#zyf4@1-NO^&?ey0ddn|#RZ-ex2_6yb!C znbM%{@>9h>RuRq;DycYBxpF|heIZ@xrrV?yf3ud4*9DE$LYEx$bRL9I$5^uI?wHRS zs;qW|(2^j@!&)52F1dpU3h7u~Y@4szo4V}wJJqnwrT1eC_Ts4vbxekO$WghUF^$3v zx->A$a8~s@ztVBK(!-O7%pUG&n>Nd%ZcAOH2M8OD$k8MPU;$cNS>>TJzJEeQCglBg z2cyq^QO^I1oynuJBNDdGBuIvSYX-IcT`~t$^fQ9=mn&*^pk%u%?diq())vFnG8M)xBQ|ff_hLYX(ilsem(R#+*7Sv zGAV;i4ZL&cf=Fpf8aEUg>`Z`@!Ykh;dYY0*GG!6`kq{gO^Ga}?m2asxm4@R}-EuAvh`%R-==8RC;M zbpu`fgS`1*I&E=IOt+Vs%beFe0>d|}29O`l+Hi;P-Es>|@3lr>(4AQe5>kl=ut^K(L1BYEOqj*HMSc_*Jap(ADzCK>_ZeDZ! z+)2nlBh5KfW-_WPtE66;2KXC)Tn~}6FWV_#=&PRa<+r6opGEkmG8dr-c&VVz-<`k7 zKw*SUSoD!&L(2*CG`GV;BEtQP?ubZ}ntRXOs(&~*PClaIV$wH`y`Q>O#zMkW@-irm z#stI@G!$b(oiz#CvMWXOP$?CYpdEAuOD<>j{fr6UtA4*YfRtn=zhvhsyw6TehAtRC zh))ebRUtM_6kx1Jr|_F!HeGd?tnbfARUue+&Zpb6|BJo zrhCtyO8Ah98favr1S{4wr2%H1s$Vss#@R-`p>~Hd(yfn?Io_d-!}1Imk@{c@!-WeS z#1=Ie7nqJIUh82p_}yx*H&!QvPeNqYmJQ}phN1MB#&}&mNvIvs{nH+cpr(KtPaG%N zPo`xUCYUKud=w(IS9lse2!IDaCZ3}eIB*cnfkdNbPL;o=o3W`QPP%iOvG0+ zz^H`n=wmUp$Va){vL>PXQ}Hfd3j`V}0jcjHJWhyahdhzRkZ<0D=<*NBAZCo%>4%b`w5xY149!dYQ;{W`5?4mpdA_ zdQR9H?Mn~s%}qG*UQ|lOws}8_kS|^-d|qH;N=ZO*MkV>d0|s{kCK=xmDb0BPvjbkx zIoNpX49T3Ae7-jXu$tZ+@q6%MgK#`3Ep0Uv?v@|h!M3ofvpqH`L%?tpAEL>~jAV3e zz z>jo>sKPyBn@I}FIK+;t&c>tkg8!od>Ta|&i03QgG{nJ(jMhHKm9|Y*Xk`{|BZo>NU z@N561%R;c{*Wi!+T;I)(lX1b9Z+}t=DSj(_cT!gSk`9BUuVRVw7|t_MaHtbb6;$n& ztS}{DBAsm0pjCvx2?x6`ey9bD(X=N2;}*d{LU63S*}6X4y}+VTg`c29xoQ?5!Vm;Y z(4z$=dPd)Rc^`k{^K4QRil-)zp08;rVJ^yvzb5O zbbv@~%H1)(M@0?-(}on4)k*P(6@atph(AfaN$@2a6&>!OEL=p98f<-@fO%14MjV3WX?K5G)#Lx2&;&gsN`L6QNeCHG43)J<)s zXqP2+@joUWA5UOcf~R<&IWr$%rG23g{n?}}JH3t}>JfS#knht zgUoR5dF8HcWO)C`XH?@3V$62HKo)Bo$p;<;FM1BS13>w0J3#6l8(*5c_l_ zV9iv=m$Wb>cOKc9Px)XMrx~8x==wX{2K_y*R9qVk7~gE7M!#G%&jVX-tL8Y1-C5=& zZ`K~IDM+tVOdFZMss}v*G_sU8_EyHPKRV;|P1fj0Rqb3i*tun3|82$?zLdEx$Us{B zgtO&Psd#BBq?VSVFT>Y6qW=IG)>taKTqUESo8{1*`6U6 z5>DdL!1Bx)<+&n4ru7dLubr&YwzL! z&m~{63~&*?E^p=3S(GI4ySGZPl>W2>AZ!Ywz)olE%+!8WwFW+F&kxyljKIy{S#aQz zC`$($8ns*f=-pGE{90fdLLn&|&cYiXOQFGf=dX(O#6lx>T@dXwcal!D36f~jP z${!2due_v3Q^(nZ0m_T{R`)R$``t*DE*i2g(9&Pgf#ZsnAd*15Rz|z{BSL!LS-Qk? z@li_r@EC|fQH1jcuLMD)A|>Ng)Y9gP2qCucsTl_-<5D{eyU1u7LZvw@?d&pTXalcO zXe9)E|5Juck>JERT@Bkbx|pD;#P%>nX&-2;Ry*x7tWs3P*^&f#_nE08q|)F@GgboG zbxo{ZnQFs;4!R2=P(=*=DkVKF(x$-X0~}O9sFB0e%2GTQmh7bLu!L3%^FrqzI?3}FGE&ES9pD5JQ&5WINp#kfgjn-NP= ziM)u=XZZ7SGvn>M^5mH6ZM#BkZ1=6J5tJ;WLz!c~373WhfhM+s)}#J15S##QbdTwJeQyz2(%e)@fXxHzv#nu_ae)ZwFkSqiSH~1}BR->j zLr2QE480kYZ8W1c>^fl1b_3UvUza12mV7FBgkiagitt0Ira#3UqzGH{vy6Sy6lU;-1Cc)|(GJsEt&&OG$FXsq_?vluNTn2{t6rY02Y#8_EqbMKUrI6U4klxO zx-iE!RZ%azSA?s8Ph>U>2XnVd#>Kya%@D4es+>L)42k9GSxX>^T=L{8R>sD-qb)6X zn;U$QWYf2ULiiO|vVPe3t#9`m3y{h}5MgW(4}Wa`{uZ?llhqS#8iSS7n0i^XhVR}5 z=&x1|{w*$}d`-tg*IWA4l;=1j6{QyyaZz{}vP#V@Tx(WhgyS6J2oP8WH9=M5@B=g? z)6#2T$5m)Dub2BIm;H=A+zxM@$XITAdSfscI08x6GkfSov+GNVQK2bJTnn&!}e}Cb}2WXo8jMG_3(ww zZrME>i62__$BWbN@We_MfSx`=ux~KYGml4c;E{^JG{G+g!;<$E==*#s^Cb=jPUr@K zY+$q4b>^tb&wq7$O|c++A!Z|8ndHLqDLPy-mil#6{Z3C-TWy_zfl0G-6t|2vls8_b z?9$>)k^1%{f1ipp%C@fUeL&J6rDA>=J@g8G#FlZ>B${R6ny8A5{j5hRYyytisqiYQ zJQV4)(D8u3a7!fYpB|rT*QZU+i0^07;l=SGJf6&@3=UAYfsw4nVY9(9IOgOtm&l7P zDYNq2Gv_Fa)_tVI0%xRwEYiNx<*O4r?8~ve17^q+;k4Ad1_i8Iwiba)aiCTiAUC1? z85|KWoA3d<2_4&?P05Xdz(kdw_Qh4_+WFDUJIoJ%z$(wp)tZa}i_^8*OjR|k!5YdxdMY{yPq%>K= z_MIXrh8j_u+ z$Y+E#E%R`YhFn>YB&1X?L#dAgcUeY4TR zQ`~rl>z|KhMKQt6Waq7f`^TP#tcWyq!7POFM=GW+PYh~1 zcA_vR=ZrVyy4p0f$|(qBp(dJ~^s{I-Dydjl9E79Fvxjl#04dV?rnJI-6W0;q(!TPp zlt6FVDhb;gF^sRXE1h^`!_Y71$EtuCnBNcbuq12pwfT{yWhx7(SOR*tZPH^gP2rrR zI9C=nwY=A5$IHA>bsj;`#_=lvHBRMb;cNbVn}zl@&w(e#nklelAGqKtn>bwaxK>Xg z6wcpfYPJ8sp@*xj`9T~rR()03d)@9+EOn&VG`Cx@EJA%?S|;TV*v6qz-%+yKLJ4E} zl#=F$GQ=uid5}v1=}=n(sP<({CBMDNF}X6U1=>KDf;NqFnYGaappoak+(kVL)QJCC zDsbc)dkcTLEIlPhT6c4w28ESe9^>-E`8X{tIBI-1sW}o9ZH!p*zRg{)t0bm0UhG4B zlQq)&=W>|Z!+GG)a;0lGt;;)N^(g8|@R?pD(9|PX$MBJ_L47WbY&H>YaJo0lBes8F zti7qtSK~7`%A$y4FOX&-!ei8124U!9M@$YYKewtJ7v5OA(7RfqrY415lx3v*n{^vR zA{on*)t}^Kh5CT-pAKidNCT3JKSR4Rgx)7;kuP?smvr@rJA|k0TxnYhgf2h{L^vqz z2wOyDBc+z|U|)>ZBD569Em>$w6~W~=Y)UO>80;&>d2><6(H}2T@gB5gJk^LN)$4?j z@-@P$3kkIFF#V#w`Xn~;u@lTR&)E@kBbB9CBvqzC0i)6~&4x}oAVkn(Hz^k^I$clS zqVhg#xQvojO@{^ul``8Y*A;$e9?aENI`!O?R}TB0o3Ghpoun%I#VQC?F%brz3eTlQ)y2=gJ;krW z=x9D9Etd|hxzgn5!l%nMeER{~i zzr;SkLgB&6jxWID1#u`4Fn)orK!1EMugGpL^<%C`mNLSl)^xuy8?QoatfH-W4pS@~ zsT`ys<(Ow8I?0t5r&4>7Lqv@efo|R+?N*a|riAh3w1AX;L`yxdDui5Pl6hWVPns<% z;yKJ~4d-d^xamplrKffC_SL$RL8FIc+M9ECA<=E4yHIf~S7UxJ&d*5GbNu%gQ%lu- zc}8i<^bJil2_no~F6x9Jm3f--Fz%x~q=+ckjpL_mlwv-Ak#VKx4{)TH(NjguvrPcc z_yas04Fk+%(J(^=!z74p0IYlf%KjnUyO*y@6*`U=e|}5U{K+IPOE^>+w%f$IDU4Ge zhFnz$NMK`MShK)eQwWvF>3qYe{b-8lPRqAMN?FV%LS5jBT`7__Gg@%J ziphlYHAd!cXtttsQ{d5`eum4*6v%ZHOhYR%ha|BmRu?&?n*B8}Sh2o^d_(xtwk?i= zpO1p-w#zI-pI&V8Lv4&}LtZiIgvZVhglcr; zJr@iCTBp6fmFFIT@W&3t*YD)}8_avx|F@%jt6orAY$3TAN?nilbRsnEc0l8r?|vPO z6ymasQllsu`C6EJjb|&^!YJ?7ljm^N4!eD~UvSwJo)7=-2g;bAU*rMjwRVeStxp6e zy)@pZQ+PUlz9pD=^04bOGA2^A6(3>pas=s?pnI}+Di()umD(_ug_MqMHQUvUl19C? zc_vu+E>#g@fyz-RRp>OWu)T$V&#T4EF-5elk7Frq&6K(^d|(fRYK z%q!97;Nz$D`OY=<_J`pQx1-O2pM0-|A)mZQey_Xtyr1~{@x@)iEkbex*2wNe6~&J^ zMB!S%m~M_EJ0y4?qiIO-Ve6w6w`Nj64jnJwRN2~t`|KfD2#5T+YSQD%e~i*YcA7$2 zFpYZj!y)-H^7x=?Bm^R@B7s;9&9LTNCNxSd@TjWlWy1#{xCyS&mzeBwuQSe0{^Fw| zeMk=0w7fag5l{dcB5wg<0!`wZDef&}L)5r1FFK@y`&c+*sbU%zrm&z)%!CV1ikN{4 z$jwgUXO82TD^~J?Zime)hn0s*d?#3?Gi0Gg|tPaG!~9*wKn zYH*w|MApyZ7Yyu0*Ww}T7Zr8q_2(A*=q0@|9A{y@Ir;88G1rDkWYV76@sASH?O{6r z@19W^^iLe8PEiJ5C`s*NyFEovj^O-k;|Ku9COr5`R>eWThdbl_glMpG_m*rUmL$Zx zNt0b>E0RUwDi8C$>Th1ug_joaA1&fW-^eZife%ZDF^Dg}9hw43A$?qJxcXjD|rNU8wYwUu|wd%v>?k?D_*K2KpYg$*A6dK^2VQ z+ZzeF<2XD*W^^Uf&k+&lbjJylJcD^RjpVh~4^1c+Ll6oLKg&QTbF3Bm7`9Y6p#U!c zTr+E1S;{d0jzJi+89!pwFFfI%RujhTl`ODt)nzUw>R&Hn-uS&g;uEos*T-1`oy>@= zG0pazRIlgms}p$NE1TQxEW2(=w!k*Qi33iGxX6P?r(pJA?!%fp(4i@iTBLbtY%{Pz zrc^3+U|uGgI7BchlD=v}&jFpsw=^ow5cB40iO24F0fL==fi55SJ|`$s#IN5my9Dq~ zK2LUdvV1Ky&-%_E>t%;DwsWpSB9QmEHa@QI_Gfz^Q;}2(lMrk%=Z0ErH&!cgE~7aZzwPh@f*L{&+h{lIkk^_ z^4IqSo5c+okAhL6LGfGPJZS&`s1Qq0Q3WYc(SQ5_1ORZ)@`)Fa>=z;#)K|6^qepjD za2%Q9Ya*xLE>)!!2~l8ZJT`DT3Y#jTkaBdy)j>h`WmF^8$#QXVz=Q3@z~tusAVxs< z%TFp_tP)}P#og_~@g5!X*z~KWuCf2u6*Inc%!W2R&_e_ubSnB?0b4_qrqt*Borm_Y z*cvy-oVmx<+(P+rZPd#z1MFk0w%ab-WwI-qo$@4R)SA?vKE$L$gtsNC8Q%9eGPg5c zj@711O=5+888jkhupRsSRs5qAi6UqARY>J9{Q=X2R@-RC{VogY-t8WB^$dwHHi||t zEEaP4q7Wh1cH?o8hUAQIL6G0#t@N(aNE4KJjE72|u4fmko6SVom0D0X zUjeW_d6?<+?Hkin$MNm_>hKXvTz<6@_qSdOR;{AbCiIJQ%ZsxO_VHI=K-{fU1Jq#A zG5~<~VyU75(U6tlGq$&7HZ-v}GG%tRbpX7{M?rT7Lt|@G2)U7|xuu-|@S>v!NN#B& z095CcWtDXhHMOvm^mH;+_LNgG_Ov$UH315KLjLH^=Wgp@YYH(Wcek~%bLMjw0RDx` zXXniKcAEuA{+9~GS^%gat3WPl?_^5O!OX$T$|UA)>B}2Txv9z}%f5S91vUh<90D;KlPl z7S0ecSIGa{!db<`!IVYG)Y;y}$=FoP)zl6``L7Tr#{aZ;aB;HvD;*PK7E>EjTT?rT zGYg0r^lvUDq+}KTY4JvZxuvbcUsmq64*#YJu{8T1vi>cbx0S!r`PV?)Z5{rJ`)}I+ z$o((nzc}Ip+Z(&Qg(n3T0KWO>GqE?eG~xT}tr@Q=J0~YQ7ZV4&A&7~C$ApK;kjvPN z$=C$M%F1J6!p#ff_!lTCJ7bl$E0YnoDTs-~m=nar zYi4M~#9_h(;skLR8gd%5{|kh?lcl+(ouSRYM)d||Vrjz1&0_*GHR5GsG65NLFmZr* z*_jNvxDA<%%y`*N&3Hi|ZXWKxpiGSU#O6}299R(Xhh?%{UtpHHQ($2;Ge->0MZB3OShHq?wxY$6vY@BQ$5DzPe6~y~LN@}J~ z&X)FeZZ6&WK8WKhHrf;00jMI;cn}|C+cKs2(fokvA4Go0KSDp{$}}C zdy{|sTTvt}ogwy49&a80_nucab^P1a-?o5_zz5sxu9h?R|ror{%&iG!1ilgY@0)sTtHjLX;vWM*h+%4_=X zLI3}dA{QSg=-*TP$nrM0{!!GAZyR1#_Fv2XH-&$hbZ-OnFC!(e_$TC#EdQLyf9LFf zS^nN`|GV;EC;yTBm#+Uv{!7$!BKa&5_^&iQ9>H3f4|3w$_|2)%~+L;Id-GG9h zcHOTE7M)iAz_>JGNz-%#ZJX~dRhoItzA`Cu zqc$72(hKH-GT9YXSHr%IWhU0zM^;UA&3p7|+R_0jO=wRPnSrM5LinM=dC>P)JL_H( zkFSpBysJOD2$&rn^Sg6m5vWst@LpVZ6S((GJg4TBmm>q;q`(kU6At_)0|vr>ol)`s z?ehQQ_TRPsamGdZ`b(?!>sOlDxw-u2W_(#$Sqm#GXn=~kI>JY8etu#AtL-w!z~CSS zKmW=DHx?F_V$pA_ZI4P=z7y}00R%$TWMUjM5nI& zPR`Ep>FM9y;IOc;(0;&E1SAWYXU|!!9APn9Sz9ZrsfE_pv+-n4VDZaaSAxXXYsZ`P)cB?I|t`=8RRGi9}rDPHmBx_l7C5C~=!omV+U}jhM z7Bn~GYq!{uCJJFO8Zhd%Cpm+{!^6XS0}mlc_~>CUVq#*nbaaICj%;jfzXOGom2omM zGsC<)7Z)`cHS1wf2$**}Lc_zu-!X;{IluWDYIiw-=L`cmTS_ta-rgbcPKP?NYE?wdp=dR;kLE4(bCest5rAR`7AB1 zsG}2AC=rYPuo{9wV6;?k>P>)!g@qLa2hxZ#t##lb(dhEzJU%{t^3hXM!&j@);w3Y_qjXKVvLiMGrzKuar4*d>1lYdqtBs1ZEisP!_w?zYC=Mw z$Mt@+mQM%+J3CHYU7e(??B0|l(5RN22wWTfFtM@~9FdfS%c$LKBNIwuYHn`4(rmlf z?ZY1t89BYQM0n%j*&nrYkLq22_FHS!bTVgzv3;<|@>UZ;$7EO+NsE1z5=VTfq^<(y2z{8Js(2 z8{uPPGB&HNSRNi8s084`Jo`yWDJdip^kGZIyW?Z4Rj-S#(yFRG6e%#6BcZpTwwBeT zv{tK1qs|Bp85wy<7j9s1&~UC?_2u07&DZv}QMYz@XuiEYT;45WmYkrrTnSEvdW~5t zIS@!xX7E<4(b3Vke50MAXi7FI_JkBB?NB@hwcy`R^HthQY2-wqS65fU0-tjF;k-a! z!o#7_yslhrZJCQL=2utSsf@~i!4N(|*H|$>w{x{h4aenR zWL;rl;SpYfcpcl&LmRfKN!eP}vZ2p9IwbWC4Rtn4Y&I*+OuNt+J7r^FFu260mX?_r zowvs5*Y;q^*;=P+g&G9|1H<9M=g*&cs3I&dpH8YS(av@pLrPm)dEOXStKhUqZ~Kgk zg{24^7}CFm=t027zovu)OT#F??#W(m4vhVZg=&T6n+ zwL-i(VC_=B@#V=^MyCNyMCLS{lSZvdE9CZM0l~Yfz1>q~NZl>@0xT~Jr!$8aml+G^ z6T{0ukYdAcRNaoGbUcg25P57NdQic?RbKt3rDE>&%{h3{9W2}NZn_NxQ^2b?Jf-A$ zeC@D$70qp}<8ro(`1iQLnNiPG6gc~aq!R45XGghsqy8bKQYY=}_{R$0UJg#q5Yl%y zInlpbv2<;0E;rZ_&`CCjJiy?s*R|l~=lkhreZ2Bt_I%PoZ!9foylwUPHBT3(9mU3K zX8)hQ&{cw+gi^7;kpijBz!Rn|EAh$6AsHDN{x!)-Nl}uu3Z%9U=bl6{2_=DC>(4(g ze!88~2J7I{T)yJzR-(0J^;Yjx)E9(GqA4;K~Rsg)w)vuTlO1mA5`ei>0HtG+szh76k; zhD0b(Y$nK6*N- zLgcj1Z*TY1nJ`4=+_jvq)cnmL0>-j(^&;LVKorLI`a?Dmw)pIg7&y(oxgv^r8dW~& z{Ck2S)MEl_vV`aWt1~yUpy)+c&5K;<=BVynMaI1c9N19hIH$KDHX$J)FLuLq#g`NO-b;f7!t21x+V|4T~5~h2Jb55(S zKcS8wb+e-2B7-lI8=Gw~#%V#NcnM0`{7QlvMwyP^w=GqvZQ>^to2?acvnKt zA2XHpPysZn7csTaic?-liu2ynOfvd$3qGscuw?I5O!9RJ#cYZMoUehhK2==#j}$3mnU>qVJV&Z!KOnF=a#;RaDWZznw)B2LfoziI1GCgXw7C z6=pAos68g#>hEaf-oF5_3%m=0H=XgJ3TX2sj{K1cmCy7C2;M(IC9t%;>&cCeoJg#S z!`xFw>7!lf#IMJ7kKd)eoejm_7 z)X&B5Gi!wGUTKZC3VcrbUqX?4t!8LM519Smt$4`{F2Sq2Q1kuB4O?Eqy7B7RSdCWB zObuI$QzNy7;o3K@ZpWH#LoxWAZ50eFzb4#zvMwg?0@tqhq73dEEF1qJnj-o+J@9Zz z-P#440Ar>x{cW-=bfL7h^*-~RArZD#s;@zY#@pUL3Or$e3=BWgPAl)>;Ze?3TI|Vw ziIKxA>FP!^X*UP!D0vn69$-x`Nnyge&)Vrc{*YiD{Nyz41GhH?*gwi>z*&iZ;T0jA%QMwyz4Z6@g|Q|LJ0sg6trBIA?(2el1eN z5?f`-S*<%3VGjD@g4x8^_Z2ZDq9$12rszP>esdsWMVv}!_uv4b>#n}Oo`Re8ZEW4L z&wui~^B&k}__Vug?%YiJOM0QO;c@=kBXU+p4*KJ5 ztF}7%IfGjmD&2IOdVjM!HHn`ua?HVdw57OalRY5K#Xi&z=j~y)V4Jafb?&d0?1mX! zI3N%1{$_VPK35xLB32c1emT2tj6`3}d3#=+IXsFm!;o?u3g%u!ls7$PH_Q)Yy6BVB z_t1Y3KdOfpvD0e2?TLQs=ZRkAIbsbxy%^IUDPoY+X9cKy`!ok0lG5tx^ZQ3aLc&#- z%y8gmJQc)pKubT;(q3p~E4i4M2c=NjaGdEPO>R&{?owd3x;{!cJ^4u&!m?jd%1LG3mJblV}B#*!fn1LElL_fPyMs{SUP7CIPE!Riv7?J)$)s$QYH0366U^J%eK0 zK6nMR!iuYxt!tRYw7Z{Lu`dW-keJ9w0;R%{5QSm%>~odw&=m5!_y4q7C9R#^%y+HA ze78ZbIad|Bbtf0s%)4oAe8QU*))&2K^OQcGsWItvHCL%cEV;0;p?f*-%oR5GFi%`P-9V7H_evbdrIX!}BMvza^u36v zh-r=!_z*Z3x|`ecZa&+Gf>~JGht3U3tJ_a>s&?islQTT0D{n(Dsru?blxrVBnOo^t zg{t}iuE3dKbGGmY%xEE8jchFwx{q6D!|%tu*x`*X^g5Ia^a}*rWf`hJr-OMl@!j`B z2I`Z`b;iT3t1rAN_G04V;@^H&zT9fz5)ep0K;cZ<&EQ?0!nz_ZtZZA@!e0C4CGzyb z&VEbW(sJm!+%{}q2`n-9Re=#52RtYBjaWY$4U`HFq=?FU_?wtN>TrYw7JYLo&EK%e zFqURurcJI;FrkJCU`xx9rC5d8G#h*DL7-&^pzx(7{5>-{Lx;}8?CK8+MCjEuxo{es z0>@TgRYkFIBkB z>~Cnj+CS)@+GytN@fbI(F`vuZMOqdaZ@{rT~cUF2nw!UP#qi8Bzo+h z){2H)SSLBiU*;5^kFJ@IdtD%TiJFsz&w6Bg<$I=YmJVKB_=&zD3AtESM_aw0UFmwsG4 z7>O0f&Tc0rSYfaVgpn!{Zg}0Bn8kO``k_BrU;KyH1?A`ZP*lvPgL3&aOjt#R8d{MM zXw9<#?IUpP&w%&EqynE2K3Wmr&Kc{}%;Qi3eAN4|=DQG)hBwOl!fIY%B!2usPn{uD zTYH|1-xR1O5AK9F7U`fBiIe%XFol-7eVNij%KSn^ZXGaqK@_+!Lx{r&Y}+jWl@$E4 z2v@)g+eM=e?{J~UP8B5L(@{Aa{DRN}4|a&sE{(1Y#by-Yya`)&xI-i8CAzN8_}p5~ zAmUqdJ=q&3L|yG}Gm_b!{&jp{AS?%MJqS>M zNbWwfm-w!{kjX-`z`5qV65-gAt0bK=^m{i9!{qW`7POK4CT3qCJM5*aC*@%ipT{!Uw~-LjFavyK17`z@{A7v+9oZZ+O&I z+&(SmrpSGJt@+lD>f6gW(_VnUf{rsVQ7ypL#EN%>+N=9mUw^I7o(UQJM=*#D<=OrB~QyqY_4aut22fjV| zO{GRff&vS+37&Q6p}ZkvIAZ6lgaqumWJP$Q*P@UJqqMKOsDSYAe~18^v&Ia8VUdU! zk;r&?@EzyArMel#Np$^IJSST~!=tCX{tUh!xfpyTS{~ z{=Dd(#-B98kzMY6TU`oCjZLRKsMeBue{7@IRkToh9d<$X8NGyjP|fY5e^Jd&RR*LK z=+&DbsnzI_Ubc3N%(^4vzx@Ei%se!j%piJagpGxERny|gvPLx^gjYZzrb!-A;5bZD zzI;KixSZOlParl@k0Nzo4!ls0-I{D+38j+t&kMgS)vcwL9}G12LlaibKNcYELO>$a zyWxK*_#R9<9ZXAyUp`!jMu;t&mT?UyY1V)hctfF7f86Kid&|+DDJU#4Gd%LUZ*Do; zQ*7Q71&p=!VP(qcQoz_A<&V}hO-2VEg2`M+(M=@l*^ldAM-|KxKgS|5z2xW_e>q}O z+`j7GwobWh<1QZ`JN_Q`dDyJ+amL(%zA%km1P<9tMA%SHXYWv5-)I4z)xPDJ1e(wdlsST;V{>} z;Etu^*Yt|Lu$oRS;+OSx{6Txot-AL>JXdRel^tFsQD}HnJVzu+-(5ILl|ylMouaQ_ zL$l05?mGrQQRENv{NLmgevw@?gGyM!!lf&M| z4usEEsNkZT*zBSgJsX6=gfNRy-3`67E-o$>=1xE{r#n79JutfZ)}9D$|kw; zWoz{K3aC|T^h&LQaiKm%CYICZSqb1;892RrkX#qSn*K&tRmw<*TahXW?*YzGVC?_ML{2E=%ed>5jL?$-JpU|1(O#1W-6qF!Kh&867sE8x`&(8?(kKFp0H3qmCZBR*qxDT zY)RIBD)2L0Q60XkH2>lyJRptBcKI+kBHAzXNSOfEUNVfBdEO32X5TQGlbxMC?-x$F zod^==DYfTaV0c&UbVCh#D?1A}8491&BB@DV3!U*$-Upofg*dLsEVPAQ?ic-3a^VlN z4oq#2lq5YgwqU<^I&*kXThuDCVnt}w8V}5Cd?a&ob5IvY`G|AcSm))IaBy(KKkAH! zW5PMsudlB9&UNPqOy8C2z4y6M92gv|I|vL1PQk)G$vxn8>MM4NCfyAAZ1_DNVB~Ir z5)j4iHb?beQOSCgLkcgtLm0Z91(`M6(M;Shz=#eu4?IraTf_=#wBp#s0-dQjQxHx>U5fHx%{4Q1&+iKVRorVHg0uP z42I@bkrI0i)!)2k=!S#xFYs)%X5BYGTrfC7BZ@$vZ`31oKdALSzT$nD-E+QNpD;@D zv*yjp8R9K^*{UQy9+ zw+lynXOzS1HATF1OU&p*084s>!>HjN$dSXO8E;FQ-f#xh3tYTpX!AG?g^iwXD}1_X zM^klmbp?ka*=}_EEcO<(xAR%phs;@_dC^&Qti>cH;c{?uKMTAs(~I=D-bbSgPfkfG zC@rPU(`oPP6EQJ2m+&_I2n64il8dL4_sy=5bB&tl$ZSNOyc|m7zOmi%NbvCc%3efO;Ig%Y zB0~m~I*~ifBC&=Vggk!H9UEJ|Ei`H461M%WqZ;xe_HXa#h!KW$+T9M-&v}m ztW0nv3)HC>T6qK^Hy*jmyv*;TjGArm83HSc=PROHg#B&s|A(Bv?20RB z)_`GLgEP3hy99Sh@SwpRg1fs1Cj<{R5FohQzyQJBg3I6zgS$M7`<(m3`>u7KKk$62 z?%Lg}*WOjVtL3_CGjB!I{q{WQz#Pw|gqZ~md>kkx)l)#STDWJ9V36q5IF=)bwbJa^ z`!^CjJSb39m4^|?8#jMIxOrbpTo1JE{IHJHL}K}+#Uv?;PD(@UP{P2>}1=p?BIJbyl@QR+jHT)U^@R zG2=Rte3MrdAHy|ue83kiD92iPxwJNZOjyK#_iF-hw7R>xCYW5r+9Wdd<}I8_ahYRT zof;*C>05$t5{R@856`8yI#;ptzQKk06dQpXm|CD+wr;x97slQ-@UUQnp{7QNpb=sD zBi@*zZ26TojB2w}0vzFJ(h5hf;FY{Mc~yr2h8!(0b@h zACvnLmB%lj+U$hEJYCa>23tZbSo&X(Y8o45QlY>gU{sbornG zZNs)w;H70DE`^*@6;0+=iW<0D+SBq#QqoTs@+X~8DU>jLp5h3Z5Y)Ugjt-wS6(6q& zGJKORg!&U$uKQYGG*iY_w-Djp2D(HbcI+TV51kbD6DIO(MmNCK#|D*b!kTVCRql9@ za^rw?S6{{;CE*XAUrjs#haYyTX>Uf$3lf9-2QAq%r>^qy_3z82E}Q&u$u9~0`f5r) zKe4t@*}5uEMfn}3u3|Yp(c%v&#;D53%ymr#6v!LEe#W9=>7f1;LIgleszAr%9Fkq% zPLkFEJsYRgnUln&vYax?(`nsDp~Kid-MqX{2Fy5N^2n8LEdFo4l^YMkq_1A^Kuf;*@Y9+SL0RpxbsOCEgTc@ zm^TeaHx0y4ify*dnZPj9rk9#xupf03bkhkWwN`4JXChRF4Q51;dPPg~0C* zpge*J#W@R3yre=?8ow1Y=GwgdryOvw z6f+@JI8;_ug|U=cAlOX%yS<&Hsj0~VEMycE6g2hy2pN;iw3Z5pQ+Ow=I8AWvL*}#F zY}NXZkkOf7RfTidvg(kJ)Y|+&QGm8=`AzdD;#06e#F6f61hGlG*U%Mf{MHq|Ic-%1J1)w1uhTlspRz+<`wmg@4CYB)Nn2&QZ*6dJ0>vxBO~g_LCx z4I^#*v5qSpvimh&eW(IL6b@<_O44$uyLA9Ts>Iz_$FuNBqzKrBg4?rcYBXHgVFV2v z|L~jwlA4q^ZY`#AI4ML9@HWA}E~Dz3qalksWAeE|b-%K6vL{nnnMmhWqNHA6-{O79 zAWi40ZQ1uBbHe~V2=x28onoQR-y>2sX%!O6s<|v%7kPiD9-qun>|qS4?=gR!Dy2iV zF1q1(k06gCuNU1(R9R!{R{fx1gL5+l{hT?GXL$`%p0DS_py=}>Z=JtvvcAz@QF~c3 zrfei^ePqVe&$7|OV2IZ;T{a(LlvJikb{{E70;|=qVWfCqlP}t!FO=yN!Z9mY9Z|od zG0g`Y<6sUj{N99^A3y6n#(rU5;>FGv1=KHxE3O_A%ED4yZ~U4xf&0YsEp=Brf})<` zQwR$!bU2}Pe+o9Ba}Q%R(3X-R_zbr!nL4(Ul;|<&V4(mm^G6Om?^fXu&wv}#zcR$Q zX-oikljb$gbDPBwVI@h*NgPQH!P^A0Nl>o!iSs&Q@yfg`?6eTa!AG-}>hKFP^@>zEg9hF)Df<4Ql=;*LyHq1Ru{?~KLM(*^FNWb{N@h59Cj=yT`JdrMb{_tq17>~Yl zVr=VAEjqO>Y&_17l*6-}veyv^OtF=L!O2+t_b$C;q;ZS?=$OuB&3(O){)+`D)~H6) zH&I#rIjrBNT{Xz87jo4Zb?-jOUrqh(F_LSr?}HDk?57X}n(P+NPkfL5p0?Od$ zi&hLk`(n*1d7TT+cX<_Mr}GyUGI}++9lza@r~*>3++-f*D^_F!xzX;VLb0gP?j-VQ z3MxTu@xMP@R%0k12%P0P^gb%(g-yfr&Rf^>4dxw@`mV&r3w z)_kP#f#)N+5Ltt5*BIciZOBs<3S$01Wu+va+~&=k{q%W$aJRW?1e~v4CxF%?bX}d# zWkaJg2L&mIl0!0#F6-%qD~m0ej#rj>1Vo~3d3J;Ts73JgBADtQzt6q~LKNm~_X0Km zL*s{OV$y7sa>B(Z;VoI**9L1ua$Qk%8UM=n2>{b5GWQZ07rJK{(Z1djUnQWDgUW36 zxqD*KsOA;b6&*@3Up?_+WZ`kPA+T}0s1b9pgdQcJs(XOv=LH102|Bb?lik)$GFVI-6joGLM)YnHH3ZU18+p^E0~vq~ zB3vjz?r>rJxScrz%xFLa*5&NC=rzbafz$F8%|=SNQvtGeB+FqBq^A*;b znWDV%8kHChe+j;&s9~}X%lRjEh-T9bR}Zig^YM#{OeJBUMMvUWpzQ}4ybfxn15tFj zP$v1!eEXo-sFqj61|q_#R5}!zojeMk_V$jP!-rp**i@i37ZDJ(G|8;WWt5mNsB1WD zn_f$n$ME0W9HKx()jajj-Mo+iUYK920hB)|ea(gv9;z?*!`Ig`zLZcg$jXRY(sn!$ z{qNlhso&7jqs#evY*vE|rhon38h)dba2{&}y@>ZvkCJEOW?vE%Mej`4vpET*T{{4p znVBg_vazx<+a5^35t%Ge$$=xed<5g9ERwk}bwQVP5v54ydhc#zk!n1)?bx+m))TjiKhmHm4)HKSq zNWl=d1BI!5wu9ZMI!G?b-rN&10MFcI>*WpjvdG<+F9L|&x*)coz2|Fgdf`R*Bzg5p z($QigXl{DQOQskChh<(&_viT6v@rh8lF>+gYVZ@l84 z%&+ee|B%W#2E3neJl}xjgjUo z6&oAHa<>GLMx=^eyh?B`I!#5&j36p2hm*Uo-A}RVQdyKvS3fYoM2xLWlR1wG-*l_G#+4geg@<}oPBBCn5u@QV2V{+Lx)f6a_%fk zHsVl>8DR88zSvw3mW(JKPg95A??LHo!=1*%!*S%HWmw)P0sg?Y$U;HIt@gk09^enC zn&dAw$;A@Mdc96RZ1X(*AZEdJgpr^R_{KS{K$0S%Cm4#pg6 zjLo7IW6f*6g^QD#iIUn6Yam|ar ztK(xKXvlgmaO|2s8!HHcM4XAL) zsZ})B_SmjZ?#j8FUa1RqX$?LJgiN7^wJK($Ziz*vGQJ2pf>n%(5}J=CGN1u&<8@$o zKx@l_MxmOV1iZlQEgW8IoDy3{J?-6P40S|6*pZ17tszQS9r|6Ql2g=)Ya!@~4*x6q zSFtlpk83`&+ZNr;S@$lKqzY1sB=q`0MDLbc!9N$dZyUMeNj+?R{?Y0QMoIkzQPNh{ z)>{YDMe=2ft*#c5N&P-p0QugV0cThk;h?+ON%sTJGTrH#OS&k)txdnjPG&3Cc}Mz02|6VDe?L?S^d>6LpBwjRJHqh+nh4`;*Vw_+Whd_oopf>S%e-t~S!O zG>{Au8ca;v(Ll`Ef@{ffO^bOk$%0ZkThGy4MYzB%azlf6ty%m#l!Vy*&vhrSG$PuI zw^>;pfCgBL3TN4br|axbuy5=X%?2`4`Giy;8P5Z&?y}x=ReP<@ue@igB?bog2iGHU zZ^>qAlV;mt6+ExcwKm(bTB>1b5p&6Xol<`gFn-&Vi6LN=)W#n==;>3-O)_}B;X(Km zW;>IZz{TKde?^Y}tLs3YE!O}(RS3!1+2?~Z{wx0X3r*Ki2@`pT&uCgwk<2U|3?OCFNFWothg{QmHt zm07J9&iy{8$P#_S;gphTfW?^^m+7sVp4X{6Q=XiHGBjB!gJ!`IT#Tin4d)h4Ie$%f zgv{i-9eK;KF0^_O^8CL~`kgUm|KdJXWSoGUdJzSU(KWR`tlz)(mOZY`-q&98Ydzc2 z?uj;%PU$3;$_NbXJw7Hr$nB53sWjVT9MFNw8Y8{A2z ztgXAlmJf!P9q4_`)<3^=^p;FFiorGSq;@{}!19h-`*Kw z-K_Q(wA@}3)S`QP?ynUrBs~v`Z6KeJuxDj~4H05p;Echz54UNeez4(;S`#syXr_HV z^OV{*xSvn>>E_hUAEim5Iq-pQwSfX3Twh7wyex2PM3z$2`)+g9x8l)zp?1o#(oLV3WtM%L4DZKnIJ|4!(Pd=2eh=Y5dDja58 zyws{MJhxCEl6~U`IYYNW^Pw{ekEWJwXQf7@KXiHp<+1Z3RG(s$az4N=8~M)JC^Wf5 zame-eQH>a2x_6APY*YUg7DAlg!FSBm$*;%qP#$)WU;W94l8h}zjl;7VlaL*eE%A!U zoytJ-c9FmZ`+jB>18gakhLEX6RJNw%c_V`J?$jEC?2fpFZP@XS=QEXSr%TAt-Q`}%2ECeUDUmFw!%H$wUC zaMykA%`&$g?^nIMP$eS=*WfjNbaVpNi3w;5n4ByFw{;ACR%kr<_$0CA`qXmEPa_Lk zn_M+x;iT}Xvn-|_A;Le|+5!1wzJvtSu(2#y?sX6iOoFAQCHUxAtfUA`fJ89!!55$f z35U}oW&s@e%5$$A1Q5r#>2xC)hRsQ7oFGtoidQtvULi>dRDl{uD35bkNi^B zrpGRPd1)HzZWz9JEd4o)r+BTwf^Z$fdriut^RYB}G9@Po!!KM)b0A(Sl58g4%Q>>T zpn!@>59S?UIndZ*I#;f30ml1?ll$87WMThyQ;k&2!b8Vho5e8dq*&7Z!l?B%^lRhh z9CS6Bxq-94n3!;D&N%>myb0M9!1zt30AZSlzG#R?_EQKNB9IEBeXRZjl_?rbl3I@W zn#rQYRFC|F?%`i(n5`?#`s;4d#~X;gVcWyLBKSO@>bV@`eZvUl;#gXPm%>fC|D)!g zza0t!;Q>E|*~W&2Fu`;Oo2(~v)wEKwD`3*dD_>504du>|NwHG=f*)0nTXWKh1WjKK zfy<20`^oW!sgDVc8x8J2a{ZIqxX*I7{&-~^KDdT4Z27nETh{t^TfBX;GJzxlzIIP2 z#Hn{3DuH5S&&y^GynUo`Q`$@XTpJS?*PE&)PJG__j;6z6LnCL|!cFPeq|X!&+-!Xg zO$0rNgq%nXCaS=|a0k(tS9kP4g$f7U6@VJFee_g^`<#V=$Gy~yB3RASqGtUTz52}p zuKgCvUn-?;dx_Yt#+GEz0G(D3h-^zvI@#& zR(uUuqd)ZT3hbe@=H|MHh!iq8L9t7vwBPq2^ds2g3V}vUE3XD2*=sbfs)|F}pQBDXdmRQ_7 zg0Rj)vpbJ;Gx<8V zYTKndY+Z~L0YzPlcv`VL+TUdG?e=v|B&(&R?vHdN0=lsHGfEf8Ru!6u`FCmrT`Hua zht$@y;=B_#IWq;VME zla*o{9XcM5L_+2cU8Z>zwhAtO25B`W@~^?1X}1Fu@gNWyrFKUo9tI_hx+xAW42pQD zk`%tVMzaGzd3sugwQG`J62@c1{DUU-OjN|Aj{DMu`Sc{kJPf6*zBs;)o1jig>V*R zEK%fajd`)8n-7quLYe+a6SBKPX&Zxj5_5B&%7s^nu0fg8>zte%(hGWq@g*-ht1bjx zPekF-dj zQ=t;*^%D)PiIu(WLo}Wy#jvTR!w?x{Wg2C_QBTv#q=Po4uqP18`jFcv{;=~uRTY@yCg5k2z zY45N@z<+8UQ-@kKuQv~mR8aL={Be0&Q`0P9?T3}%h}f3%@;6?bE)RySjOyqkTi&Do z)7)^H0R}3&<^JR^rI`L(?ewFVtIPY(y`m*Qh4~M}$qS21O5TCtut^GbGR^9PhbtkN zI#FX|V;Y_AU3PQj1*76mXWwwj!AP|H{DA#m?wBy9%Z=Yoa;bngb1U~+)TkPp{0@Nv zIgkrZAP@rPSNV<379Y|ffCfbf1-(heFxYzX(Mq8uuC=S*XX@I6U?5~p= zJ0WgK8S1d7oq~-j3u=`{VR_!h7(r96!W#Ze@Aw3dtflz)_<}1HRl(|`*aD??2C+*x z=s6w^&Q0AXMUpy~&6N#|2I771u4V5xC#rMf=C|sll%?bDt`v%4a(slYp!D_ar+ljd zc~3|?{^P&cV5T^S!zXYxN=#TdC~BkT=s@pxbJR@-^_`yVWF94-UVKI%3r!&qNVBsJ zDB^z(kBNzy2moXB_wbYW=d*usDF){!$Ua2klmp(j(PhdPLXZ6DNRE3}(Q zaiSj+=6zcvv-x)8F#wp+@R5;`QeEk3Y5f>=C>$B~V-rT&+K~eT0}b`-q@<)8+S>hs zRPQ7UTsErDF?UObV??hWND*i-dR;+*Lu+0c>;?ngm@pkmeAM-_G*7p5L^ldQ~2MW^Z@1J zl7ALg?YZ8co1LtI2qE_af~EpF8YU)5v9V|#9v%pWNw2GNaRg=y)kg0qZ&g*yW~a4S zCC*NKAt9jw{>gj^N`8KRTL*`TB?oygF9D9u>n}wo92w{U0N`VQ-io^0*e4 z&!uz0$eVm-W=0x}e6zr|*u*a&00aX69=s^3sYS0jMUPwa!YNx=(3Ys>C&tF2wHk74 zi+$D93_m)uYc=F3E-igG@W7K+4J*WN4iYRYD{I61?R2@xp4L|qq7Dn=)IwU*^(xGu zoXJ()+n>&EaDLJ-8TI@)^IJ*5(;G5PBgkKHJeLcbWN_t#)`l!=VL>}GI?C;RW;m8B zLYV*fuk?J`zNW{niAG3>n4FyaquUOO`185USQanxey%r#`27a_zlDXOrY1m!Nstf^ z56}6!KlI)7#Kgq%@ZiEi*izNpzq=VGi62uV24@^gP06VP#E8NeL}3rXv#%AYw>G)hxB!eVYt^wLn1XywKIwj_m7`Rn8YD zzqz^bIc-`<&d7*vbKj@o=Lgh)A6Xm(NPncJZo0L(>-KT?t14(0J3J%Z-&rp5*$jIlSv+FxWO-;NK)!f+0fIZ$! zE;H%w8wN&3uqT@P{sjMe>dnnfvK6rxhxa`lLP`Xb8{`*Y}#xm~iywzQ= zev7AG9$sEimAW*-S6Y=T-Jc5@5(7`~dj0ZCuseSMPp`uccvMB7f`o12>;F=;Ol zIY$P%LJ|eWM_mPlXo%-^t_X0Ujl%LePvqq}utvLv^To&S{@}^4^!IOKPwW%FK!4Q7 z83?1FnykHjMctPLaNrY{@1^C=&Q7<30IAf!UAG5}Nsl(eOo?DH#Q#fh!nI$nsBgWi zkssCN>BCgbsYkA+ysRufbBQ8FWkCT900598zKwD`v{QL(82V7BhR#sLIVkOAfFPY%p!~oJ|a2R4N_)%c!N#OI1ynC4+Nj!M> zLQGXj2_421y531G6H#+=;_z=_VY_WVulM?J*7fjr#v^$bv@L8-4su*xQzIA^dH4uc zh~j-%yYsubY;0_#VrRz~OrpFa4J(mgWM*bIsnA}cFP zOU$;lwN>l7Egt~&hoBR4u&}aT+|&eqqoPV-=N@AaQn_!x6CS7RIkrtN1S&%dWD zGYXM}Ou(Jo;i!54A)Vr25~`Ns9WT@+7si_GLY`q7r z5CP>sb#*x8BhI}y@ehRoW8+9 zQgMjA!>wRZ7!?c&S~Pi42X ztgHt^%ze2!I3b)f@KDL+Bbm?J%iU9;laUem$jFGgjt-I(HkaAoh?udSvZ|{7iCmG3 zA=;#k_mzY9A|8xhm5%6nH~gac#%)RdY3^SVqf=7%4RMTz-%1BQ}9 z?lN8jD&}J2<3I56eNI{U^It3g-^Q9ZM9$rvw?sAfsKvpdc00mP85?0+zf?6BEp>QD zw+4pGY6QO;P0oX0qg>rX0L@#QNlIJWvlv~|$S6T2N1(8+P3Q#rNkv7@$%%u9mp3Il zTiDb&s&`Y;$cQ{UJ6qkzC?PgB7B)PhCO-Z{r4eY}He%*rrOBQU2H|AirNsZNZ9hIO zAtuH)V1t4;j<&gv2V#Vld_kWK%UPQ>N_{RbH_nbQ*&y+q@?7L|1~E&`!C5l ze(aoG{||blMJ1`Ct@F*^T0OS+Ag7>!e0#bgc;fo@PFUX#{nn_8Pe>STHJVN>Awg-- z?7)3XPgJ>yglgjJ>#Nu7K#hYV*0-)GmzR;XA&lj6v4L}3yZA&^9QxRzn_VnUCzd4KPH!vo*oeq5wUjV z^aK9??9`h1CDUa?j+qCn!6UDRJw)f1>w1 z0{=hN|3m%1t^OPO`u~HSr-?^Bgz+zP1ECu|$dr8I!46jztp~uki4nq+sX`gO zAc))c73JIg;Jx9*zG_^(2pQ&T?E%vEm6cLPVL`!0V+}Mm7Om2tYvTU-yNN{T*qHLG zS7Fdv5Q=R(6effnh6F}g*5QQT=deHL0ZdYQpN)-^3)>SGa}bXPh=dmWIq4Bk5`gug z=pL`6qMoCB)A3;=5QY&}8ODV=1dhs^fFl5A(H3be0As)q0=j&O zgX02pXM`-;horgcSs>be#7x|py25pz_J{Z%-@#}_ z`^yVHW=QpS zzg%^l^L%i)T(KZX&z;!mB~j}^-%&Lrt$R`fOcLtuDo8_ey}#ufwjyQP?b}Ps@-6pl z4a-Ol2e!zNlCB=Fz*HNd6_}EQ_)bNM{Dbo$_vt{*c}2SC7gTlXX+Op^9gnj&vsviR z1Ra(HtokaHeVSv0Ie{lJ9w?l1Vv{($qPHOqYUjY~GSck+EE;!lC;eu~VdGq@C`Ncv zL+<~CNp4DyjU(5coRP!s2>1C6G+T4wf*2y!HQCbyx`RRVhY>|Bfa%VFSw%G^J zYkJ~byxP`!OW`{ETnV~!{dk`p503|98Fhb87UB%2`4$`ZT8r!-^y~O8caWu};1(-^vg*3Wz)g)j|laGA|$9vTTL|jh;4K{AYuY;xuTAf~3Ymt#V;5T|QV4v#=x_PJO z4P2#i9KA7IHk*dl3#ue}lWW$`wLj)ApGUG*x57#IY#_r2O{N;;$$t9u{o)~S?ImfH z9jSIV8m2r5-BB_IzFQ9FO!8kZ;pR6Hmx+&oct#$F{E|zY36r^uIbq_QzXI6|IgqPC z;5#8w0nw z@18{FntWVtItT7vHt)Iy`~Gcq_6|JWNz(=Ib@@Vb%PMB!W>f^%LPemhZFgh_Fh74v z8;~@kO0tB&ZHz9Mk9&vJB;WQ@zG6R>-y#aw9v^W9_XKQTEiUSHTyH6vb=3s$!?Ye- z!E^--`oCbT*ROpqOY;)Vg*@fG?p;y>&jPDNA9M{vwWWee6aGOv!3)17Y&7xIy%u1* zUp|Id42_TXFZ0{WygiG=0yS+l+EKPdGujzXA>)r-i z!9U-rkc@~Er1+U6&O_yCfEri?oX~&)=vA8i8BxG?k8K*k2D0umfDjz~0Z*_L4o?vC zILr_R+};oCJ$$+@!3P^W0{e{X^OjKtrpTmxa;$vrZORmkqQyO9^V(dTuYr^2{j7^& zDzthWT0=_-2{Hq4kR;JEZao`->#XmRv8)6J9|F%jzJ;5DZ{L{C!0dL!!weDYf z@a4s93kc7-d%V#arbK3PH&ny9fP&LM>{z;ryk@PRr)?)PgLvCtGY$L(8~?SXPAu4c z$+{Sf-ykfuE^u0;TUPWfq=3^EVrC?--O0>N{RO3b>K2O5M@EqKm!QO=7%|I#u{dp7 zFwSpU(DzruXnLY>*d(>;#{D(f6JBDeb7H*e6#QI~vfi^t1)&<7CXuPcdk zCWCvTX0ew1Jod$_MvbO5;bsALHa+ccN_q*#1hT#O!y6ppa80d^2Ev3e|%s*v%^G>AJx)rOLGtPj3e?mDXepw)#`&I6+L@xz6nV@I3&SVoF1R`+S8^^ zeX$=|bZ4r!U>G<>V%#Bo`C$RVwUk4=Jy%4uD5AfwT4Ka;>yTVTHX<5A7Smu-3 z(bTSVcCm&~dGESx2ck2%UV}wsw5y;hyuvia$4(QNjOfj20_z`pL*=@^oS$;QZ;UZs zeQ)aI?Nr&b4(*SR1d)Wt`rL7k7e#*c8J(|nkdex%27V*}G>v?Hj)k!OH2dx|c69IXJT8-=IBG2}!dTA`>$h>RExpZf5t%&?lclO5 zitlDJA*QSG%@Gq`LjHbaZ=R~MNwyK9kc4kHV;M@%$FsGw9}Rtjr0?B6427_;t))nblm`e-|!_i>GY-(ZTLg z)uMx@|a~#|lY9D~uL`e?ZGMnbD z+%MUhGHU;G$QliQ`jx=0a2!;h9YL-(o9gKO+tbB%8#jNXdm>E(8=4iw1H0JE{h2qT z_FSFVVE**TGs{*tv{V`oQ+CkP$o;f>OPB@I5*NAS-0h>RbBP&2E?r1_6B~##e?XOC zKA=91Hxr+7x?)zu-GKY2qlGoL<%uHF)z(0l%3s)i_pJ8SWj%Ukk=oF5p--}n>Zq&w z>IpB&jF3TCP6tY?bekR{`rs7dq5ScGo=uCFyuO`I*0am&0P@K>7DB(RY|a*uGQH*y zs$VAos}VwhycnEZiZFYc-Rq3=wZDOzqyo3#yzv8}RFVaplsY1w)qS5~P4z9XH4V^o zc8~RQc*3ZzMF&=2tvIncp>KPE3JgPSMkjA`ZNIhEsXHl`2-&)Z^W*?Pg(j==*+Shw z`osBFQfNF?k)84GQzwSdZ}mN276C@G8I!E*po!k?{EC;PX zEXU*J?KZSsX*HuOK;**)o5lW!<=Ej8Uofja?qQC==GwW@mtPRt1rsk1-E!cQ#@6v&#&H9wDQ(q#k`1bJNsMAqpUnD% zJa8xLKu(NGaQkRW+dZLX_o|;9KOaY~OHIU|a;D>(Lr1v1YQrUKY=ZpfT4y<6<%4V5 z%2BPZPaZ)r!ebvFc4|7a}ewW$#sbrBt;Hqa_m1m#HRAEq4r9#ON z``F|y2IU@`qs5{6UbWtI?Ba68^HuuyoYnt9VV{!I4pyKAFh5F_;|P7TD4 znX{_>>`cx`TOc;ZBMs9#vivIhf{~9dX8qEw)VyX%>~A;~+j@RRVWmF!RUsdy!sdQS;OJz|oNr8rIP_DppJ%vlPT1Ca0Hy zb(vJchbdke=y8dE_05Y*JDW=uO1)=tH?f7APsbHxE&k44byP<#HGftLKt5)mZ-F2( zKWu(=U>-Y}*={4*l~yyp`mVV=8IHrP;WFedeq?uqf_b|Bx45y7cUCJv3q?=9#a)V? z%PiodP;})r0XM-iWfIxr*}gi#!cF8k!L(RG$sJCLPZ~UbhCG6v%Ntb}#Gi zO6OC;@)WZ_9TUiD>DNaZWo*D%wDryNT~U}#n@d-Z3m&w;&HFa6d2n+`ZukA>sg-1% zernw{GBd>VZCwtBoHrZIz;$76)34R=VAx)~&%7s}k!!C+(_ZM8n0}P(eU@&Kg})ES zs+Vk^M|H;bKq7O)4j-+yNh_tvyuE#bvv@E4zJt`cY%SlFD2W z+4vfOyR|adIWW%~PGX)h?`;-c6BUAaD-&Ej|0G4RL2msu0ABbdJ|n+s%Z zVmGrWM_d`L;i-r_+GcQ{^2}N{@-+1HQVYUBy9qX-W!oGOIw|i4Mvq43b?lArRfyI9 zX>y0IRMCIz>$-wEtFLK>$vg+cTMC``5tu?6x(nC6RsFb3vi-h+@l%|eF})+}BYe=on+(%FB^>%JOFCM< zSi=79tTa1WR&|z08r=y73r2gK`uq5|8@3YEJe*C=zV2!BmKgJetw7M+yvRjU9@$Qm z4%4I}FMr1n~Y;ZwVS5u%%aD2XRUhgPLvid)KS%k##E!Tjw*Y|8!Qt z35d6jF&1QE?!1i18lQPwx|vXOdQ14W`6fu@7_S%4veC?}z{lNxgox)OqbC{Ngrhjk zv@ETM&VTVw4Vo(4%gfp=KeT=(*VVI!<_22IZ1cuk9(~t1xp9g!)<^(P%z%DxyLlBT z1$#O!Q0S^Z-G?5=nuG23x;P)jjPBAIvNO{_l;#bgUx}auZIAmJ z4_{>Q^*kEkrY!|>WQ2r_F1HGQ@=q;!lL?;Y=+A|^;x_9;6cz}S7CdnNG_?e7H}jpU z9ouwy7;yU4X*X=p{N?Q|n$?kaF8^VYbclg{!Vj;hFd(33klB743l~ShLFl*5AIv}5 zsj+u2h_0=zayPCsXWX{%wf`Z{drD6^x$!#93m(p<&eQIN==87{ ztjw{*_0L%PzOnfI-fcF{$+9C;$x%P{8wVRD9b%YXkkqcF)M5Poy=&A?DQ3jsamOFb zUdu;ZX=J=>Lf_mt*422F=vFfe8#l*$V`msZcG~HJSvRaAMs10?tg6(~!>h>db7^s99N+L-s_umBk zpQ`?PO3Hnb%NgZ6O*J;$f;{oY#>M$NT9=nyYO#dDG_>7)2mn*el2WmZct5fS4+xMo z3)LEcUNJhv3^+WX{K4!fm>9ezL{2C~EYG8}LXmPoNCZYEXP!~qifdPNePmmZN*$pm zw3@$PHNf~dhy>&W>B&9UAE7~d-^%+pl0TIOa@`ZS%_VnhASPwG0EA4H3wGi8? zHwnV3q^H+vO8tYiASq`CHaR{^bgR+S_H0i(P-H88`Fo1vRUjJX5&vQyUGV@wr@eDw~W;64AKC8cyeho8uW%w^o(q|AXz0Yq`$E@j_A^?APJO7HGjk*T> z*U74j2R2&vx`osIn;+}zH0Fc2yX0J_E2DJ+M@FQd$+UKyPnv~7{pt-5ChTAJQ%;P& z_fIS@#4RgBb|)PXPl9sa&RFh*X$4mCcJK+co3b(lbfsR~OBJ1Fnl-fdZRuUU)vp2) z$JPI1oLn=CQFQ0Ao;x!~_S*zzWu?H_pH^H=nDgcx)SkTOA-zQLLhLtR?G}FD;}9B-R%t2X1q4CrEy*j6 zvmuQ(X@A?F2w(tg*^95Fy1XT&PpcbPj)vX;$P^k9O)JRHHyVvh$IV7^*WSL+iKwp) zOmfG=HfbJHS$Sc6#ZrM2HL@>}mGZcx?c`{tDa1k(6`0=7O8DRbSn)60JqyO61~1$<|1!^)ENJgt z2)Xpq5+>7|y~R|ZtF`m{pj1!dyySjC~m@sG259ZbRuP3(z9t%*?58ZxOG3d8n z6a3|wkk(~sI+rMifc)Ua*E20r-cDfphL6|ab%JE>?-hW^V1qjx4HG| zIb0G|s{7SfBjR_>(W}u>k6R(A`oUgK?{fC!)zv5UOa;*GK=qruEE5k-4QjkpxP4ab zRfQPl8H2dZz^Ou2)FY>6E($0n>3mAW6DyFv?ot`_EUVugtC48ueG%y z(^Jno8l05%lc?&i3BpT%~@m*Opk#hcTmsO3rB*|_jxjJ}24>xQb25%EUxttK8- z|3zO_Ci;KadCRD{nr>YbcXtTx!6mp$aF^iFIKcyf#@!O!-3b;5?(Po3-CY~E2Ko%% z?>l4cefGZNocrV6Klj#;*{fF7nxkyZXU?aq)_I=1xEE5IP6p-kfZaiuN9C`kt|!-v zi}i6<&!ibT_r~TM>r$aERRshBR=3;+g3b-ryPh0o!q)wat}r-jdpaR=LYC#h1)SZl zYGfQFX@qd5r+rjrc0*IXd@0^!1R{|WE#Img1-x&f|K?2^5lp}hieeOrfkbVkYI_!UKF$~yWF=3J;$jBP@;9sIorBYu{;BL9J7%Cvd zJEcsly4SFe5oM8E1c`uek{CC!FaR#L3cH{i^4yW4TQs_;hzP?Pak{`^zSsva8#N1+ zLY!0FjLYV7_V>5X($ayU5m416ay1g81iD*8QMvQt%h*$-kZ(07v%Zyr2*OZBcoL(M z{0BpJv@R&Vn;;HBnL`t4>DEDX1(|dYfM32)O>?cldmRd^hfRuRi5~eBETP`k2NFhwsSlID@@)-!I7n{>Dy>)2M zE4*!pU4Pr$TV3^uk7l@3APVD8f8?Z>+@)v4u2RphL8cw@D0V2H*pySS5@7A1cap%j z*l0onrN8Ww6nU9@wacQr%>3eVA7-PzLF9|>3nW{~?<9geywmiM5-S zQUt+6lzjYmlGdW-cLBnoUKE##xag~ALGHoXGClEr_JE?h+k;fypAiu-sQ>(Z_k=LJ zL@`e&q#OW(T z!X_=<6Z$^w*zknZ1zVEuYv!Lq=luVeq55Y6-)lBwVZ#&rtES;U^ZoCdh7l1aj%qmH zE2S{_6P?1V-Vn0jpJ8g8!Ezb}-QbQP1~JjW_}%@roH$7>UA5n*YmfyhrQBR9w-f0+ zcJKSMuRyVj_8S$M7*gbJSqk5NhFqT+1;ej_fk3W%a&GJk)lGjeYsDd3ypIzJ>~ppaH<$alZxVqQH_pz0f1{Y~sDfM9Z!zD?VlOyxRQ zXF^d(tS1`!yzn1Rf!{2T8oNlmd2c90p7#C4CIZ0z3ce$XTI@#a*M0|C#g3a!k2`$K z8S6=58>la*-vQVhaQrAS`22@thZWjSyS9jg8b42Ya7_AENu+5_8t;j2a_&dwaT^h%dcXXiVy0>Bggo)jDUHJ;Y18^u(%Z(H5a zUH7gtMe=DARzD*n1>8=wc0tMlq!_hzb;O)za93A3;8!}zCYTe5I}W3pKNnPlIba6s z0$JNw4e%{{M8rKXa{oy;pp8ZRlk*id51lOI<0|&GLNBq9Cn@&DsMq5#qbR?zise3X zL9a|x^JLxq7{8d9lzq_vSrJhfde~J{qc+zk2%`Wrg2y6Z_MPF|5n9V(tw1|NNaw>F zx74%vUa4Hg;hW6Bh^>rlXhZXJ;zC_OqI#j(sxo0BGX3WKAPgS~L^lj`*Ta*}+Mc%; zA^KKtuYRKR#mDcSG!+`;F$C@7C^S|b_lClRXK1K12_a|eJihB;l|p_#_Ck1D_3FF> z%fGxro@EGqorqyl;IhOa6ru~Zfa!dPjDR0aID(wHZ(VKu`w-l*&_a`^hCkiSxm;Uq zUE96h*01`VPFNvU4lu>e3(%7gxQcE}TqODNlFh1~>w{1BU4lj9`CF0!8Dna5CXf2K#n{1X<6G5FQs;`;#Otjd2p*;4~tOPGj} zd>nu8wVQtmUb_*x+Wk0F<`n$7??{0TWSx6Tf;JRZmt zP`3U86NvrCpe#|~2^PaK%h|rw2`@CPV~tqgyI9Q#L=F6D#<4cBt95xhmviA&F-DGWA2df zwCV-p{x9a%9;FPvQ}auKRYan~8pP^719t=WuGO6fPwOc6O^*Hbe-hpvATgp{k^8l? zzeTn;h8|a-3Ex}^^Tj6eoc5Fr%U^az8<8)$=3cCEQhc5Mf@CvWoWEPO{VuBlXR%lE zaczxE7>+l`j``UOe&3}(Nte8va_<>Z^?JLcniCa+8*&{YdJJAim$TAww|}Ir z`}5$sK+p?KNvW_~24`WIcR}divQCquMAX1QEW!M-m>c^E_!B$DN5?ge@xt~KQ7OP2 z9wHB7oq+F)5ouGA@z)n>9XWHBRyoTNFdAA&Jdj~`b-Rq}CU$ocq`Os+JX%gN)j&Z8 z&tJx`x4z?5$-qp+%Yk*1x4qs7^6iU~rMM%Uxw8FRpSh5l zRc8Zk+knKh2Ce8~O@PhQ_XJ8*447g{Lxap4cz!03>y$0(0NZUwTpYDD&=Wdncq?Oa zHJ|xAAaD)KHGFX%_MrdR=gA#Ab;A#y@UQ>Q?Vr_(x`5}}SR|ybzN!J(@yn;GoPPYQ z?mS5L%*FL9h#WKJV;1Dx#3vXh;L-Z`I>P)Fw#zED(M3}A61 zmeD7d`D}F2;ysSk6X;;uqn~&t3I#rwGh-&1%pU(eYG4y#+H`l4eDKydN2c5t+&CBYFlvl z-?yAI4lbq@q%!&7YYCJxV>l}U}_{hKTMD!8X${YQW5CEw!9DUoYvhwQ3;IA>~nuV|dF5UO{l}`q;DnopC#x%7Q`i4uf&-`J| zKMOwKwfi@K{4=Btbo95?sr$%m{n8vkGN3y8LGqGO6yp2GpPB0e-;*Lcd;d1ka2`Xh zqwAF@c44z25P6bf5&4TVl^+Stox@<5xbMEu-nqbV&a3-!^F9m(@E0!n-#bq)DbNa8$U1Pb?k{?*6P%FPS$XMTfqoq!*>3^Pqt@$SAslE}vt z5fknt94NJ})>8mtQnPw}v>K>fz;Ds1;cAJc$sEKJVp02ME;BwvLUjDl;-1e^1Cp&J z0r%vxUlANzLG&;<{tD;$xY5a%o$-7)M9Dv2o7zZHU!OZzvSJR8Abql>-r&;BRkXL` zF5H({f2aGWRPpmJe>kVAqb>C$*EgLiuG>Sg{bEq(C}CxEGzU7I_l?Acug%8x%)X;x z{06?EM=J70PbiR>+Q@jHekZpC=i4zmKZ>pAgBf`PNPU~q2ak|z)GF2BVQ2OO#sy(KWgR#5{v!d<4-DrNUj1y zpnmyY8`LUxbGu7M38(1}4V0nZ4NsN$9xAERTWH=7;xq)2A2;p(tHO{OYr_}+T+fBq zJw}ArJa!XN(Y3VvGTcr-FyRs?TjetGw5EwGZgsz1IydfM9%gp^0q6Q=LNs@N(O1Py zrh+R%&b$(w%URMZ27Ae=MR6!SO{}1Bucqe8fIAp{cFClgEsA-Ui%arEmYai>AFr;p zqQI}G-N%Y8A~JMp(`@(2Jb*I`0gOFKICo`jzzf<7;r{55HK@l}-F%PkGF5N!`zcGE zhDN{3R4DK5cNG%ZO#7@0;QUdvc^fbraO5R?0|S!7oDy= zNYx_uQ4QYPTV}hJKTs>Ho79c@AfIphahyAG{#O(Gw`Yf`n;&s>zAftJP2Snin6DMf zvnWvD&uuwhZkr7#&j@6cJH|ts9yGs-1ijwJ!?{%t`u47`A3KG|_H7>+;W0Ba3*zhE zers}CXu1ZLfH8~R!^6_dstqE@?R)P>u*(gObvU?~ zaGYIMya?=)ukZ#QD?Y$BaG=shmEc%A91?jHPIt}V>gqn0Uy#nbzstVcdP#`^H#;(p zp3_5FQ3-fJR6a-S&RLiL;BkFy#;aj@(3_?e(2p=n_UWam1+HIsP#G8w^b=WNnWW(F zjn<3`T>C>$R>)33(lgi%202_02vd%pTIbe=2BGvEmDdkpP}DzXq=~}mN0q$#xaCWo z{%L_DoYKC|#-=Y;AzM0QV4LY5a4q4t13mhM^L`ri+i=<7ckP_CVI)5K%E8jPwFS`r zki1NF!H||Z$!yM8-7LCBOo(n+w8hR~bA0U!VOffypz;Dx!iL4#mSs$p#uH3tH)?!) z{uC%KjT+q8wsOSTggdN5DscU6tVh-+gtH^-j`quMd`17=I!BE8YBY@E;sX{Q9$o}K z&)(U;=f{bvOi|+I&4W6OrH=+G zSTBipIvibSK2O(@SuufVUR;S@Lx-aN;O8TX7BzR|*?xcP%XPlS$5%h`E}}xAG{(}$ zVs6*;z$fogACQCoPgXYQ{e{R|#2|#VrF^$s{;zX8vNG!!YqMw8(nVKG)ut)KF-Vwv zB$%;DS@yhNPK4F2n^2x%`((XH(7{RVitF3HK646dY8sY%JY$p-Xl@N<%vw6yq2aMs zJ3xCC=&ar08`8PWXq_H)NH$N@ zZMO$@&2M+=L4J>nCVtJWi3WmWhwPBBd@=uu`9Df;A4~3k=@38H4e7s}hJJqNIbBA3 z^rY*{ec^oB6fT8wg!aQjKk(@+VE2cl=jbz*VjW4%G|Sz0>5K?@zrpmq5jzgpaQ%T3 zeS)gKeq9Zpp59(vz-1s>laVU${Oiop6R4~Dh2vr&CVKN|7Kk;{WWayfILN`gG$bimVdIX;oA4^V_6Fe!_^N$ zK!lCuSs4pXqKnSes@E72?l4sqeCHP@*$5@F&5B22asd?0ADriBm)O}y2>ag~JF1{G zeK*&>DhU~2@FUxqDvieVNR^6M_L(6EGTTbw!{aah0X(2yvXOgK-i5A#>X8((RB}x! z?>#kvvOcf(C~O-Lq}P&)5)jB-Xnl9l(=`>nAW!lcjiN8fTbY03IV6GGo8NWN2p($iHoS9_0{Y+)qq6Sg z%+vgS2vomLCd6e++nAuBlGDKLBBvJKIL&v$R*d1Fd-Ie``mt(HQXm8dxPc@w5)UAs zr6oe%zbM-b-T^*7YVk5`aE^usb{~l&DW72GmyKd|hzE@AU!Rs;|BI_aVM-FGMq$F! zity<}BR<=-=p-?xqDEnomX-*OFywq^hVmq(@wEQM45|IAC@?gFu@nm%9{*qLlU77T z5DNA?i}a78_slyD_-`fuP*M2*Qu9tlpzX-Pu+hgfu>yX}|! zhg*EF{+|99YVrNi_w@f4|0hEB|IJ|;b|cgk)`$66_PMA?R6*f{Rzo8-{u;nzaQY`Y z_)krnZgX7@Zf;Af>T?rGaTf9H3i zp{xA)`Q5|e`_P(mh``~D_KG|xhhwIiudKYBnvUoFSh1rXq%M8xdM=@)v*xh<`063- zR!&~|{d9YD7ihgLHE9m7Dp$U!NK|n&n5W{L?jcUE&br35FYNTfFe2i%wcAX?3cHc+ zqkZfMEeMlmZ$B)dsW$Cn1NU&N6O(~rtJRSEK3=ug#0i_;VkU#3J^RTr$^z6@oo%&| z<69H1N%NK@YL5)e3#CT7MutIiTMmi$1DxK+S@K^iU)F!eG}SFacEZg)O>?dlRf~o+ z)9nP({Htd-H($evu3gNB#~R`ueoL{Gd&j+iHFT?g%N!PjPknZp`7lOI1<$}wUM$OKN5h^q8mDe#cf17`a#QYv+6h((v^pED zr<3pF@9Q*&X?I6gqJo@%)`aO&J@;)rnW_V9_&qm9IOL>qKR)6S6c)o` zSPlH}gOe(RUU;?FL@FIJ>P|U+I7?0G>W;6g1ItSpoMz4C!I^(|&Z65KP+Q9dg@TH@ ze0xn_P*^C`@W*6-(e)ZM2odAgp3?jXeaqd(Y~QWe!p0B!^AZk!DX9TP4+7-|CWf+g zcZ^3s9jiaega+}`x;fZ$Td z-LCE!a<*u1DPTQ|vr9;_*AEGqwr9I|g_n`Ha(+yt3QT{RKmx4wLy@~w`6&8{@c3>W zgTR9ozfn(W_)WjmUOm0BZ&$vul>)0uoSg> z6Jnq&RA@;+n4=XejBXl1x7c^e=nau=YH(U4Cxl3D+J8O~3w?H|ku)6KHo+osb z3V?c0!T`940sUTj2QpyUE}s0LoL-j){z7y3+C#CwF6+Lvyt0|~8jj1O>?s0{O^mk4 znlaD;#}Ba8kDekF%mRZ6bc#^#NAs_MgLSs^Oi)(%0e7hefuxi$1u54r#T+tCi;sZd ztmV&JYfx!DpJI{mt_iI=+x0H4%jtIX=a92y#$P`XWyWFL6#DZTyOQ`h1KCq0wXUPkuX zJ42CwA2hXQ#be9^Cg92g;3WNCJ_&IPk3G7QLS_C2Y?^yVLxt1stMoA*6y$b~k6u_=#KX`TZZCd)l#MHK&qoW9ws?(7@C`WQQ zbD8dEM3`oQ{+p&XYQj^HbIn^I)WQp{+Ihj>-vy0}Cp#9VB2&?vDSBe~j&-fNkQjee z@j+<>=WWoXnDNB=uLid2J_njrf3BLh>c~vdZ#AL=OAmLO;p`CosSgbLn|{_0PkpXz zqOW!$io0v887Dh+F9(~RA4_t+xf7m(_)-i=PIq2m??_*JAHmJTP@-To+xFIgc|+dT zP`YN=0@beXR|^!Up5IHoDJTNK!G`UvZ8_}vsB;08;1l@@V9gt}`KsNHQPxY9*S&Wv z^f7Gc0Gn}ygDwz?)1A<%eYWPt_5{|%7(uk$`W03NzDwKB{w8Go^Yoh+ImNM_)dB|% znF_z~DjcKGnc1tfhakEPdgu^tw1XJ8K!AY!Afzs)g-Gq#?A7Aosg=UlyOh z@w*`lv(6+m0DO;Mp>O%(FXhLxU{YKSAr#o#pJZTPl+-^*OBpv_bh{z7k= zkeaDt>`21AlbU$B8}K>Auk?*V56+iWTC${lWM+teG7b48T)V!?*>nXN&3H{vZdP4OKek zpL_7fp@RaF(Ug+v-K)jnv23Xgit#A+jKSV<``k};tEZ)7hT_JK9G)#go^MySOn%vt zWo?|E5DH%CYMbfrwGTi`=JxdFkgKF*{HB(<*3b`f^-~dDbK`%QYtua(60J&I@U1i0}9`| z8B4H0+*}Bht*F~o8HKO8BME8uaOdUQ$0@-`rtO>sC7UcoV}g)iEQbpRkf^s}v-1 zwx38@9XhU;mVHI_FLn+(0Z%45p!J(r<(&E~&cQGx1+)OsL6?;2z<#uVhjsAxRU}~E zj`p&ct=l(PETLXq6Vs0&2)$(T=!Z0MZ8)&aJ;)SU_D@Tq^{rbtMf3>Gbq-LkGVm^| zO!;yg48tnLg{>*2f(e#Mx4E9MGkG)dXd2wnHamlVc99214|=dE8+W zHIKr%UOq1k3u6s$Y1BP>(>f~5-_o#S!U z#bG*qb0I90D$i^JRnjow+fFbQT3C)H`{KdTb+oY&SuvVDQvT!)h4NFeCu3?nIkNkO zg(EN`eYrneXn5)xZYNalgPdYiP>4vk;W)DQ5(VMp8g-Qqs6J7Y0o|i#DaB-H)#csr ze+8Jj0I&b_o!vb3WZ7F46+gZtrI2##AC4&?aY;Cbfcmy0(md?<$v*6_C!gB32Zla= zSy7^YT(OIK2)*IBw%P=tk)1fYJ-Dt78pU4*E^5YMsl=#dz%`~-LH~0vz{@frmSE}E=f}6VudKJIpJ3>nQt~g$gebYwVTrisjxOf6 zf)!G+mC~a@%ad)*o;HbjQ=|cwP%o;?_`8Yze2wd%V*2AN*3vSE+D+xyzSnHji=HVm z`NF?WPwA;E%LgTgM!KEz!hgvGk!&*@U2VWmyP7b%kJh@)b)8p-l^T!FC~d_;0s3~F z>fa`+xS)H=I*_XR&W-pX+m#f< zyJ<7YcI|0sO~$6C$??<2+3}*HN!u)=@`~}Fg?~JaZBF?|nVHtUp3IN1^Ajk_{dEJf zS<&g1@_&y=TGkd;gB>i5LZe(iOp`Qcc=TZxW(&NYq0W7ciMZm&2=kPgDjNzWC)r(l%z*gHUoaRfw`iFO9oeGQ%!(FH2! zEb>F;p0|-OpBU9lsUZGs())D0d#cFRSy3|ZQ3>qHD&z?F(n{0`)rlZl8FqV>Ax*1+CgA959yv&QRTdyKDQ zN53l_Ju}}BqA?J+EOx6NYOfXrSSvQ&2aXr;N15zH!qfEE{m82Kd*H{T7-`IBNh#4e z>Hyn87`gt|*_HdB&{0}0CO=EB_+0P&F7-0wRTY6#1q786dirU8DqJ7307@#H%vnvK zOGxyHg3qUFGAHtNoZG2g))5KPf{7{cdwUAZ4yLm9?!s%L`8*40=Fn8>IG3V2LS;%; z1d+}G>1)ZS*WqQVP<&fvF2b}iCof9M#hEY>#9KS1__k*p2UdBiP==1+A4AF)Kc6*m zw+}L5T77fD>6wT^gc({MQs7>6dR?K)bGM-wq^&SBLW-PkR}HYw(1kKyZ=&63oP2#* zpr@`_*l|`wb3)RbdRsNuk}jT?c( zn_SS(>v%M_kJNq;ZYjgij_0D)Mb7zj6CHzBb9I!5WV#5usc~e$9w_dtcKQmdh;oC> zCGaD=uB#?N+jusc8xcM+hc{>O29xU}C$xb)HZDQsNy7E;^vF5}Dkop`lps+%(V_c7 zyT{C|{s|YZ@4S=Tytw={rxktu#;=n0BD^e1S{D6dH&EcUKH?P>*{ZQxb=km;t~t;+ z_ptXG2D{Cafd}CI(~>&}(3fy*@9RfkN@b-aL0=2Bt|!UjIcuuCcpmenp5YPvw0@R# zJi=cYp4Qg1Uv}QeNQkZ?^jW*}mzw%#{BP4_r((;=r`I}Xl>E=Y%cu#YEkziQ zt`Xhn`=}4_mbtC`S7rX|c~FiRoJ@PNcCbE;{y|W0CDG?V_gp88AlyS=F>eU>=B8{Y z++_oj5E1rds6i6N=TzS-KuqrH$jR#G-1L+9y7dvgj|bo2nP$4k`B zfU~|Ej&ga+oj&>@^)GfsmVL8ve*~+@maWE{qAmH zFOU2gc_{3YVq_|_anFYC{~X%%-0_%(G&)-DQ=D1>W52+(WA4+Xqjj+=sb^?PLtha6 zR6)waA0&0@*y3AR?cJuTB&S%|c!u?%79)`e7tMIx@`W`vXu_&IQ_C>&x2;fv80YkK zKsHPj#^X>qX;Be*QF(tC^zM~96<*TkzZ?h%T%|O@!4|haN7r#rQ8)xDErhccWQ;jx z*09gy56@f>RSZ54jXg#nIj;yD;LB4q3OF~;2Klk(L?V(pH*>ay1z9qGi&Y&mc8sF? z85uM173fky-)EhRGMtAn^+~_5P?C}5>b$X;vM$M*kiclxQ&swpBmg_*Q{>$UmDbBM zvW8Vsf(C3|KoXN&y1L4CBDXFvV&QHSj2N$Ipfsw5)rA?X8kEwyL2$hxYF#1GD@Q9$@p6rMgr6LEq|F*5!Quv2Jaa`kOdWFgU}$#YC35V$a_@ldp|#z`HJ z(bcD%x<%3Z`l68wbrBoZgwf*BgCQH3%GRd{K6$E+=5qkL=ihD7_aM1Swqg-RClSj`=++`h%?|dk!zqsR<_H!T0L+* z2z7mW2R5?r_zgT~@5Ru9IVGy^$O!c=9N1r%22%aQ!A^HZ{zn43`o~=NK)yg8cs3ar;M$Bk2kwdE@aA*qmB=%k^D5MmckdFUqZ?I# zjT|MMJzLFV8M0^`0#A&8H_pU>-p73?k`-~MhJlGEzzFk22w;A|Ry>r36iv2z(mGR8 zf^F8s<{2Ncyj?k_lkm^T#%lJiBy%Do5AumJ z`quJhS9G!qhd+9qktG~qp6q&weUEW&(*5w@hL|Tz291=i)G~MslmPVZ`tiQKqGEtB z`z5!-xK&U9>;9m?$+%RSWjD-&ssetIl2C+*KYG<b&@N zf>Oee0l(I15zV%hq?s@QV5F6l7xs4`&)7W<{`c4H$j4Akz0SW&o59_h-HpBtMMFf0 zAINgWUHDl%T?l`#dp=Jy=_U%MmK)`yYENe7M4ZcsCc2y~p((M3)k>*yGM_KuQ#dK^ zV~Z=;LbtiQ4QaDc8=Z&6_))n}t6Uee?ZQ;;IZA6XKyX?1A+(T zC(73KQ8@1I#~mI)>*FgGA#6a2lJm_gnf>wwVRxES%bF4G3uJ@L9JaYKHj?jt@dn`% zvpa+1C>8JTKG>d^r2CnZtPH^ad)#ftnQ;Mp-L5-Fy=4#xD1dPScT9DYB%86 zJix%md`_JP=IB9(@D*nJfW9ZG%miv`?R2N#kI+f0^${oP{1u95ZABD;!YHr^xJ<&?RZ@I0cM-#Wc8)#3Kl5y-kCaevpG~P-68EL= z60~RaW&|8qrsuCruhb|%9=hXxE0D6 zv2=@a^t8!1B(U;-tSfp$DpwxA~$}5O^_YP%*9}ThOs$Xn~0DqK7Y|^%5Mi(7`t2kAl4xmV1@V(W7<)$%nlNw!vtB* zOn-sstmrr>VUZ~q<3%Uy*rs3C!$i3?gTA4br3>^kWAM#}Y^ezne+ZRLxI8)>HN6Il zLhaJ=)jvNM5GE5*Q&XQKY2idfc+9;*?k~hz%dfj1AVNu`l>*v{LOArB7VCC18&r$D zQ6$#r6`;kx-XA7E_Ao=!>Eee`#N^?8RWC-;6c6M;`Du#656@z27rc^(Y<*CN%!NB=S0{x>qAM>2u-zmsSL@AvEseOB(6Q ztiTECYZ-T5({zdY^(h;m*lou)uNWl&z3YhO!#b1LG&Py{G5LV=_d~{Jl9Kt%i$^q{4J{ zuC%!w+KNG#niSLJ_r+5}HLUIU(QKs=d(*`SioMd;fAn<%)U`W<+d1W*WACDUoAlaj>6mpY_qUmlLKof9J60xBp8p8i zVseALo1i%C7FbeP4S1E5xijDubzt4Jl5~LV>$PsE`Od>RH1Y|w+49vk(URVp=mNVc zd?{a{IlQ`sOt_pxo>=Q5+EWyYTtrukQ)?kPWm|qb}P-l-I?M?+kR*{nm$MG zpBcY#K$*MN>UDUDLJ0x?046e?`W6L=`8er$ezdOS05~~$LJ80L5@_XT(%~cT8y^Gm zlIG4#m%nLGVB4%rWz9!YRpX?|4Q7e#6%Q?S{RTOgYuOSTypYC0Tf}9XpgA;17jjZU z6#1N6fQ_)2uBHlk{~kY4&f&brQBQ&^?T zFS{7v0<{`PXixTWGBqOsz2)!qKvSDPc^dfm2uC-J-8KfQ*}}2O;tM^kuGwym41{4;ku+e-WL0!uJAf z-oXF8b^4E%Y6L(fpYWgL@eIcd{oj_`g;mfJQUa}fYTmya@IJ_D{CEogPubBT#X-4D z@xxIAr!I3Exdd7xi$7f#9}rND+Q~E%Xyp$#0dE{<|F-{D|Hsz3NHIt(t_1aqq-0Pu zO1y@1O&ZA7_x(2li#!T`FMW`Ej7rIGb=4QV+;|uwxjU6G|d40j_NeN;)*!a?m zN@p2mGz}s9k+hap|K(bo?QBhU$)Mq&VQ{xhg4&8{G{c;`n){3AL(z-15(530?fQPm8PF?D!iU^^F4=R3JWw~p+awOBW)~$$L@LE zUxS5Q^u(OlBHMXD^xFix3KIl?KF7)qI~u?;Y|s;_7+iGARQC(seX1NrVb_$-NjaBG zvqbq9VA97u88QDWepUMmN)O;taq6|_Gp2@)4q|s8EaKA3?u3PikK zcNMLC0vi!5Im$dq=zTGX%V4%6%Pb7wHUQXZvEg-XV1@0;nk={P)2PgD2@%mOI}xG& zh_wY!{s5d;q0{i>iyviG>Z-|FAEQ73*sJjV)|o;C98Tg_=y5}mz?oT@64cI{WR~O6 zy~hYD4rPwN96cilLB2V1`##j{h(tOf(Vu_ce@_*dAu)fbNVm_@k}Pt8iO(Gx7Uqby zqyu}A`2}2G!?tTQ=g3)-_W%Imhi|uo_!d^hiK(fIMJ_aYdRP6N3_Qaq;L7)oEC8lA z2)fWz@ zi6*BoOxn=qO5BjjQc2*+5s5uaSnLcgUX>h2QKiHCL$5{Ng*metNT@I}B~Sm$8IuD$ z$gbJ@*he43v7nR3xY$fbAbo_@g0Kq%PimAPC&#EeC8N^!WE6b?m*tV}5($fFtEJ?7 zwgJx7$Ez!Gm)V~TNM23vZWWja$3LEoQ!x_UlxIy&=DKf2dKTkr^O`EHp}1Cjs%jrTniLr?s` zcZ+UMG6&Kf&(6VA=3!wJa+zuMRWH?T3TP)hz$pEbRbel6B>F>(#oO68Hbdn8V zPj(xve8Tr0xc^(Ih^ht6{UM*WJz%i5=aq5osOZt;Nr%T>%{{}#?jl=G_S{jjt~{M2 zw4p!O9G@xr+gAEr#jt8P;HB)#0B~QusVl2Yo3lJZOv!zvTuLiMiqt_!!xia2>LPZ8}cxKkw_Gkp{7?M_JKdz6d}+ zub5JO=Zytl4C`tQ`*BfL>$(kL=T4QHdr3qY;rpwhJx_eOHI-9b6beiYBBfuKpIILdS|j0h z^Zylo{{_hZDW>_qif>vuqUw>qvvu6v3-Jcg%Foa-=GWEL&04(!(Y3i=F-w|yZ1rORavx!wRLXo+M1^;aVB@l z&d!eO3{t*xgh1V)nwA!vxTmLpLGH3wujhfJ3+I!V{C0jG*`Z;p>wSmMq1YiO_yV*{ z^zPtDRzu0Ds#<^>EB*jEODngx>40i3L;UpZ&DZs-Rz>tUo+!hu{3M6TsY6(3Fw)z@ z68EeE!(pp=-|i!Yjgrhj?+)SQCtY!-%kNXGq>P4!GUs7rpLiP+;=f(P1JRJ6;AEuS z_o!`ic*Gaaf7quz3eq}d_gq_p049^CO5+$y!S7C6*w>iPie@sxF0>6m_Dn$srd8&9 z>pr+Qy}U0%Zj{*uWeg<4~whz&=P zaW&6!{Deuf zw-5r=7SmJI+o8nNo+%$Hw0!5dspfNfPn3v~d$D5m!T)OW^*tsIqtoqgHC6|RRNtlB z62vOf$0bY~(PJU08T$q3pe*8+jnfbbKUIM%IhTB=UI^enE=yDS{^y|zQ|5;X2Hi`=E$mhVsXKK|UnCpA&dyLEH2^wMM+}-wdEJu4z`TA@ zTdmm{1MUkIyxs2iC_e$q>pg$Bw46fRYBSX=63$^I7oN%&LaX9X{W`X(%) zH&JhvkIC4>y!u(Jd=L{*oYw<;07&B6w)5`XaAhNUb3^bk6S(yny!KQ$&>bvJ-gAlZ z_9A9H(6b1fm{f0(QlD(LC!E%Aa^FM!A?>ZAL>9Ph>N1g zQQ=OGJClEPFDIzE~6lE5C>p=?f>m4tXO?z@bXpXi^&Vf6>(6cuf@@OPKY(Yv`Ug8hg{M@~T1* zj2Or}UWX7qMvQ-WzW;rDIy8H=(e-!c;*eTC;rkk~(uN8G`H9;%1U`qDcy18ED6fO{ z%g+{(-!LO3m1Q(5Fo8vhI<#~TOtbDUKdV5YmM%=61{0N72w(-5Zj}TwbISP3T6GP^ zs(JN|q)?AXe%t~HWvu15eri_P`BUJ4^(dNAM4ylPLtnKwrfP|5?jJeIz5S~@H`fPO zHiC=nUhLmZ2#fBm_q;46O{z`$TD}$(6n>Pvw1k#9ItQ@ZfDRt_E9fxxt@4~q#1iJp z6OSRq?zn*x3n>n1+$c@aRKmY6J`D8KC>ywP(l<^FsxCqO?4m*4(W=zc=q}f3#&|af zJ5;RfY4OWsUG)dEy^nlC(KZ?ZXI?Rtg5UGjTTpIxJT%!kspAh$UY^eL{1hUxjtdS8 z%P54r#+qN?E`z|846hmQb*zyIrQ&k7;NGBu3rqDpk~ID(bob3LCLEy*#@16u?+EWAj%bamDx*I|@Mk1FTKS(V&z2$>MP99HcwN*a7G{RJ#nKXhErRFq5Bv4{ zTnzw)^H90%AbjTYoVJe+xf^&aT_3*?g%Q5EQc(Iczx0b}-xlksTTZ?VlJcr7_zfq= zAPtsS$39z$ZVwRkTz!^NYD-buEUa(#oG${spoKW@MD2hPQ5(z%t{Qo=pnNv6AkEuG-{) zzd>6*IPE`OexZlzdYR@Y-$<6PSp1tJ%S*uh7K5G?_ArYgO<-Ls+7ck{FjaJxbcOmq zx;4+BrrLE4gMf&Dp-J!36;VM-gn*zx012Ncf`F)WDUlu^bd}yklopyGDm@fw0clDM zU{sJGgd!jv5`=_=WKa0!oV{nhJ!kgb=g*n*Z_WC%)-&(SJNI*4cf#A6GB{cFX-)D7 z>qG&oT`h*)81cva*D1|v3Q9dDK1+IaiJU>OLipG3hTYYv!A|>kMsWE zz6O=NYZ;k_Xl6uA=2oQ*+in?AcU5;+b9kBwbKPA(;7`ga*W3qlIdWiZf6##@VNUyH z{)g`(J;IE<ZA8EY2rHAP0Fmz?b|~?LIWW76>V*KR@zrIM4KuZ+eMbaw|zOAezdye=U=UR7xN#bGEZeLz*>K?2OOF8^_eg21$)T?F1o=*DFwhp{G%h2fVIjf!K9G>dpp zgk2{SBZkCt{24>OVzc8l`=hPEr%SeCVedkJ7r^AgRd&w>;keA+$Ozo&PQzk1f&gPX zptQqivfku#n&ZVXqcP?Ia`>81%ir z7+Ey;`{j{Drmtu520wEIWphUA&(V*uk7LSRzr|fqu}-|<>pYZt@@dYQ)#Z8whm4LhCkY$=aUdGqFTXPX~=@Y1=WLJS3J$0Z#^WRKt)jN~L* z<33pPR24wVVJA<@yGB6td5ZM}WpYyRcP>8tx)BjW?Kq!t5D33K%$h`yavPlH55|4AoTbKsD7ssQ%4D67 zE@|z|_S|Oy__F+0&_WBF<9(t49<&5|OwN?+*o|k*>r6RgWDoa(;Jj9z{{U@HiarnyfZjv6is;NoSAQLsQ1GJKE~Y_Trz?&d z5=Ft0x^WkkGp_ytA;0AsknbUcH}}5J5fCsq&pg8s{*JMRcHHGn=Qs_qpL>Xrd8Xzx zeE?EZOE-B2!oImujHu?L*a@;6@m8)?*j-bRee*>ogfpXO7T0^muEE7rQ9mQ>QmKz) zJlqflHd{`E)X%49bIbj1uSo#iSLsP z&OBC7SZT;+%;FW+c|6|6>5Q34-6qss7lm^BHK-FE)_C27#cFDlNPQ`6Wjz^A2#)^r z^UCX0QHpnat=nCQ)6ijrU{P9$ysCU(>grU_ePgZ@LHa2wY}Z?|12zM3t9vXDwU2Y| ze=1g!^YUZmlfhZJLUQH6*vBW&xfD%9_#+hm{j+?u)!i>=iafbrtsw2LJdc z!GYBg@D$^yk+YCI1ba^M&WMkZXrELP=a-7ix!#igWb>Mv|4lf3q5J1S^(Zs{U|+nS z1PE)UB;H?%G0T$kY~_wrx?#<)S_xATcxY1uyrB_w!g;!&|218-lou9Z#uqc^R98n6 z3_kNN{n=wqj>Qzc)4wYOEEY>r0hu9#DrPjV1f{K^aygab6;^)5OB(}^$bj<|exm3u zsZuVOti=7(i*+6!PuzF9Agkg^JtF&NRmmxm^X2MXqpham!e-u#4|H51UOJn51>x7%;waRa5P&mvPVy62;KxZ%7_jxxed~K=HcJdgN^$u>J z9`WAY==(^#s??UBV`E(54cfLD}zyW5=$%4o&*<go0dy?lQnYH_od42kk+HqSCYiAGC{gho#acP$RMAPQSHLd_{Yd3 z3iypSpZ*wuL#`6xaQ68zPc@fJoBG<-4$bW5pAC_NRR`N4GS&AbJx`+6Ux!F|nxDP2 zykWV*iPBpT&*mSOmvaQ)zN)T_)*{}N_A%LXwli6&?(F^6QR8FcM^42nRF}Tc$8ELY z56xdc+pB{R$6G(j!eV=rTK)H+zfAQkh#Sx^@TCO}`nfnjsTdgk_QlW1noB-8g`XjF zLIe1=CnN6TpQ%_a&(PqRc!2^w0PlJ@BF>qe+`1uLp0<<(eYq#4B3U?`z@A5rn7oiD z)t1kC0c?F%A*L9KD!@+l;Rz%F|ge;V3W>pjP?v-tnKE=vNY}`*4FO2Y)RN zfW{Zxt`Y{2QX0B{Q@py6BXraM8hKH@=-d|(ia<0vt47wAb^#N$2hb3rrY$O8yE5rU z$U%88dTF`$kI-*H1J1-9CVl* zt%lI#K!Ga<8l)u+Trw?XB&OCfbjAEZs*`T|Cwq(4HMXrU8YGIo#OOAwanA=acP#JY z5!!YrQ)w+tPV$B?YN?#y)Hw?@{?VEVIHAGUewv$hp8L+VGM^z05PJ=YhMsb*rS`Z1 zMfx}0)e->glqC(2({n*7-yC4}Of~&Gep#l&RD`7F3W#inZy*S=krxV1G#%6R)L zYH~`#3ON1!stFe#v?IUo4doe%dTE=0=-MyqIt@I5y}sp@$C)3E7S>rYGj_3@{er_6 z23knbT~sk_P{Qn+TfeJ_^HZlSL6uJZxsIB?9s$=u$?t{e$mWrTk@N%L^jMYC-lQx; z4)Z! z!ez%G&7A=1CJG~TUN|#sDg%@~?2Nh;dT;!kGG8s>@NYQPY~fh{_fY98F`6@}&ZFl= zJXWkh3u?a7+q-&57AYzd$q9O7yZ&I@yU^hNzyeDWZ9 zTTBL=Ri1p>c67&!v|Pw?Hj-RzPZB5H9dh|assyce@xfTMFBt`;jg%bw>B6J^O)Zoi z1;%2=c_U6m#+NqgMaIx_`o7RV;DtWJn1p$SO{MpDkU?ptvXb6Mc~tn;_89#dw^L`6 z;w;+uj)=fNZ?$I|UcLKuLW~|oMmQK58b7xc(-7LB19!C?nSYK^+FMpVQt&<;E=6z$ z`o?Wj3K3@T%i{s-Ohe?5yutul-bpM<$}0h2ZT3kw^qB43x7^D!D)&=ua)B{l0z_`WUs-tqUB+euS@&k*1QO!mGGUSNDETh>i0lzrQtL zF-zDXv4vA=RhU~>cI_!gnPwUk@x}ACdE}0HGhq5RqF*n2$_Me0aa#FiKP{)_$xLlN zS;^fI0jw?ZCuAdP^7uAXtfxBHh|SK9J#V0$x2E6{=a6K+K_hg+6_f!(117|>!H446 zqu6_DvzzZ7@l4FvRVB?6D4ss;@en{C!a*YpI}VntNS?|B=Vm?+(LyjIy4q%ugS$|D zH5TdRU6S<+5T())*-2*R(Amqd0Tm+YM?714E}n#3o>`^Rpy+5)M^dxmI6-HtNioU@ zM<6*SA*+`iG%0qaK6;dm8sb2>=_IeLssy&?3G&)9t1sRV$qfp9x0GnP4((ftqkb+6 z3ERZXiBvbG5G`SveiVgN8ii{EkF zf(VhC$*%NW>_yxn@NV@xZ7j{BaOclZ%=b3}XH;lDd$l@#vSje*xo@Zi^_hmvAHBEQ zo<&g0^k0%UL9@Nx#$BY#>IjVQBE2ukqpKxr7)6g98Q26t&`&Rldx>2gY;^F&5==}?OyH}Qj7og$-vD|`G!p;- 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 zcmeEsWl$X7^7i8HZUGi|cZbE@-C5k--Q5WmT!ItaT>^wa0t9!LAi?!5epmkY)+<%_ z!~5-a)$Gii)BW`6r~CBmY;BB+k~9h;AtC?(K#`RJsQ~~`xBvjuJUlD_0C*bo?*RZv zAAB`*+|^9I$edlBEN$$;WbQuBU^1|`jU@o!ymjbMqKUU4Iip?g#0Ia?+p{5JG{n_~Sg7@Y@+A~>pW832UI+@9Y8hG^jXTy6V z?ECrC0qJ~2y>^iI+S%2O)GpP_?L*f2&B>rseURbBOJvx_M1kYnuU|xe#&{Tm0!Lqe zCTt%_|A@YRdhJXJSfd^JPPBCbyTid`T-zq1Z2IZ@_xcl&$^JK`4c9l9#h?c-^BbM8 z@ttj1a-ZI@bP*_-N0D~U`?3q6@sfN1wIJ!K66dzPIPSlcMMA$6qLG`r9Hxe6{DwuUj|hLk~Xu8?Sv{US8)9Q;xe6 zsSEY80l5=T31~{0f<2S;#tCmoeQkGOe-G(fX7S#atF(HYcq^J92~>f2=OG^vZ5?E` zF)Shxa|39(`kT4pk&JGNkGjTNqnV5j#kfC#-7(rbca7n927XEUMSnI)?k$j48J#8F zA8+3-Z&rxbs?*U1?!NP<;Knml(c(t}E6)96gTtSgk|y*wos31^D%z=mI_vCS)V$Bn zt7e%O8Tu0?*O2-5CR+y;3*sxrJSH*To~Eqi7!bx1^hK~6X7=O_iTU@)j&pQ5ya-~n zRE|;vbP00KzH?0-yFI@^Rxi}#F5wHhJ0A?phoQx@?I>`3 zXFsuTQqDC`5oW0Wa%@}O1-3hF{|QUux#n=^;FsqtQTAIq#`%kT6xdKJyZjqgp`C2j+FfZh3)G|m_Cn7NX5xII!;b#tQ;Spl)5VCxccG}1^A3WKEPhQEF17x5%%&_}R(;@j zPuG*CS{HTxr^Gn|UY1n?ZI+|O?z3j1+wPv-Icl)Q-S9W;dQB(hg^2`di-ks~pb&Js z8fpb$BGYf$53KR}k?>P4Y$rkL_$QeSr;xeYtY4VDz}|w+%Qm@O%rRUEWo66qQ(a~D zM5lRPtdn}U8Hyi0nWHe~IXt(@>yq<#cW>iwf<7Z3L4#ZWxUGp!Ss^xYLw4y{;MOUmZYHwvXIG)Mu=;K9pUutwy6KV-zl=Ar z8Z%gx-xRLhJN6Ru$;e4?2a~2rmBmeHb%D{)NG_w^CNyKusj)axWX3HuTavyrz z_!@^B@sXU17Dc|KYuQ2F1^z*!h9uGN`nLues!eN8u(g;s+TTJD9;j_UFgA1wl7nNx zEnk%4Y~+Dc;tt=QdMl~qXgA~#L#(y^!CQ`5E7PWV2A>O>)r!)~uV$weN2-T`NAd2+ zNYI8PA1Sm&9aDEH<(eQjU8La5E@Gst>6e5b^2k}IqEkS~7>TQ2vVpeNHX6wQe0N*n zYm`IDTpFga3S9l&=jGS*xrCliQDISz4q~NVI&Y&IcgnmpeE%XGaLhn!bVMJtf zTiY?=3Y2X0yZy8jbYg`9?8q}QVFpxi`w+J#aG!U^1wTJT*D&t0-<*k=pf{Z*$3go& zJXn>;J!R7*LXknB4onD1^{RErN_yq9wQ`Zy%8gL(TeG<*CxDH+3_zRoq8zIYbiBz- zw054+7sJyym3RH##E#g?Rf|;_Uqc#K70?`BZU!T$ZF zexF+c5ZIjt2;D>_yqGui2pu3Mp`63Tw`*5|#j7%dB>HJSLfHJIh5UHU?fs4CE7(j0 z(1bXjS_A|lJq_w{Zz_y$H8hhXE{m!OZBkH%H$e0tfco!m(lm&_O7%G?N%apxZt`&? z_VImfjeRoD6jA!3WD+uEbOei^wM4nuAapmuK2db1U_{g*|1Q70i-2q} zsOx81I@#SN*@v1>Lg~YZTzf6`uvHtv3w{_{EiABBOYi0oa=Iyd5@N10F>38(Ht=i&7O!Dhh@TtT9@wvm_s zvmcBgbW}2I1wk?n)2n)#sx!9Kr!C{Y2e|$;tz2!SaII*{hGZXPcGGt|a~#VV)2{5Cd< zy>F_>|0A zdFUa&HB-y`FB1{)&-GIc-voMijC;ROo^i)gB;Q&vdfT@Ai9bf9TpS=4tCaI?M$szS z88nRJ63T(~h)$=@P#FQBfApXl+sqIJie-BmCPd(iWq3$o zVto7E*emB{4Ud}#!4=vaJ|4Q_15y2>d%h({4R66nDI6~KlDvp9?)@d% zqoTMqiFUPJ-DR4mvv&Cr+iHRkzOT5M-4r&iyzmyy_;M{sbowjbiYWQbC3r%wlLr}Z zrsH{a$rxi-u(E0y!NsW>3Z9mBr7Z*ozY?H@bjWCuKGKzHVE`Hw>Psn%U^JIU{hD#H z4I}iY6j=RgHV4p(hizNCEUsB$6Tdk5{s4%GMMF)P?esH@hCL6Z8>TF4>A|H~rG5`d z*m3NK91npjCN>neiUR`pSZdfxDKr72ra_Pl-0f@oaqLvSlmtk@G8EJ5O*B!R5qvJm z3Pb#wkf%di7Pa#ztkbc*JwKSt2^%?e*1mi}tBn}ykfzP@VITk}5|UdN;`PT+hW$pr zfd}9c7m9Y2cYakorCwf`85zb6LvHHtA=uX|rBe#zo5SLh)Ej61Y}`a^e&v@+fz~1~ zzR1MqYl}Gm|&dySZVk#I;_tO+q3EV{#Weg#ul3rN~v5)7VFxgxSo<5(+8-Q$& z(kxO>#uf`-7?DsJC30KsXVXZ$2^Kt_=nG2<_7YHz41pI!?33dNUWv`X{6oujnSx@? z6P`8{>ExP8OmfH}6MYdQB7j6N6Z`ywr))ygV21h@s!PFR^F=;oi0!ZgH>z)VUGM743Bj{e>1>Z)vQ!I}Ao;YPyZT^v!T? z@18>6u!LY+%@gYb6-Q}^t9}!=3^X{WQnX8j4}~+13$GK`8!L0&fI2nNBMpiJNt>YH z57p5rV8)0;NQ49F#2y4wp`84E$FGLOixmOwQJB1^4J-W>B_mtTaF&`){tA^4g+C1;J5ekn;M5G4 zK2$X@flm1YbgOsa3(J?deL)719eXIC0AZ)=hr-(<0|cf91QNqStPUuaGSy+7u0o0L z1#FJ4M2@EYxJ4U+o#wexjr?rNWZ^O$7%7Oik4O*o%Jy2;vGH9mO)5ONFF5s8b9PBW zs|m1w9wW%2DZ~O?>|5K5^4l~#h(+mg^bU?hNZ&MA7CD4uXl6SFkHN*yKw}k_q{8`| z<1nQF^-7AwpEzz^M&&IzNHF2oNzsK)Jsa<*j$X{`mq10@ z`#-(ZP-i{{6qDoo8YQm6n}K=H#~{?b%1BDb0$|E^e5!VXsC3txz zJ{}6V#r(wE!_x?#Q(DPX8*y=;gIn^+wgidhwt@6R*tK69iMCl*aN#=INMFcl@vY=a z1$pRZlViF$O1QCj4v>b3BR0Hc@he9Qy{NRdo|L78k)V|o2q%gL}l9v-W_v zkV|yJjMqeQ7~k107oWt2t?;jwKL&Jz`%q0oMPLJ*XN4V)3BBm5(9c+xh+^OH(!S$i z!z`k9#&T67EJ2PJMl;i+CT6S!l?lv^Mn>!s4_4G-%o?r+Rug&8=%&Lvn(wft=ryWL z2cxJN&Ox@cBs`RK(}*fKSQWaP123RY6l$x5kjTz=VHsTKuNh7*#7A!x{5ek&AP*&8 z$`M}VPwz9pR8`mYv~TXz-Qn8p+IUmk-HXlyMVNq(z9W3#i1`-kZ?gH7gGRP6Vf6QB zAwFj{S;RE{$Q{PgK(F)}{!A5hu41L$eG&6b!2TyAyq_jQu%qr1q%Q*_x)mk@F3~iS;f;s zMBf9Jt(7XedV&W68|Q&6lM_#5r50mmG8HJgBrwBy4&l!bWy1`~+yF{*Bw(p_Q6NS+|Ea`|P#YLbTp;Vl8Dl#KW{hOsIJ z!|PcSAeN|_m7eG4LFlbPuwJljFADMu)Z2He3T#D4hHu1wX+<@7jLZ-wDi!}JFg5mxIBdUL`g*tp*W_;3EQQfW|ptP_S0v$nMG5AjyU zMux(v#RBfT2?#$_fBFOQ2xE|JQSE7FH;wL*PT90Th&DaAVG(#xpxREdy zLYluE7PRe-rLM1siy;(N}mf(?JW8NW~*>;@$?TbzonYS%)ZQdX_7w zQMk?Y@_A?xjgB&N#HiMrPp$f*)TPFsPodF-|7yB^w9#R-pF0%m62+G=&2UmY$WMfM z&N+%k|8)Cc(YT?cb|P)r#vU~iOEJNg6ilcSdG3JcFRCr(2^$EQ*l{^DEnW*4R8S#@ zzVh{Pz=YKA68DQx9yR#F8($UqYRe%t2trc~741G2SK+#@AKfG0~NLDTk z-m#MfqGjM|>Y+cck}%KoqKLax4cN3SLbH))fmkcv7LDc~T?EaK&4gJT+P`QW4 zS8{GbDXtvCzpDo~rpFbRlN=5V9EaxtQi%6omS@BH4Jy8kl|id^2;D$Xi}y)%?QwiY zGY<9L8ND(>cLHk<7?Wk|Q5UIfg~X)3A)G-(T${;%%QgbtV1V;Q1Mm;XG&$TX1~{=r z@@m>`SQa}so4v)6u}Ji(APogb`K9NQ~;8InD2?b;f>eX%x0=hYAo6pzga$JJ6A zbq+{iDWapFqWe-C7JNc#5?XP_e;}TZ>-m>|pi4>OhlxVPgvwdSqhs8{L)r;zjSk*Q z=*q{!G`xU`@$LVN&dNWbV@#ZpmcpjMU9}O6PH}~;p(U?Hj}sOfp?lmzs(V~I9U?wd zD5_sew?{B;?|xbQNYKLVNv}|R!6&~o`lq@B0~HrVgP!(Rx=FSOUT1ajcIG~hZg9s}vd`2J-NE8G>Lgmj zA#%J9g>JS_HrB~{8<08#Zu0kcwIqHhxM*s`--nWq$oXOMob0j(0jfW7E^ulv$o76M z^uq76^VA!%xYJrs!(9M%r9}a6v~vu}&fait$ic_$OlKNOjT`N;C{)iu%|+s#eSs9! zY!G&amX057RX{=!y%sMRxFxNE3rAaJ5?wHPz`VNdLCbey<#<2zDvX9J3h2RT%RBX^-jP1 z{NFw+?Eakb-?cHH@`?n1c%3k!X)d0wOHQ>YM@2E=r%MqrtaTJ1ta2n-$kzQfhfD{_ z9DVbv-w)sD(GJxJA3BZ0S+SiPj|a?Mu!$E(d!*8bj>ldHTct9mug9N!7$a$m4Isc% ztb@`DCJ zf;PyMY`gSnW_@o}yB1Z%(61->++qySF>zhEX7Fr%L)CQ}a+?(oss*rl2WgFi)#a4* zO_v1^UP*G7=1Qc}^QsMl@{ldKq?ev|8bxlL_(^6B;+zWPokJj2mOu@vxfsmo6G9zemD4^XC0nmnnHibPV$Q1#>{jqxnmT<) zp~#<6VeI6OKc5O8Qd^m}K*>8B!=8g@YG$d(7hyDmkTtr)otvjVSZT%uwqXp+vABD_ z8auE&|EA$}_l8v9+b^_^xmeI*yol$LB;9Gk2*kk)od0Q|>PI#%ldDq}(2`K|>SUg= zGD4|B@8CWbtI*;mwp>NcXM|xpd{J6kCN|x+qfT!sb1%^+YzOCYBwDIO=(q$Wyo0V@ z?-QjzixH+!yU13F|EF<}*NoauMU0MzgnzbTlDcVQ@~+5>sLkYg_<@lNlkgZmI(Lzy z7)=qaMCtfcriZkpHOWS~PUG3UKvS1bGZx|eAeJ3|zPDP*?KFBOQcFG_1`q0IH4}q; zw)^lOxt2CVRAn{QLIylhPE)@_+aqLWw&J=eRgy>dGeL8v8+&| zfsjnCL<=Vt-dCW(A=~hdmEsItB#jlzzEa*5m#;5#7Dl;VJtmQE%^M!-{~Qyf%Hn6% zwB5`p@)9xdWozhXqV2{|dgV#E;x)lNq9L^kKT0JLez6)91$ zkKfE1>|U(eDOhqU<+>u#unMBQSB0#~P+H_MoI3==B!OtOJWPUdbTF*2BV>E^b3S;H zzTrsbrb_8&09@*TftunmgZbHtar@`RSU{`))cr=dbP}-NBO4Xzi@8 z{2|bTPp#BHW}qCF4=K}fDfLG7oH4F`N?%ms!hOTTJdH&ce1s!>lU$BZFKtV-=+8%P7JYxOM8v4NV)kQJ^p;N+(ol-ovERN}+w zT86E*fS3$1!RwAv%8#oXZ6cMYk&gELemz7yL?6een;CHK4WCb|!XZutpFnQtA}u3bg{U9vHzh__ zpGhJYOO7EHZp2!yL_Ep|bE*OKX^9DFabez)wl5~9Bf5am=dWKL5o}F~aImd z0HH4Chma-vxIKLVq^q(a&x^x~ z90x&Nc|JP*v?bCCLK7xiO$gq}N+!}93Yu#kurUc9%gHMqRiF7&A{>sq$0G6YDf&zE zKmBSDdx%D@Z#|KE!qNgSHP?xlJg&PXZ#+hv&X6Nz7A-inR|$UB4f=5D={YN0DHxuq z*3yhNH&_tf*?Z<3j#uTAfL0Q*(W|~7F@FE!;snCDEy&8sBZw~4zZus1M~wuq!L)!V zIgXXm^;;TH^^+121HW{$G-{V~Qy;N#rBVjTZO`=)!;N{(uNH>&1(^!UZ{ zS5*4wGx(ub$|-yy3ga+tLOn&{rV|Bq zl{t{}hn*mMSolWCS`X^1u%+S=RomTkrQ6hj1I4r0Rz5Ye`>H@W)Ak<>Dd#iO3>5VpI|5eEtV?Rfpj$2<|`arw*WQYnD@L zlCPii1ww;fy0Bl|A9$ReUuiTEO4{FGyuR;`N)df6001B&Y$PO9WF;j2@h1QPz&kG> zMM!2ylz7Bg-Aji9=k}q5=YO2wc}dC`bTnUJ1=4#44epVH7fIT zCx1I)=q6lrZf;s}T>5=q=?rioY$ve0>#p4o4!#BRXH6oT=uZcZjOe)R(^!0eNdVO0 zFfEY1DPC7ruK4V}Gb*Xxi|y0=>uN!X^fz7Piwz|c->)hyYn-h zU;iHJ%$vn{`KerBj<_&dcfl+VIHXs<&~N^vwlV1^9GRjal~Zt-gh{*TlAXi)+WhiL zpB3TD3m_E5wRV>s_XhxgLTIC*FWCBW7;GQO^0bzdb7b6-1iJ`13TFruJ0zqf<41K8b^ z%-g}<(T(3*2>2H+zoQ%f`!EZT>@O8}J0YNsq6(RWlPj2vlbMs5l}Xau#*-Z=j7TQv zYGKK*29o+agsX+65YXD)-IO5jgOCyg_WI!ot??m!jj3&$I;!? zo5|6Q;vM2|7$C5lxvPz{yN#10**m7GnUjaR5D#eS!TH}b-EA!Yhpc~#=6&X` zaQ<~6-VV=>k-oBa!vtfQN|siQgg9m?Cmnc2q1g5Qjjoz={O z)0Bw|%*DsV$;D~O#LHu5#>B}d7Z#5>{qVk)viKz3%<{~1xSH+8pka&-^_D%d!Bc>m9YhK&PQ-QDz^O*U?JHa>PP z9!?GpJ~nRlzd8B~PYdkoX5-}ej>*Q#%+C3j`FmR4g8@6bo4(hn5RmP!ac>7_ehF8w zsk@V_hLe-M5bzy>?A`LO@+K4fTT*0f+}xd9ecmhn?=`OucKO@g-?o6g&0kYwWPg<{ zzp44(oVb~Kf-U}X^v@}CYg0!nu!Rut?-lY76Py1-vN+6mx%oJ_EtxDhS-F`wIeGb* z%-AisnanKNd3eqFcv)F_dH)^V&B@Z;%hVMtW(9V17XnJ#I5__m4H@lURMP#swwE>d zohMdyHYQdsCU!0jR$hKCE`Cl923B@{R#qU(-vefO-_`$!Snz!}C@TIdCx27;mp1U; zUH&ps14#)Z3bOpOUHzT2|IhOGT>Sr)|2p}PUH_5%m#+Uv{!7ykE5dY_e2kdAe1oQ%m2*-WGqyqo|ux9eoAi&%ETM-A1{~rJVfZ!~n>jnTIVZ9#^ zfSf#h0002zE~_XBw+BN2%fq0ZlgbVNkO5>tVjAAdr)xn5nm1b_7r*!pTv-g-gF}-) z63KwGP4w-*derI`wgxI>&QI8H+{pj35>d#nuDu!yZ2oLvr+;MILf5)S|50B)B(nwO zkvumP+$oA1DOL!1ceS(bH~sMH@|$mUvImdZ`Jt%yQxY6i)+FEgpI$ugzUil|!m27n z;JXx?qU*wd-euTC?5{I2?!VpsUxxp#^^Y?)Liwd$LwPy%-28k|Yb&mzqN25}EhIog zTN_T0M?ioG!0NEfIXp5#E+DXS&x3)1p<4QEyX{j0&41#5G7M)bq^zQ;Nx~9QS5q?_ z0tKg{t7`)tv-OeNf$-E*z}3wyB|H187Yqgl2FfHXc}Rw+RsOu~$`J;mt(~2!mR97K zFYLVe(-;EEwzl+$h=|F#xp8xI8od(H(b3j3+z7JoernY8$GjRU>*}ObRaIw-6e*ZQ zL`d7#Jc*!SF)%RLbkOtb28vr-arN6ANzz0y7)=-rJJa3RqNAgu2SN|s5pYqXpd=+F zY3b+)7F^ib*`Gs2)zz_ba&x2nyB8OA7X_kGj448twErf#r$< zQcyrOS$-4u^lWYX@l$l5tga4~H0YVFs=8X$&`_wdpUq*JvqrZma;W5Q{;UOIW85cP z(}CU5(LqZ~i{GGa#w#Z;uWDcrUm~4^dcPWhglD$&1?-Q9fq{V$2E(Qk4{mVgCD!Tj zz-V1n)MVH7gteD4ddqJ>FH^7 zxJ$sHNkc(M%Kg&Z_pH>^P@g~h33>q$3>+L-jg5^mii&$PGC;EiGD1*8^!@b8R(NcB zIyR$ztGz-bG1$t=e5KW4u{S^zJoDT=I7qTiqQ}6P%@!p-Q%T9qjidSG zZ8mkl_MQ?V9!HRrl+^e^dPYW3Lqqa8W-&Y+6BCzs#Os6izB~VW^~_gmo8KJI2ZCvU zz+wzU+^z7~GWjgCZ#i5$mK)JiQwsL0?HE2jKFD~Wl0v8NGP1G=#HeF7s<+3-wyS>U zJ(ab!dq}b%5NGN@aYF;EMP-9ttxlsE3?d@ps3FYo$cX8DmFCND^LJm{+h)D`(UCEOWB>bORDb@CpsrRBbjD#4%qZ(%==kbQ(s ze$|iF2fduQaHPi_x?|Yx(TV2T8|vxtZ}X6H+b^OoK-&j2eT$7nOwY`OHQX$!t8>(t zPym74`3XFeB!j$uYt`tuEQcc+iiwGh^Wmi!I7A-Wv(J22Y|#8XDraCo{H3X>(SC{D zex;RZ7ZPpf^ArdKst9PHWoAa@t2evc9;rB6>(;E+B4=P=IQ%6iC&x<}YmNSRQhR}N zw&M~}+1}3k&ahTBmvwfB95x1qDs*VX&=$N89yw;V1pA}qio$s<>) zpaLY7JStFm8T%3{)^^LJ?Oujpm}(s@03QIr#mhpnCUj=jcNGuHzb38$dF?q;EM9BV zWY!pbEdM509XPyyUxbCPn*D(kG2!yN^=_Zg(dPWGPvII?o+g4ICG9q$va)RqK($nK* z8dOLeoPYZgCZ<+|a<4!AIRD{wN*m6{fY++8J(%Z^WbL#3NiVGKDj!PZ*OFs;#S4!e zdYTuOeOq0;b^js#Moc0}t~e}HAK(~;#Bfi5yetoCWm_})YnP3NF~tV|Y?w)@aml6q z)9slUX`5cH7>y(z4vy(iB+BzQ1&}*zB;o)(CL|fWisr}@zWGSDnT$k{c&$J|YASoN zMB#2ZY_qon44@tDjfmz$FOKtF{34&kP4C?NJPNO)e}ln1&i(Gn!{MSTzE&kXF1sG7 zPWbIcP0YB;=enzdS@$upX+*3F`DU8a!O+7&1@LOQZ9ND|$-(G@RwexX>%KW}iw~dP zd7r6{M24UT9K|hR{?X$}Ej*V~QD>*G!L%tN*RIV%jqWppI0(bm(~oGQ7+wt1?~Qai zYVpY*K6I92b43FEG`{M)+w(L-q|Y?O_X@%TjP8QC;?fsGEk82R>!U{e>YO_t7?7#P z@uz{}q}0^Z!lVt)6$c6}m9jEbBpjOD7b07@4e^uZ6^JB1&~$E24xwvCrHte0yymN* z9;pszsg#@Xw1`MVCLAsbgF)VMH*p2`22bf|(*{~i6UkYhZkT#*7Frh@X-o%kU5=dp z)tSHdF)He3Vx~Lk-(0pmZ;>u++Loo^$F<`V6XKSZmg6WKV=Iw9FEFJkr}JZjho$Kh_U)O!L%53Y?U~mGe}3 ztROVti=m@2J2SY)oyoUWl_f%mSSKnIRlnWcmbYh2o6ZI3&vf#xAp6p4*x1<2bl}j@ z(NS<8h}zhIChz(X%gWfi&0XF^oVTSn`iDIzny^n(spLbs8R;1qX6NQOPe&!SAO7&R zJoH9qE~(!!>wVA(-<38BM$ckBRAHMlikb-!6pFRle1UB*#Cno1w9{1$@+5d*rX<_H%!w}E_SK1O}JCPxv_rPL{|`J)X+qo z?LSK(3I)(s5gobJh0{^Ps?1%CQu%!M`f^LF^x*}7S?pgNzUhVwQA}GTeH4sHpnhgN zjQ8OYB9)~Bzpo%AZaS?t8GTP3X^{3;H|`f~?__QUxCPm-DnqqUeVypDT5sffBD~w$ zc*wX=@bfzG9(OK_MccbH)(`805a*b8r%wV(g7;iZ1~TQ*ndDqtI2|1wV)Z@6#b!Fc zJ}6a2cN|OUd^t;2dp@9d*ZwVams>CD_)2TGRUB|K^b(0UVEdC=;($3Af5lH>WC>Q= zgNlE$AZmFDLlNH!Er_S&PI0ihJL@t`lRn1Ia`#-!>dtc}=YSWL;9(1E$mH zMIAIa^7-4OM5csXcIe@fww(ti9@@|5?Dx*{s|Ts0W8jJZ41u7%MtjYD^jpXKqrevm z$iOi9@wDa+78dDjrOk=-Qj!c-&CoD`NxwDRK+Uf-@Bm|WNfsU2d(P3|VN#lPMA&b9 z?1=Z3EimY4&zV>RH4WWi%KRdUbveLSm9t3w0lJ{)?1Mp5fHi}W)~h$g1gRcMwGF05 z(ilrCJUZDBO565u2cL2O>G?*~QCzQC`T@s=VPs!@4{75zC2g$L6ybI#?O?6@RxFBL zitzaaIq5(T=er_JG<6)8%C z-Gc+Tp4%^9zL4{16% zz`&zNh{>OZ4~ThPpHLre+V!=`elvJQA=AxvXb-h|QxOLRA|@VuKv_y|wb%nvpYKCV zx^0hnh1*ZvY4eoZaG2(BW3l=047GaW@O#=L60&Mo2`D-CqNN4uE;#WS%;S(pnMPD$ zkuwj#BfaaXd!f^i8lrxmy@Py%7u1d}<)GDh-xEVrPtya4^F%sEMv36bOiA~wCl!by zr;pCO#Fce*zwaIh2nbd^a-)HAI2!O(fVLrorM<|Sb}~sx9}3ZrW69t}>VmN9f~C-W zZDXWpda{!qxaFXPBK}b5tREqBQ}W4oCkZMRNek_GBZ1%P04ka|UnWt$T7;~w)spDm z^hwl5AfnZzrMm3x^o>aJ1mG0Yim9$%w6CF;(e4VjW1i!^ATW^7f{ITfhg zqNo)0?!UF$rmvk{FZ8TJeYHoe|E(!{<4q=|TX@~x{D?CrW-M{u;VXaqv)-cH(@LWO zo=caHhli&a1<!K1b{=-38l__e&Q4iz=ZT;|uthT+BV6L-}7{Q^~jVoE*WrYT_Vda=^nDduB?J=Ea<=tl*W+sUKJWQaK>@f-bf0v*Fmc0M2M}q zgS}3C(}p1^wjP{cY3;`(MO*p_HEVH&gbp!=2VGf(D9b9wuG`$_#KyMl3>3StfxTlU zW9TxtpIeDrQ{XCVBHa^MJR6*h&<*GOla&8pnZ#Kz|H}n-_4BID2|| z{pN-P2|D-4c5n%+^V5Xc%pQ=;C!>z?P0a3L^m@t)$jiD=G^%EHd;G#&5=~2=enY=YctG-_lf&3E$9}gK)?|i z*t63y7}&RQd9W#8fl93b%~=}s3WTm^x{#h&N^IQ_Ts^<@I)yh{Z`apG?sNpy@=&=* zUXbfdi-@e9Qy!btr}^xkHb_LA+od}zUwkV0J+WprRWPQ~gHz;tD=uWDrUn@`#8y|w zL`4Pjw{O$bD`FsQu}4EUgoF&IouOarqAweFgY)xy4h_}l(B!*Ov#q$znh6$3NLg>Y zaN{5IqZ}WwL8@1|WK1%cof&EO+ety;pKm8OUA!ccDGSVDM53x_(V-#E5)vwz7)GM} z-eS_EZCfJSs|Zo-Uxu)8prlt^x_e!jpv6EM?u?XaFk|cfL@a@OcK1UW#!{1h=M-{Z zB9YM_52}yL*%YcH3-5+C7w@7KPgW5AHG`71eUaHm!u&!=W*0JY zP8j;@Cjk~Cuw%EFt)lqSI$8xIY8Qnny32zKGfRY!-$3JRBnGYz7UUeSUzyMliODF= zbse?re2aoNK=`LFN3Ok!K|HYj&-Z~SQK~v``|;e)?DB8J!%?45){_I>l*dQ?wIBY7 zMfGJ{P5*AQBF}Ti@Ov>73sbtw9U!``Dq*tLEq1H_phhsYQ#06OXTiunvNx)@>Y3c(<`4Lxh^&2an!9<=Ngt{9g% zXofq7Q6f0JgeA*#rTf9#@i(M~(+UKVy$YCsxx}o}#)uuwPC2q9$FJ?yqTox8a13>9QD)Qi*Ep>E8* z&;`AxuBG2QFbgKK!(

i@Fa&dvA(YtcHFlL!sA*PW`N$1XEarSZp$~4)?40a6<5z z?M=_!-@B&m_mD%>!*%g}ee(PY4VAAS_o^IC=PvFw?OE~^E~SF(*lvyd<36oOT)jXys6f#idSsSKFzxhIa)Z12t(wSmzs*%n4t;dK(=y9xTKt$sR_v}Hu_?LpjB@~a?#!^KIe^y`~CwAGxO*~27|<{872nGRehTa z%NpgdC{8iCq%K)(vC9~7)$%#L>T*`MF`nf37bMvOE8w|y($@DjmPks);KJyOO2Y;! z<&jXUAQUmpqGKVF9ykO7qicct;;-Sfv*EOKxK(2%CupR@hB$ShUcU?_BSDD;Tc zJY((ek|IRhlChD>ry@(qKB^!X_~P&nWhBADZeN7MrtJ22R#}$WE(iaZDrQ5w)!>AF z7Yb5V=E*B)-kyVNxj!s$D!ygux}05c64NqhfR9;U#~pFf-D>;*#PPHf(AeQqlYoRp z#&JQA3EYLD&^VOhFeoiAkIb`T!>3qshHd7O7f&l)f%)V2SR9EzN+ng5{ri9&%n*&*l8N?38I>2X~)gv z)h{SvNETvtXwRe+=ah|>mlZRRLoAr9?;_i4S+HAlq7{yT8ur zjb4T8i;4`=*gR8A+8M9IlwlpB1U`0ThI9FpmSVMWvA>S&BlXbI-iSPdq_f1_nlKveA4jF`9Gz&(+o7Z^L;!Fn*=chk$F<;gONXgV1Q;3^dH6(mhVM zv1+$O`t@kQM$pp%TEP}uD!krwzw!{hW)Rh77>r+6cksOnlE)&PVkIt(p6WTZRY7i3v>iQm6xXs=9i}aTkW@*6b6X-we^x4H2U&9yG}n7Nd@LDCZ|8-4qAf?4~n_ z0pQ{VLx<04By_?;N6F)LCyJ)0rza>9!C|8}U~!)2Xr7D(y2ZzjKLE-r*r+yoB2FHrz@?sy&>pQ&l zVKdKdn&6Mh77;V%>$w$q-|wo=pp&pRz=vNAp(D;PC*;u@=l!B%CoA5lTp|2F_7LVj z{$7#TA{gp`-TJg5=yrP%cxZ;@Tuw)i*n1KnCN@w=xLUMt2Cbjy**ulYgSOJ@FfJcQ}-(Z}j1pA&Rmx9<*w>MQXg! zhsxzwicqqx9+BQ~2jg}~DtX5tBp+i*uPBL=h>PCoPyl=}v})?6iwC*Q<3szA6IFCR zF9N%-f1T+mwOgFI$F@uqV*br!TatG>{%tPPc>d!oB5nZD_oB#0&~e>k_UcD!MGp^; znT**Qt&>8Yq&yy{4{B;^gTiChdad@l1dm{78ia`sW&WU;9%CronmT6d8c0Ncmw=eNax9V<*3 z0qMcvxAUpQY{__ENgatA1#XZnTSu3~tqpwMAO@h$6Pjpgg=3Km#QC(Am6f5A5dHHI zM&YMOiMFl9($?0-BczPieSraoJDkN*CbN5^XHMwJsoDmsO-a)8D$7BL_$H@0zVvTvY;W3W5u7SX)e5@sFR zPlpOmARCE@!Qpo%mS+`=us4YM&_yi9nSbmh8H^Ug=4aw3k%riP3lBgoVIHO;wr1;s zd-d_>5c7ZZ%tXo-xDSf!8z@7;`1OqVrMt!TkJP{cE{8HHFFeSkx*9+ve@aonC>aI8 z->minEh$ahh#GTk-pncc?=MGBN)`;4RaIdvr{)Vb)BS31Cu?eIvgi^r3JeUK{&t9h zO<`L5z7VhAR#;(%=*pYI`(LwF>wSDiXS`Jv-a*T%V;*vA^F38Q`iiBUW*X9CkU{vN z?n^kSNxRqZC2Ql-Rs*@0%VKcz31TD{vpnwmF^)fw657iZpOvrW>j44}-!*VlS3Xz6 z(@TakXT3+IegAN}U-htAcjg%G2$tlx=sxF4so<^i9(dkE=mYTOyeIMfk8yRY0q}^ynVI~T?0MjmoQ5fn z{@DmP>g*7t)5j*`6Qz&V&yq21BW~+0J+6M5g%JuMUCVUcypL8=nIZf4Kt&c%t%eJb z|^=93VnF^r1KE-`NJ|VZk`BX{Ysd^>H)C~ z9M#pv&nXjlMIO7&iIUD@$ZCin6YM5I+15wStMH{u z^Rm#B0z5}=%`&Pu^+jH-lWu+YLB~&mI$2p+asi(`eSCbT=S!obCu3Y`P}SelF|v@l zh_%B}KY8LPYY<6%H^cO~F!-jJJzSyol}HQjG?%KQ!}5b+&Pmc-?+F{F^Bl>S;(ezy z)+9VX)tChmU4p!ku@F(7FD?l&t!Y~HYTdZ_oFA!2{&LD(g(EV@R0aek;SAil_EV6@ zF3r(1pUIf}cp?8U7NA(88vToj%4*ukmp1LHp$~e&mz|M!?o<5LGW3Czw@1xgANaqo!B~NbJ8oi@1bX8Y!j;iGL%NBYJ-sBT}@b zz(U?^dZxTN6*Yc^eT^%izwKYUh^ zt|Shy=ty`Cu*Ha|n!yYTRr zUwyF`w!W6}x#T^gjI@{~UB^Ai|J<#R`wlNZxSp-Y{AiHI_N(7rBWQFM&Sec}0D^{l zl{_0a|0Kdt^-gs?n-js>wS(Z<+1dO=8!Icb?ZJ3F;8cl9HaywILl<815``;sH%#hz za(fknmQ1xW7+O8@kPGQNm%47t8$>u9kNG&eoqrBDcl$1yLa|M?iP{3Xn&+glq#Iehp0QSbKSKT)ha3d-~! zcOv(;B9l5RXC{RxE))A@EL}%RJ(I0KXevZ;@d%UA_N57L)XUUCJJM_X6fOmQf`*u$ zdRm&A_&-%`dbXwYe+_1V@Qc$vz$N+}uFFC+9U}QehO~`BnsEz8n!V?7!$wtqu5|bh z(=aWaLJ`vCqm?99t3rTf6_Z{+SN)+$hjN~c1t-7xC7FIHebB_cCA`rIAKP>dUu~h8 zjek}LJ7uJ$^=!Jv9eOSj5rjc2bnxk&NtVCB*Kd)($x|6dQm&u-p%0S<2mur zF|O>96{g@?{6Ix}SM7HW8szt{n&K}u$-xo)xjwR5IirCV+2vl5ODbv1ho z5-0I!{ph+OF};Vwpoi*)7Ts+VFg)l#h7gO){{wPvNb`kSkHF(n^m2ST?6hj%AU9RK zo$eU9^U${c_r#c+{bfM!(dz>^bh73Qqtc{}JanFj&pY7aKJ!RynfPk%BDCMGB4baZrjwpB~wR;BNS;7%3D zvFdpOB!f_ak$3&!e*2^0%{%krZ|Vd%h#E2;Qt;+mj6IIAypuCfe~cmxOv04qxXf0> z*RK*1jqFt1Zr`F+YY)Pjk$?7^8D=ZaR?2Es;*Bg}1t zU025xvt$NtU)L(2?H}8R(t-({!$X>eiFer7yEw3RYf>Ef{a8^Z))Gcu+ zR3?_dhj0qfkwObGBnGsF+xQ(=9bU0~Z-xG3&Gowo;n%h= zz4p>uF(#Pcu6F2ZdXOLN0E+K7UEgHOK6TYsCAJLw%a}*ocOhpknUm}hN zPTh6A>9Y1ponLv^R!g*t&@ZSSnR`nnQ=2@?9w+~4eZIBXmeo=XM~jq8R)1RkUcmTu zQ#zW6NkW@oc)$0LT27+D%QX+8VyNwGLOd6vo5Lj~!O!mfFKjsm2&qEIE-v1fE(9+G z-_AAN#>7qJ96zCJNk)8N>0m5r{*V{y&hf~08rY5gS&MJ|aa)P``4r@KQ6!ue;Lgx> z5e1b>wqmYFDYWDv+g-8tr6m{$ORN0RdhRmdeS#wXXC#bTBAsw)cGh)ftETs5`qq>u zJHHHFM$({JaP%G4a?yrM3#Y7~CIVt+((R6%<#;!GJs4$SPVq};wAoy&VnxO=*ts7` z&=^Bg3v>PMrN8W9?eAUf1;5slE#0n2BN-J)kN{S_8k&ELYd1E*5`*%W*f=&~>R966 zlwkh0Egho9pFiWu{?^N)tyc!O63J`p?r`NpVP*SzAG5xkT{wXxz8giqGw-BvImTpp zL#?G{WJF;TI#tG|}AKa7L~HO(&b_UQRtF_YCe9;?ur5_wYw*QfUri(yum95p>np*C%xn z(#^Ln__5pD+rz_4*xA`F?XA^YDg7CDeL!Xj9>UL$8Rj01^Ptt%z@?^22g` z2DRl$uJg|TEz2Z2+&ksEI`xeZe*1Up-gjo1+fH|@pl&pYh@mwEjg*c~!gW$2+I(hb z%YbbiLvIMJ2OpmVuIwMRobr>1g4QNC%@}w|0-7Hd(+^PLG`4oaJPIFTA{w|D7996F zs0KFC^71l5R18jHI5wep(1-ocg%)HS&JWo6@RX3Jepx7CEDH-us6ZJH4^L#L@Wh{} z+<|Y@0d$tF(^%Gjg?sJ$|3u|dUdY(=+J~(y&%oRb!cmr4tHny0~mup*e;eW)-dFgnxaCp70 zMlNRIq35ps!8qouP}1|vq@^FC-?%vsTaEhAz&TJ%O1w4i;*T-Wgknm_WG9`EI77lv zG|VHT7>tfo_#Uf$y#5%CIjV~+wH*5;lSPZU9wmkTel8@`){S=kolw);JW zt~39tr*bgpnhD0mvAl*L`7ZhHx0+wxb_f`ZU#J*r8xtDL4Co0mSoX{BbD$EKB2 zzL?Yx;m(jwwo>>^5Ly4O=C~6X_I)K7J~Lb|jpG&j3wBrRSWpMD+wauIJ(kn;hfCwI zp*5@#%f3H$Ki0R~;vABci6jsSw0lFK&i(5!aa0=zUN-Z>n+MvjO1lZ4YNKOg`%~4# zNY7f|&~!L#=#(s5?~*$<88XEJHd~)U62SMs;A6?51Qh^)JCN4Ax}z5+R50i+U#LO% zhvEIf9%n(oQ9lh65T|)Y#B9K#|4Xxg+knN&=Ss<&eiF9J@nvar!cMDuB(`O*MyCMp zo?3hzy|x#QKb%%-1E%S3Kul*JdOubVFEHhY4TpHqIz#4@)@K0bDwKXTl6$mU~c81}WN@~CT zgD{LDv_a;91%?{9bgF-XK(9zcFB&1oK$oHM*A|lBc_WdwhnlBir36U$e{8FML;lVr zymmea-?;b?Q6Kvt(IU#k;>Y(e0M0zFZ9QRLk^+j-J~Z(T>Z|LKUF8z$aIu-e<)wKN zCnmQWDK6^~q%Et#-}dr|^{lx^VeSFl=m1wf!J?agpO1qs>?r*9^0<1@V{I?d2oDeD zE%2`5=L6xZZaG8uf}+&M)Xlw*bmv$C?Ju)|_3!x#e(eBa#iP=X4= zjWJ@CLj_hH25f;*4Chj{gCsi`4M#Ng4MA9Eq1ls5zM1s7q^H??zLqiJ$o#@NQfMqe zGcgdP;8}!10ol?vm+QG9 z@?p`FOCE4&Gv5Dh{AlzGn}F+5XM1Sy)7!2+q3fdLh^XpXq%#UVQGO;vuQxAiz#lCw zb-$$|5HW05NwyLmP?7rzy49Vb%BFY(s;EE!IphlCTiGEb=n~;Q3%6HY1s+nd8 zS5%0ya*dg&i4ElGi7tKJ)XZW+_0H|UiN|FTT8qJP`$}=<4tld-m5&++o6;nBwrM)Da zd9me3@fVVsDj$H0cs=Rfjz6;9&6LIG8)f=pHOZ?#-?;RPI{kE7H|!nchvbK5zMkQd zL06&%V_Iew9%y7TaoJU`s!tin3*ap%SRyIe8gpaFH}9d31=0gkCKR^?QZ@$lWaj2P zm5VRp-9yqRSJ~Ow@Y#wWGCT%Fed4VXmk02c* z%-Sg^ZgkD;+EivLH*tRz2@j1RP?EuuB_;6XorovOjD9oznoAdt=>}eJ0GDqHH46V&{|C4)DG8U6&QKVQA4k-MD(}Dx zunI;lM2AZOK_k{Drmwc6r3iwVo!mw8M0u`khU-zBhQ#9($ljUG3Rr1sHwse5_Ta(c z6}mb3{vec%L`Bp2KpJx_x!n!zi+X)QkwHx|jSMGI(6Dw&#L*8K`in&>dM0u7%teDd zQz)ROtxeEv^|fgQe^Ufcbq%N9*gqwk*6%$Uk=_8w@S14Z>mF3&aY_`IMk*AEQAWB^ z#*Su&PWn4|QxeD0R_QDYH;lxM7C=}oNN^7e;<<^WVAC4<;G!m5rk)(U6VI@1UFg+n zUIXrjUA}mcP>mp`3qhRu!vB>Z5369gE_OON?hx^xm`B&46)os3ARy;gy%c|3nbFiV z^IuD`5*!uXa#^wC#q0K9+{&nqI<)0I95~4dqa9>?Z@)5-^tlwybQ410BD@2Ov; zBu$uqUyQP#xTNF_7!H>xZ!g`fK6C&H#@2}(A0OA~bnmvGFV7zndpxznEAK+4Gw)`n8%Uq@DNxHC#Zm^o=9VC+K* z6q?s<#5Gb8EDrsFWn z%((frdO3OdsHZ!bYJ`#iu{-el`u1aLGr?r$r)*?_V4iA$Al(L_~yS_xJSl z0jxSyjtqzKNh59Th{3_ZhWd4Ka&irA?E%5}ZzPL%Z0|os-!2=Dle~B!hob}Zx&s4- z*Ss>=4F*Bj(nz@hH~PoR4K~V?-7e6s24THpNK2xN6fJEbFQXnHy4fT?v%-1Kmqp+t z^usf4cTE(T9N#)25m6-DV_yVDo0n*>qri4328V>N??ZH6MR_@TRaKRZ%37||qS6~n zX*1mwMkndVf{uY9t?ExT6%!jP?HrkrlESkE#Qb|;ak|=;DHiY$oo55>3fMn55OO%Y zb_5&0jXzK;IE-Id1t&@v5fLG`?Z4LUxm0V`Z|9cL-PlNA(Bh1Bf6ec(;JU0>^YGw% z3~9lss;c73o){h(k<`&40yve8n!H>Pvwdkq^}9Kil$S?w6d>Q7%1iwIok&YtyP&8D z!OqSuQ#?p)+}cTg#0cy~BP2wssHiyT?kGTh>;*QNl5lWfy*YtIJv{gVBtCzBckBh6 zot>RtS&1Pw_J+IMn-c6Q?dqcPyE*PHl#NYFE3VpgyE`*GUIP)o0I;{{;18bZAocq;cf1FWS-L*usTNpa_7!#oo!D=C8e;je-}VqRn-J& z)6UKg$>Peb@Psw*u@@Nc&qJ5|*49>1YAU{K=b{maa&~rBste_Mk!`7oUqGO+u&{6c zSwT%LYRx%n!kQOe*}{UZL@h5NCI-FLkYig^UsE&e@X)^1kfXS?^i9Eok6JaXP~UlI zkc^Cs4X@qFN|OVfj|5a54&dBEUeo;|%&45nRoy@Eo!#Kt>0$PllDsDfIzubS zpMNx;1D9xU>5SfnB4c4eH##=P4LUU(&jAwWJ=K;YEIw343mW8 zV@D4!udS07sN9z?WR3#l!otGphK6y)#q{HO;?(;3`X4|#i-|XS)AX6+rn2iXdyu3W=`}fGkMm{|@qGM<`H5L|DCbtDLN88T3 z;rh6&EV82G;03yc<>hajf#6Ax|5<5 z=YG&1F*`e(l$sj0vty~Htxal$9DH%{wXwN*YmnUKk5z_Iq-LpVL17_0v57x7z2XIM zakD?_{08D;F;ghp`gAd7*07wEnQ7}^)z!6*>LZXn(Ylb9MnJcJXIw$AxWB(IVQfsr z0Y2s#u8$iX{Zw>%b*rGMiC?0c6Eo$%%bUq%Ce?Gz$i#$_)>K!AH+2*d zQZF#&$jC_9@82Qxii3W8O^e3T?(V$R-7mgNCte<2UXhi$w8EEKm5`oK`3(sHC%65+ zxg;a0&d@H=eJFng>P0;GY!$Yw9{Nf_HC|N)a6FWN=v7xJE z&-Fg&;5unTsXJRV;K9Sv%8G`a9qVnrWNp3(olQ-I?t7E6Z^NnM%!h4|ho_{ZJi(*q zuZ2vFj7Wa@LRMd2AE%CF+bMi~eH|z&I52VYw zmz?|e^&V@=qs=f=Jg5um_cqZY;rD5AwL(C85RaLTPW;AbvMJ&P!ovJP1K$KXc40Uy(VCzFd?5z_) z$**4p<>jL_cuoS@2;UuZM0~zVSbT;d%}fF}E4W5>D0#qGAe z-2SVBzitP=G9Dy7K6@C@C#PiA!$LV=Fy5Do1JVia+WK>1_!lX8#i2AB9s? zQi9CcaRyJs{)Ip%6a9wl;PT6{7kI{D7W(vSrZS@dS;(ZTlRFG8_dldlJZxgsQv9RE z8hI}-0cY@ufTfL1;tGrO?-v7uXGYmX z#Lm(I5XpDr8}Z2tXk=ETCv`slP%W86u0e}6wb zHAOuW{KnTTEG&dWK*~SS?o~OH$_9csUM@1a-wa_ed^;flTRB(6X9?sqVa@AxvSP)D zdhLV%u;;!zE*TIY>UFXbA>_Ub0K8n+1l^0GOh*cJ-mEwb-uZUc>1$}1`5Ot^Oj5k< zm1L?*T`UAeMJa1KZ?d?*wZTLZ)IGoM4CDP78X|YcLrzIc3%|PZI9_f*ao-*1t}d#p z7xpk*T^iE<3(?s>7orvpPL>BO^*LwUA{;Ic;r1L`l>~-^bCBkzNeW z4%3bjQBl#dvNEn+1gvzb?^oKzSG7yCvpkhOQZh0gjL~=H>Rs_+oB;<)t{*A9U!QLu z1DuVFC`U&})pc}`C2_gT`og2fd&{b-1}1ZW=fiY~8*e)Y&=NjCuS!SctOwy|8Kqp2 z1~usA6bySiLmym4$(}&Y7>h_{QhTF8>r@Z|;)eXmJs&zSpX>UgaQf|=%m?F0m%4+* z-@ev6JC@^l;!s->rIhf?Ng&R|#Kd5mrlw|cM#dg7e5DR689BMzW&|-UKmWWf-;pSL z7bqJaA3yo~_sAmo#KMviWa_{>8bc1^clh|y#)<0+YD!AHWEl^&tkAv2=H@>8CNfs7 z9MHNC*AHb|Cns4Oo6@>3_`yk7>uC6l`2_`Vu+H0Dx2LW>Y)=mT?`~K78gE;>U%!x2 z=;oQ&3fv`}&nf7znHjdJX(3;`8p! z39=DkRO+yP*3^_<`2Dg%De6lQg+q<~aNPwXhu&tq_*cxw#KmFq@qJ2O{QbXJ0KSbi z5LDLPowr0a=di`mv35J$R~Z*^`%9^64tnayj&2Qr%W9OM8eP_dXro-+LjWD5%`B;{ z?OBYWX=D_yk}Xis)+TfeQ&drrb#~_9;pI)v$`UqpiR|B$FfyXd%F0qVGK!ChiGd3X zuZfGptTX~I*oM#ULz*0j0f@(Yt|fk_ZF_O)@zK$?{t$Zlx_A4P4WFE_j(kdHn3cV} z1aiH%(B8P|ojZ=2nwrylB_$;f{4d#AS$$;d1TpjW11StjOG=VOTW6d7wR&uCPEKAP z<>mw;c3OG?8tq?KvKDhjAr8FsZ>UL5fbO?CHth>;e_r3FcmvsIwe-hwb+k%s_HeomrSX0!Uy# z9vV^o`#0LazyLElyR41PUUUu<0|OEg5>hSX1e4&u@eVvnkT6UL2?+=P9Q7(+VKdOt zA$Wl)y}*<|e*Ey)#r+ROer6e$&Hupt12)CS4Z)hi=H`VOn|}KyvI3g_vfMNI9f(R) zvIpDnWn^RsG|0mdb>MS6bt;_9M5p+SIVDVk+A(IiSGUHga0l5Z~FgN{XfP3P5+Np|3~%zd*?}t64$&t#WAKz_Wy*3 zXc#ZvQT%Uo1}o3EpaFO@uCB}k|H*Ody@}{8j==v<@&Bm)pH}~y{vWOWkLv#qcAg;} z^AIMu$O(XL^rBGni3K@cTD0yL#!ikBA5RxZ>jgr;ZeLQr-VNOuPVTA3)&nW9S8Ml? zw=b=fG79qZHyUeTF)`?s2Hlf)Pv1<$L&nFIU%Uze*8)*(+hKrUb^sYbS;q00-}_)7 z`@V~;^ezh*F9)tSGSa>eNo{Zonhv0NKJqwiMO1UjqM6DLZ zSH$objDlxO$}LDlboAtnsT+LH$w08*(JeqL%6mZ(kiH_%aQ1TD<2fvQ{OoW(#K~>? zQ*0G**od>o+v96WSs1L8H;6ATgz}7q9v#bTF@>CmGAYVyPruiP_Oi&I4tw`vjOt`! z^#i)6#wJSq1F8GpqkSB-qD7?W*-u5xG;cJ|leUl>jc5uOY~#F)*g6(@wwCVt;wV-P zX~%SUQSSk-m%1;NJqVw6(om~)w3g?&njzQU{&drI$@RwLa>IclKXu}MFNs_a{D!6> zVcnbRZxUa3TR|S21G39AY(>tr-*b?X;aln58j+S5322cfCtp2+0IH49^G(TueWoLU zbMW4j-W_P!FUWWNf~wBF?MK+A6R{3vHj4w9;DZwXRUi2>#d#*cF#?(KV8OgIoA|*c zgAHk5I|qKZk!H^)k=XNFsaHb|8<$!IQR3qoO20>JN>c`0JlUS4jBIu%_)n+czcuHs zNWr4rQ@u^#TL6+DKm;ux(31gJMKdKo%MsOUdgNTX+}3(c<~sdU3BGmvc$XE2fDf>Y zyt|_ac7fM?jfs7!Mez&#d32jI#L|*~gXnPPH~_^ka|QdpI$4*I;9zTOH@u6E9%RvL z1rWTr;s;v{S_tM~Jel6&OB|~8eGDZXUXFxz)xHx7teVX@zq^E;X$$4-R1T=z|9zq= zuiAC+7TAIz2^-B*`sSre$PmCulc6{L`zPH~w=Rhxq|uY3CJtwsbmTKM(XS=|bUO(! z*tik93Y;Nob$(f`MM3F6*yzoGd#Weu;hkPEaFfh-0%5ssHVv=mS4r?D)vTXsf6Q4q zi(sv8g%|hUKtTwcN;SxpQB?ePexJMcoH)jgT>CEyP#%ckBoU3!BMUf{nCmCrv;(?M zehk1j@;Km^SmunM%3;b5732IFz-GvSQVs696(Sc9iPT?S$z@P$4^Elq*m#D*d#@SD z|5z8!ig5BWHP5t4undT5XnqDhvYc&%9ffvLcP%5%vA>dhY(wfy{n-$*WR-dIN64Fp z|7p>@3ZcyK6p$c_U|Msl0dzg(*%8N!(q6xP1kN{kyIyw=-ac>Mb`SlT+wANge7KdO z58Cbaf#sA{{Dq%Y5nKxa!dlyIDGUHhRPE*xW3x2ziJ+$Y#kXg>%L?J?R)uU-j40XHBw+z2M_Z_YL&OR5AB0_| zJDigEZ}-}!6K$aAJ`oak1!3X~mcru;VjqPX0=n7<;6NiM>*9QHL8EX_@4R0#%DSjB zsUMvxpZc3J1taO+ow9jtE-lo+%kh5F#WEFIJqoFzqlN~W5ps|v(lKp48FbZI-zMQ$ z2@Kr_oO;-WnRea0E`wjo%1#kpU#D!Xw}e~KD=2p)M1o60QrQ?;y;w!b4@w8j!kY7q z_?KnGwITUc;_YPQMS11-Bq!yK-<)ah9gE4c1wz#5nq$U>y)YmR(`w<)nJU=yOg%F{ zij=R%{s?a$j>~lit%m&A+zSpas~Hl?Ma%@c!~E871E4j2P?%pV@Qfm;K7%1EgdNd1 zoJmFK*c)^iynXH>?=zIio=h_a8*&h6Fn8i_JUy}cCVVzO(+=-wXW(=D&(ZwBCy2Aw z+W?>M$_ui=;;OB2&0jX@6K0W}g6d5-RPlqIyQL$(rNW5hTYwm2CDp@|oc_u0sH~F; z)NWb}S5jEeAud>zA*BU|dc-RSA{JSKCxjlHoEmVL5{J<0Y4ByFyPR77CVVo3zPkF~ zhM93^w|i!8mST%*L;N62be;gU)2~u%hhoN?0-=%MxP{Ul>kpPk5F|@zq?dc^(LdY9 zcv{*P`)TO{GJ};~N@4E_6>hu?9|s(d(G}nDR>2coP?~{I%@zugGf-vYZYw&{AI%lM zHrteX5HnQ$$UnNUrfT!bDT}zap^$R%2!js0>@pY>b_7PKMFd7gb=AQ|1szk8{>&W? zv$p?yd`9{68BQRL&y(^aeuzqsPBaAv0&bc4;tGOALx;*PRj>KL;B2*eGU;lG=hau@ z_1#WKpxzn`!U^?kmoFPeSVvy(@m3Ahkg3o&(u!S#9WB_kHBEqxPWqxxF3pg(Zc#pN z1!tXL0Emh5af_;VuJxv62fqK~^U5apoQW4Jig)}i32&iBm3!ve&ySXZyzfLwO}JnS zI7TZa%QooMO}=U(+;2Y;qpbxB=G-`{uutf4POs{Rl~&q9f(!a^MB3^a<;yaOR~Ht8 z(@E4;&;k|*j*U917WcwLQi;Mu@r;kWWL@PQcR(p(;j8>)Lxr2p7_1OE>W>?Z`h`%RlOOry>1Nv}o@DrO!#bqp`BrUY$^Ixtu;g z;GfjHBV{2*Mn);Snka5W)~Rz!OlJz7z^-=n&MCl6w-9k9kYFf@y^~jzQT0~G1HpEiX;A<$me(`*448R$@l)&WNlXxhZ#%;Xs@gMzQqM=*| z83-Z9F~522*M#lw#yBgMRF>n3V9y5YcA>E~kr=t#?0iLG&B_<@(5~ddLtocZyh|O` z4Ego5zzyQn%;(`%e)NGuc!V{ZNT$Q&bFVM69u7PESXHi1+(MuFlHjBBfcx|&OKvjd2W9N&&v6u}m{TD_m*Qn7^^mUtD(ngRCr zF3+qp@ZNEd7*k=Y(h?1GKva(BrFpM5ZwbqEWDK@H7NL>2OQ07=zfe}owHDroo);6R zh&eh@U=Mfpa$^nGmhN>^>5#uqOYVwNAUa#X?Mbxy%>2ldNZ$j)<{o7L-@>i2IO}K( zU3>K7tDBK-87eFDuul}oYmdYT+McqxmJ6BYh+7bYe$hkd4!JmGPO#3Ho7{q9b_M0QH1J9^k%!H=D~;TqWe=#W!g!r>QXTMMXb>} zjr`@cLE`!@h4#qRb4eY@s(v=;fLB0kiQQmapPEX9t~{ab!u^H#mwc5mwgrTl_A4Yb z0&)_!|K|_GuY_+{zso+V+%fE>XQPwaKAika=S(K?s;@3DbktgGt1c0l!)uWGe6t&9 z(rvni&DPTP6Bzurryjdj)F*ORx}dHTxSOTG>20wGf*)?AuHBcJL{Dncnlu;uX~V=yFHx69`<1m|5M! zaR1C@bE9kWdE_T~`{zA({=`xCuMXv^a)VKQRnHy53H(5>xQpv=AxMVTz4ss89|Dt! zRlPngMRETuGc`s=n1GIq`Y|3e;jpVrf=~7M44sOihh0gp)R|qDD1)}l+2hiqfb&sn zYjiX4=RamdirEv!k1-9k2Wt^c97I()>ErvnnM)xUxeL>huBRO##4(CY?q0O~Sk-t& zE%o(0qoaj5`lLNCDmueL!pBGE{F3%Jya-(!V*E%M{mg24r558Epqpcv=~bblis~mPzYk9- zD#kkJ@bd5s>0o_Q?yq}9WRicc1t_*GM)V_m=GCt>^PCgh9RsrSX5I2zH0$*>SL_L$ zEjX)Ts>Yjx%~R?X>abxxM>EdEa_)I8neRC~H(+A~_{(-!h;lbs5sODm8*W(bs#7^F zrpo*@q@?chyYb*P*%9g&(>`#%F}5y*nU?1#a=<{J@;;{hdoBTva$wF$I8=lF!v}^# ztEz=zGn&DqgeyD~n-<{3vHMT{c3!&$4Km+_MM21R7kjn!Qdg3656zL-LZ?vd=5g~M z{UV65`(2Lp(+juBy4%84Sxd#GK0JbxZY5guw^MzB-*yeQo{Kmp)Xr&;H(Rr)(kX0% zw>S-SU=K3Yf+Q6)AKBYKFhemw07W}#2bZysyocV!IPMwnkCMCIzc*hze?I8A+q)A;}vc)QJwTSn3T@j>~NJ-fPKi9(CT;6}}JDYxA zd3wczfnPpV2ZM*>RqZOFNLqV~EAwK^<9tyiS; z&8C`3@YnXil^uSN>Yn$JS1o>#Ag~=)GV$Wx~r z8*}Y{XbR$Jm3`jL!zZs)+MKd7z2>!)(axZT-xXyf-%)UuZ~D~p+*Sh|%iVD==~r`y zl@@i5&M@)L?;gZm4Zw+|@m~$w{V+wB%H>-X(AUy8^Ag zZJsCWNU<8u#DiO6LBMXmqKR*(99okJ$zX$diIa8pdEJOyNeh0-n*~8+%y-UH`z;dX z8MK9_+)H6U_|tzj)@uF@(TVvqwL1Ih__u8c8976nfMbOL$uW3Zbxppl-bF6uR)*GpM3c$Dw6WesRXZ-Ys!?bZ!$Ch&~UrUN~`Pa{)Lkg`f0} zJK5UXlP+A7oOqcP-`)d5njn@ZL^d8dOC`JeqxSSGoir#!y88N75Anm7NcmI*ah9qsYme%YNC8wPQwCKK6*XH(APvDZ}N zh)$0p7MJ|G{u+$++zk;t(>LRsKD0f#uzLI_lp4k1A{CSIjy6|wS_Ag5Hr>ec+=^xEi z*%#5fj#{A0y$GxU)YcC*l&$iAx1tu>rzHh|ba>;UoZ?Y=q>i506uzIzv-bZn9wu95 zrhPJB`d!s7JQR$Q|Cim^pJOPqQbFDx(o5{Cr)#~kBnJ6xcrzv<`VGr@v?t&if_GfCY=I+m|W0=T|8Q5Pu_RtGYsS zQ{n$R1H=k1*!v4^rHM3=rZ`!`!5KAi7@8{eg!kpjlj<2P^$+i{2a%b&0wFoHVP626zI-$AN7W?wl!oh`| z3>Lgv9umv3esqB?d@k#m22+%8@C8P2d%BAVN)beK>iRHfL zb!+Z~gUrv}HmX(v$3Ty<@&FSp=&M4LpLB9A3A)gA(`4Y$tQnIQp{TrSi#xe$O?TqP z?1^W~M?vZ9&oTsTk$oaj&*Gf)7v@zcP#W@B_q_gQEFzyxz##r-9E7o_LCf)hHsCqI zGA-F`6=@FsMP$2};<2|tCGSy%&B+!W#pyp14UDB`{;l614j+s*9WmT~AO(0RG|&ce ztbhJPL=uA+wd^k@E;5;`sRnP-$hZCM}G;{6hpxJX+ofg$0}6xDqc z?S1e~;0wRGa+%Q%666qjH1`W2JaS>DRKk9e*P^;UasHx9dunF_>v3e@I}?uPO4a&f z$iI2@IxA8l(|LS4zHxItNCe)Sirp`Wa9=uhck7MVSZ;6jz4H)>YCfV(SP@Jr(PyFP z$7z8D34Au0ko+guScU&ukmvM-eik3+&Hz3=mHLZ-$%xi@dW=g zZK;ZzGy`x3@nR)50aQ;;f+AAuz3mjq3`T`qGm8LmvjFY z%BL5zTqN}7uAbxwa%5*U`SLaL(Bo1JX3mO(^16`n6SnG6fENi)9p+Co=OPW9b-fHU z#mf-|&R>IOjuJf@5$u9kY#4eedsg$K8x*yf-aPm!bKubHqNbRpwk8RIX@zzC2EFb; zSKV1{PT3t}{?qT!-k%e*AT`rhNwzS`r+QAJa(R2b2pq4aHvA40eJIcK%M>PS; z(Nk(L0*0%(5&Ah2XZ#aOyg8Wb^W#$vt=JyY`m?op%ce?IkA2SqAq%?2W&{X?V-J24 zlYhXcy>#7-q*)75BIzDvM-+cOxW`MoKmSNQJGjIvAmo(rdGV)$-0ST;HIO6!-c-zL zZV=l$JkqgK>f9D?=>=ei`oSL3s{r%YX*~-}Pglh3eZBYi-YbabMkhMVIGK3__*{V ztSXc2Nj8Lp>T$sYBoA3Z<8bGytT5Z7y(|#aD&9g!#lW8c<;cdcwu^HZPQVE((4mt} zjP|*A#(`MQGOs)A51VM;$gf|uzryLTx9yXNGm$DAB%a2;C3#q$L7!2Z{ldPt$MmO@ zsyF6bu+WithdIrBGM#LY=XRkF?*F1{l(e*namKDF5%UJXTYuRP}_Z2_yD{n zh-yMnLug2GHp6KhI{Fp8>URqEx!hGD3>)tJ=c@Q&Xe@5zfC!%gXr^1RO83;$XK1iE zb(Cu~5*qr8g#-_;&1zc`_b&&@NfE*=N|xWhiKcF)KyDOxm#Xb`%ZOD zqO8`RsYDoV=tOUC@)(fX!EU)YI7=u_EG%uV8&phWip_&_^fPeHLHCro{Hy%OYFw9Q zyL%l zi#Q&hMlEi#^Yd7WRVBw|IM&xgNQqwNs~5B-FC7^7O8+YsN8{aqg-&J9jHk{pu>33j zf5dq3|FHL#QE@cS|0qs^26qd=f-rqU*^*wjqe5Puu`}9;#_jFg))F`rlJ1N{t0K|2YLGqq7jS*L5_aEr#1A}5~T2XAW}gkx%mx5LGq@iGvKhl+UXOC;}jm8boG%MFFI zi)LBSGi=Nr_~hNWQ`a0Qux;;`GPo2;fSM`Oqkd@lyjeV?Epzg-1ZrM#ax zbs6#)@SC21N4{Fc#j$&D>g3T|I9y9Sq|zvFYoLOWhh`e}M`I&?;M&?+?c(#kK;Hla zdfinU85!xjjcK%1H2ShNl*Zo(2N4XbX1;-DMQhbUS1Z+(TE)k-dQ+AmV*bzP=YWyZ zw=c)4{dcb`Bieoe{$LTT4}HGdv@kd5_DtM_-?a5S?ICXAnKoK-N%Y=Y#B0DQ^n0XT zWQgN`o;-hs!C*Nez&NHn&*0Ytj93?$2+0E3j- z@l^DDe5pE3mY-YMCFhv}A#A@;^pSUr_0f|1ctY!xzCM#i|I>T+z5(8!Zp(cT?wU?< z``1VU83s0g2OT_m=DR~d#>6=ncZ-pa@NL9R&aJ2K(V_9OPiND;w^Y=3NBl_GFJmJ# zC$C^{OO@#1RR3MNP-Y}tx9pVIT#7po@Nnh z-Wn@sfWz4x^qspR^LglZ7!KA+5ZisB`1y$2ULah344la@-0U{Pb#B6fUPfe4IYikzd>+H`M|4zEiNch=#f7d54`ULPM9Z?Bsg#(&*Yy{BwI1&=@*>L}Z22LpE zYigsHle_6x7Zx1U=x}HjG`ub#v)!prR%zJ+{&+sy@qnlA`V@3!LXtMBLkkZGsl(xT z*{l~h_4r~-dC{+xPJp1)l)vpii31n8%c96PjQ3OxoPOqPKMao!*mSV)Yiq4pD4b8Y zc}qg+>?*L;g%tpQl}55@aT!m0!xRvDK*C;0h(Qbte{nX1*eVh=l?}##9KIW6M<@n; zCfS4xI~n9uAlaRfa3MpOluX0;!dfv8+Ao)@4eM~4TRIRtxr93O?0Bel_Hi5;*oC-Q zF5NcoKy+O_cs>r8R@u6lH*y$r_?B;Y&-}nGEifuJ-PH&(bai~{n9{?L_E6+r+lgiy zQ3QRr{P6I%AtNPMByur)+n4$y>)Z5n9N&7=g?;$weO}uc-<6g(#2DG%JlR4S2m*-zA8U|^Ot(SnUJ{%e`ar;#p zf9BN3S;Jz;%Qu&+bXKW=JJk!KCzkykOGe+TgSFe}zTMKw5r+~X;-TW7Q0ipRKn%$B zN1N22W=^h06IOlqnf;SL53s4jUu}@G-(+mow+pqu>v4 zkXLhh9~@70+mvIMU9DWLzFovkzXNTNGciMy3exF!j-Nz>)_*EYVH*fZ&a@A9b#M^F zZI;2!+ucnRBM1;DrQTr-s~n0+`9{yxl6Oj0xB`~xwiIMgYC}TgXbo6_bt#h6AuML{ z|5VV|XFIz}%uP`wSs%_Y9!&AnkO__U`9v$oHXGA-49stCMPAIz}kxMyQ5>Fv0(a#luNfP71U@|)N{5_k6%M8!$=PQEH zXrI*pJ_=XD>P7+Y=w)mpl3R;jRrFaN1w@?7Y8-Lycf5opoYIU{{kObWZs`q((>YpsikH4JQ zilsn!Czd9E(vE_O|vYE;%YD|t!Q2GZ`vps?N{^g$Lk9F8%o+pXjNME8_mfRqT z$t73M%W$8eDW%=ZUhGytz=U#t(#ur2FrUP`0w|VE+kCeJLgvp zWE2V!57EBi0;9#mTkh14qXdn)M(|pKiwpLU9BQm0^DVwdfK9OTRbxV-Vs*iu9Qr?6 zFJxvY=^#rc7}|r{cJz>EfIQ-GT15YzXRQIzjP(wt$J4JC=4%e~dL+6Yt>nJFkG0 z!b8%x&UkPjnW0u^F7qD*@w>5U>_yO29`)S(Yl)W%KyvtQxRh&N={r%mDM54xhZ@lo zLK2u&Yxj#d`+46c=!GB(>`-KsqbgV@ZCtO z_9N%PvOe6i1R9r?Z2~qU18_S{-$d+}wONXn5BXOw&6tKy#K~5D(R)t)PXb)Isy|E+ z)BP_%^>*!r#sGAmeslf14)Qzu{zXGf-%)e*Hq5j%8&T3ngadF3bh(;v6xqRw6KPRi z}>6|AN80|ax%eCaXs0Ndun>en+%1)!3`#D&t-9@ zna^(tXkIY8Uv3`1Z2Y3S2%*wP<1h=E?wNX)$QnPe6ySx`K=eJmYxZ@Rwb$-TQO=up z&)TP^$o%4;pMT>D)EY54>Q0-8u<%;BoK1xrXrY5!$%n|<<}9zFj+({^z)(|1=|7$) zGx6jjpW@k833@nXwSNq`BVv6E^zDv=T z<4^huo1_!1eYzD%&9Aa^@zzG{_}y87^#MaMa=~^wOT7C9w8NJCh(iA1vYO6m(kwF` z&{`Q3HhLv0UUdKqYadU((C?l4oS$!#_Dh*ADQPr0g;4nZV#7z^Ri0{s$dB0e%O>Pb zUG5{p(9OOm&U3`G&DHM~8b-r~y?r$YK=|S`7K`>8mZ4^Q z{6<}I3}$Ht1+t-k{SeLU{drjdgrbl3Ec;&RZinJ&@!Y@ey)%VP+z3|&mkq{3{Yc7l5)IoLd6P0xXv7?xM?j^gw9dx>vrw1z|Veau2!6DDb@5 z>OOwUQ3fCyxm$mQ*5b*^7*mB$vYwvtgpk1wFc}z}a6e~Q@pE4Hi9GDoEl>k2Dxmk` z-<&Jr6(Di#tF2umi5(ykvuCYA^7+ufewX9Fv`l;{%>Y3mqT!dN?qn}a`k3;7eX0N2Yx*}!U7aA9eBeyXO^s` zEY1O=F7P|fbfNs)kMRNNss{V;n34<$fjihuSIo_ypp*?&4<$Kn8Rk1Zsndm&VF?Re z31D5%eg5ZxQ(tW&sezp{E)ja7b_JM|__hLPK*0Iib_si0_e()WhN%+=Ez^B0)^u=} zwOBwNK=LbRa%=c%HHg}Ra?!^6&vRlb!A?^BXbhzKn2jSH^H>@Jq^F#(SBWhsg1n`o zaxC@e8k`e4IfTT=HZ?Dx#I|adw*&oV7bc`>ISyIZKSe+}g&4?cePZsvND;pLGE?IF zIQfX(b{$hi7H`4hr5_j;CZhyVURPm$2fbec=sWu4ZwmU*XnGSnyZ`AgQKLf;@Cxid zK4`Fi`CKR9PlG-@rMTM{G_4=fW%hMSs}HAtUVR$*%IB%e+m;?7jVTSwyXU2by= zq%Hk=Dg7LsG2oUAPSJkDIbY!Ld^64E*Ow~qm$UdZ2a5F{hb>5yo32@sWAN!4Y=74g zh1O@Q(+HOfdr+53I8KT6A_)p_$?#P$X24SWj;_S0l~hJ&ZbZO6FNos{q#3_%zfWQ! z3NEOMaL=Q}%euOz%FCOJ-f^GjKPJSlBzrOUT3w~&@Pup%>GuoH@}dQ_yp?=8#;7k} z4RI@D(gq0Ww#P-rII7-KOPF9Z5P$#`r)G?C$cj0&+RWe9vc+Nd9sY;<&Yq^{$;|lF zN|rfAu?wyImP$V6V0ghYItT}rvN}T+mG;o$t&S^0KZW+YbHJa!l)lvMDb)xfx?^tVb5X;uQ7!}|P%b;} za8vtf&|%@?fMD z_&NHm{|85rRV0*%a|@M;=8CzC99OfJQ6z}QJ>mN^`Fs?bz{T^6;VoYF6@4q{Pycs2 z^6Mc{DDq977Z84M%Liw80SZ1N7Szv@l%QjkRGub=+lwE5)>C`jo{M6V}` zr1ZEWC^u#@)-?9HV8`*Q>!2gyMfObO+JJFdZ2EggZXz;A*Y;@V*9$)e4!{%qjRZ~FBhblKmPDdLz?itv~5>64As7)tYMF1Fs&S9rm|tVT9TRXC2xx2c2XS&yWOteF*9jd$OT{29HxvhwYW?cP|nEDSXw zB4VtEh)q~081JnGBTyn%s1#@H zcN<5KZG{N3XFY;!Knul`tIYLPr)~Xs@gNBvUZRhHwz)(i=mB=@bFYZ*2?Usb=Cu%~ zfqjk^%yo&muNcDP*a?J59S@I8A@2X7NEb)#9!+gul0J?I$Dmguef+*`uelu^5iv&P zgtD(a{Ber4+WQuJRtW*|{f2O1Qkn>zuG`I>?B1M`r8<{(`R-?F>0L1JWvs^?pG?Tr zMqDGjsRKuw0IJ1V-%aex$&bV!8I7l)jkKxXpRGvOyDGvjOupP`fr-H6?_PMUu?Gs%K<8BE$6I97TVL^)!&%tE;0U^XbWVfiP@guY-Mh+gVV%&5>Z)>q|jo zr@6cazaX?_xY5cCiss*^%`uJW&BOx(y z0Pgqbv)*c<$}t3i);q2X6YsKfB@ zWZ4&q5$J;7A3sy9Dr9-Hb=7oJf!gffEPcXoG^FomoOspkXa-G7cJuTdn2w0-9GlSl z!3-1GI`EQBjTHRVy8e^NRnsGew7f0ELv6JJu&LK zM>W(q^-XxZACY1M6%5FH zIdhrN3+q*~w8!%J!PRd6))*JAXF=AnPS2Ra^&3C(??ZVGPxN&yTeaJbZXb(tm%k@- zCL+M>zOtm&j`boqlnm<2;WJ__omU@7+`;l7w#22x38vz;qLlW~Kw~BO0N-kT1f+3T!Bj;B4UJUXN97yU!+VvANZz-yp|5o}41b!PC^9fO)*4K! zdfes#7t@%<&1=`L%*551IMvtghPW$hd6`3sJl;o;#WiY!MMG*}}ZRURRZf#qWFRv-2;Hc)S-C;wm&6{M*3 zKQUY(Sl85%{&+M^uT`BA{f7`fOTK^ir-O&a0mdSzgR9Xc}Vv%1;S9{L?v(^g|!njx!E?=M^vK_ zne}sR#$n4nG(I#`XfcFin*5CPZ-FT@^`!2}9zdWFk5UFMuNiiQB?>n=Q zJ8U0&q8rxq&H{nYQw;nuz~2KtL01E`%p$5@7W1cB6w1N8@Lo$Wce}}^k~!crb0inD z3X%MJ^By0U3baSD=KXH;4&e2p91NPd-r&DL-3m3iug!7fL=l;hU5axYm^|L(HS!2)Agy@Am zqAO+$Gxr)(YcBZL@b^3MHZwRoCj|ER+EW*C2__}+RNt@(yX=t4PfSgg-~U*S+s^W^ z!V`tR+26<+Rs--SqjA8XHfbo|rwl7HwSIlvHT3Wj-O`OYXIa9Lr1NbG z^iv$Fi{%=&aFlKQ<@(Q>Y9P2(jf$CmI?P<%b#$-ieOFD?~5b@V_%M2bW{B zM14a^uH+Q}!Q({lb!IL>ja1pT7`(yAIc-a4Kh9rsfeKLSU%x}U&wSFcm6b;i%A1g% zm=xVoMrf{BEShFi$CNcg->A9Y0KfB{_u4;^T9=H>@E}b}rvZ#~D%UZ^^R3kM67o_< z9u!1$q`EJe-oAb7qKY03F8F|`mKan zocrg9k+YS^pPvv$CD*;+HNfHWN8cxh;ai+CIM2>?4| zD-J8%{D0TB+0qAt@gxXq#QN977fLP;MT>^?HKs1#J1tsy2U+Yc^3d7{2rKQh&Z{u&!N43?LKP)PI z$<2N!Y*~u(K+ut7Q5)>_BDSpY3Egq3W<;odUNKQB)yNU$ZXGQEz58i;OAu)e;@O{kR|I(s$^GWc>biCFp*VwCP~j zn6&HjuYY3!YF55PT-mKwxTe+%w0-Ykd%Zw{d!hb=h~Q%M>gtbg-k4e%nVhW*@jZ)k zH&Ir!rZH<4#oE5@kGQr)i^jH5%)CG6Nc*+yVV%Md`AydNaz6aOFV2RSzsun+G-|)ty}0g{%=6pF#;xzsm`P(j>)Ej z1$Y72X(*5Z%4et%jd?!E?54sLYBn_?VdPJeA>%~8Stm~Gy?2bqf#wW8FA0o%eKkBA z75Y}wh4N7tb6I3#fU=v<{zp2iEh%{!jlE-i>aXMLO`QHOc2jiYN==ph|K0g|D_ph_ zbPb|e@TZaF()kM45qP>dv7_k9qcpngSN$WkTU%P7*4D4rl5FwW9R;BtDVLx!8WERx zYYEqd)~*Rd@o!axY{fM25I5hdU{e7%@p(E-?R=#DMNK3{4Vk|!`8W86=grTC|9pUt ziuun6_+RS(5AuIu|10$`{{MylRY9U27BPFv-ja($$^!8E;g;_+lTNA~azL_zJC=|R z0AH`RW^*y8R#Yz0UIsWBb1haebXUBP`abe|k=32O=C0NAu~RR{MMM>?++Nn+?1pys^+lF(+d;UQ zoa%fpVJxE+`edGdX)tN=mJ*S{#w^_O+$Mfu3|lcc9RV+u__h%1O@%v zOF_u)yHDO5-ls%nG0Rz~6*sl{xm5la9kP2(&-530>^1DbpFm#Xwi1wVCWLO?Un`G` zH1T11HW({$F_MKFZ%QOcr%3m<8~BwQHq#m(d1Dp?-RJzxXFVL;VK%3Nd1G?KMCVfa?vaWvEh6SDW|`A*_~$IvVzOJ9|*`>H>Rh;^yeL2uY6_d3{N+s3{FvwqLfOxdfg zNi@*G!Nn?*ETdp53tn`!3eGMxfb;ZdS}#W zsW|Yf$Cb38n_O_G-pXv81Hj?JXK55{%j_P)-X_{^7|!BAw&p*xDh%VN9AQQ-`(oKK zSueIMeM&o7F2SUTV>)S|a{DnooYht*%?>$W(<%U`YamUJYURB16xVNl+WP25a@DN8?kr0*?c=|O|hVzsE^aket#I{!WO5Dy3F zG*|hm1~O<1Ax*O@YVGWthBfc4ICtoO%c+bgH$t9=^!A3I<;3aQckowczqR{GR>sX% zvu>h~S9@wJE3%sNmEUcZv&~_30U~Jd_s8jrVVj#T1C@Q^W0H@ObxdhhBJt_b^?TID z8uME#LDmndvI=qa$0G&^Z1B;YTHzT#=HF!fH|AD)jX@R+{y6C=jkY6~A5+(z+mJ&J zo!n~HFL%{z5gXS{naS>ZOyudRK)9rL4x%c@%XK534^Ed@o0gx4>E8FY z#PANNX&+mc!NU1B)~?l0QWYDtz!nB8#5jI-R@zUz9DbS(qTiqAF-}rt8~U;^uvwYw z$c;tomX!biY|y@Azu`T`&BUkKC6(n3wGs*K`MBe7eic!ppdJ^o8wFOcJR*A?hi`dP zFgweoyVfoV#F%lGvWw?O$H(Jia>p38$G9#>Rw+iPlh=UCe8`D^&RKn%Kj=peq2<#|~MbA9Y3+mwk zYoE;%KS?xv(G_b4boMwXetT4@KND^xF$q|V?YU$=cF8J#+;&;(;y12qHm&mwd0Hk) zIm6~NpE)6!NvWe*>fJ$L)rsxlq4u#LzfX%j@pe(wVuET;TKoG`Z8KDhh*IMr=&VY$D*iinLJF}&wN zMZJna{ykv36UR9Ww~d~mKB)rfnL{K$zw80(i_?}WLzgz~@Be0A_``Vqc=j^gocJK6 ztLLRSqJO*wtT|UXbtlj(I++sQT-fMYxlFL(vM2r8Ka^mSY$}KS!5Bj@RPwcBzhk`( z7n!_nJ&CFAo3_x~p$)${?eMvuE8ZuM*Q`J>7hcxo-PX3>BFQV4&r*Y}{rpJ|ujCEm zU48p!VaHeZ-F3$g-F3&V1}e4ZB9>9XRW>%gDITIxBZbo0tvkoukYgcGqOzIIqvJaq z66zYW=prhkXHn^xe6Il{hCd6#GZnc=45{`Z+y3i)48Y{Zl0U}r?jgRU<%K?^NveQ zw;E|Geq}Vxje&VeHUcO;HFj-x)-Grh=s8&5JTB`;il$^34l12jZD4D_Gm6JHQe{7A zbK`j`&c+Ea-*_%wg?+SKMK3+IyZ-!&$^){x8eLbG67BOG!{23dOf^}%3CxDig=M|J zIjuq5dYVM{d>FefGRASRD`4)$-UMP?z*5v`bSF@q5U|49t_2eS0d`Tdvph1${-dRa zPjN$ks{BPuM&{0sGp}7qoO?y`wEN$Zlc#a7Y%0LO;+F(NDSY<%cdczr*`jlTxO_S2 zm4WZ|AHl|NmHL4Gka2nd>7j#(#SqWfA;OVYIb4(k8zQ=^%HKX|GP*d|UpT8+0b4Yw zT0!5giupXtn?1p&$>pT#v3A-u-lY2q7q;#pqCjOvYCu(i6F}yn#htjd>k9{KihlMY zRWw9Y2weYmW*azT_#|bPQR8u0gNF81UYZ#LI%6U_sX#`W(Ot?#NIJOZGSyD?%-N!L zRG*;k#o{&j%QvB=%l5UA1B2jc!}zP+5_mrD5@Vl&~({w;&d>bP_!ww|t>}(((OraFzopB^WS22z- zDCy&zANa7bWJtl)w~aUR3n4>En(81h%A6usaIi$ZX&QK@&D}Lkjflf|&;&GQ9izXq z1*YBdJFzSBKdKyN`ToG?GGnbpA52+DLEGJMIyJ!jJ^T^sDas2zI6Y$A#4oLfo4Q0usZf9x zeXJ?0^ZCSiV^3GpC$Y4@zHp+rB!Q|Y*?(7aqWQ>PdTx0JwJ7FXyZ`hLbq2=9x{7u6 zuO#Z{LuvWr!_~I-y_M9K)l!8QG8#x|lvaW0Pm6(g6XbD9%WZjK!+*Nt+m?q$W!?o zdzNpr5gYesZ$v`dm5hB5GvEqdn|qFz?8a3Jxxgv(3R`CjgK*dQWj)ACnCU84w5@MH z+L#b{XST4}b-x+;-K+X`c<68Fn2d>ySPXiE41@p($E-H4QfNs%6{J|u<|#iP{Fbv< z40oD(ALq1)gOJBif^}^uPm%iT;9S|AF!t_Qifdzf1^MwSsDNiGPmB3|{Z`*E@$!h?`wq8~U#$3z^$m*rP&Rsm;OEidMIvz#nS=S6Gn^n{TF6gOWx zN*LX{YlmCT*}R}ZZBPFUuA`9$Q<683qZCY=XZS~~Vb5~U2mbNW{H`}w7RT_8spc^a zkb*3yL6DTa6EiAfel2DkmN6@{9O{@kkLhF^J~ed)pQjTOAr=xh@m_i{)l>|LpG_a< zIUD_rFQzlYvRPh4K42_!jU&2;Sqw{wcqeknp>4<%SZd_Nqa*h&jH&idTs%&~xlX1( zQ4y_>s^>d|&}e62VuDvFKN@2O`e~!DY6yk(oAo`pb>g=LBcXUX;LLnIG8(cU#_D^@ zT8dGD9>GH)O9s(Rc3O|S@?+MmjFSYa3$k2-G2;kAp@*mG2hKAz7iGa8+&?ipTW+?} zBVJOW7RLhlRvY$oNHOjz*mRZv3k+aiLt!$rky%CO;^44P%x2tNKDq9)By)yhC=J;C z=AbFlhMzv!jmto`Ru*^r=3;TZ9OB6YTIxOkl;i5zbiqMsBxvU0SISj0rdhnx$K!Gr z9flrNk2f(3W&%=1FD<|4dmDAk#7RNoQ;La2Q*Ss`TD3Y+K_5`tWf#Ml3Z1bpS~4lA z>jM_%&n>R1M$+R>-H;{Ub>vQyl+rlM;$tG%0kBbY8;IB4e3-~lqusMsnk3oy`=}U+ zJ7(~CHye*{2m*RvZw84vw<7YRA%~o-b-0GbxTq9xO_p5zer79z?N8QMoF}_)`PN6& zF6_=%Q%L36hvLLQ=uzkaNKzrYdi~b9y;8Z9^cg>3<^nZLJ#GFH)Om{Er}b|kX2bYf z97g_=CR64-n{GZcNq(y`^!ij}nU?q5)vcicOKbLeQyqEah1q&GzTK&oKbC*>Pc&KB zXB3bZNW1QYT2M< zmu#hD_Ph)XDeUV@=i^-?&Lfx7u4%))%HH8kUdprUaHMSF7hm}BJkki|=2DetuBvE2 z2C0##_UWVSUj=5y%~5&wY*fd|M_PM6@bl{I`*r6AOj!a63q2LV$(p8&2lY&N@ldo>y5c6M(e76efQ5M zx!sH;M6yV*oBHdp$1Z?)Mt)^;apG96=gXv$pDR5Yt?%4>r!|Yl;<&@;+-SOm2{l0l zS}r9G2NvVf`u+(uq?1Z+?5g*TYPXd+jCQ2aZNk?hGt_VWXD7*DEQl zd=%p>6OgUm!&S&UbCr}lem(QQ z-KHX{6}6tm>mX#+c|_{plAW5J7UbnJHZtaU<6y}{5uwbh_bx5t7J8#HdcwZPMb7x3 zq#kP4R+sFFHqXCkcJ!%P;{%R5jcoMjfbIBMyDdM3lg$A%TYRh-=YYHkn7!<2=fxWt z+C>CPO-=Q@&9RK}e(HHX$-Z1u1AlOEH}exJz0XzrJ1#d4RoFQn7IL~uMQ{*YZ6B4u zL%=R4)C)BLS=a){6Y@v=89_!v$fBO1;-qYV$@B~ogfl2~>u#O;|k;Nb4& zkijM8J+fD^)BZ$rjzb97IAwk0gFS=OC38{&9A*hzWwMviSKTccH6vccPeIqK-SEWS zJ6Bo%N}MTvN-xH4I6}h=LMUq7w`U>JS6EzxueR9UXZ6%xv0r1awsV)_RYrUrA*v)6 zy`#GMgx`P?l33XcgMIn@ec~RZ+?yMO#|xNE-&7_VKR{~~xp;?vcs|=H)Y?f8SefN; zJSj6i$CJY%z>fXt7Rh!$opRSn7?b=e-n<8XeKSxs!&$6UV}MCzxGmW30x(>_`E z21A;swn!<(OEd=8sHEhWd<1oWyvmPv#JV?jyCAVy0O;lk(l+$BfH#PNT$WGyl_ZUR zrM8}YtRyY89aYKVcAsbC-qZAaMfwNR+#o)bpW>Y6TdmTJ$19jlmEAS=OvvlGYAl6WCsQ9mks>d^lDIKW>>EjXfxcY zzFTi_ep^T7fFYW@a>~ z!^{VBwTVwZFOYGY}}OqHSLJ>)K^Z0Fa?@9_yFWTDVxEfE71rXGV_ z!Am8AwlvHfoLWOFMq+Bq2D6*EZ_in#VLn|t8zg1%=6==IondWj*h-s1hAExo^Mncs zKQ|kiMGR8*2f!c^uPF2NTFilhOW{wssz1BHqO4-tLqlv1)P)4?DBnCG&FNi{xCWW+ zNybPkApqTsdxq{fU)F>lGx^maijo0#-Zt0zG&O>t9piXADme#N8Yhve1 z;AVaP=tL+~@KGHW!yW#>aPLOtAKB@A*+d0c>gdFN0c$iF@E0*pOLqy}Pweska5NLZ zt|e{I&N=hk4+tO9I2pz`vA>x`6Gf%8Jz;$DC{pJU+}Ie;=qaK69NmDBo&CsovrKZE z!#M5YS|}LqIw=}gapk~@{L8C7x=6sZ&hHH}^ju zP{-(r8#DmjOTplsOt{Wo>=t@!7_D~!B<@D9v3o(aq%1>HOa0QpLtQvZAg)MRIk&lZ z#m@`)ed$8uXfZ`u-m^LeCD^Ul`{YsT&D92cD%XbQ9Yp-+gy~@2&DZVNSu{49!0jrL zr7g1VV-RA~ybK^O=w`vtQIw^=b6hpi>$=n&;OH)J0O=Qf)GI0Bs6|7)@Tda9Wd}+}o85He+Od0uytoSbDGKmk->JQIk(TvY@EcG+&tR z@jM|n8SSyyd6JW`3SO6IiyLFALG@XRsTpf3YYMjJZL`D=7*X`~EeEv&;@AgDayB|L zFVfhxbHF0r{d(WNhEJ3JfQ*+=D(|&fQ6;#(drp-CF1 ziVah0QS~AZxt;4MOm6Q!3aVuE!*f^2nhf+z=(;Yq{^Z4ezq;J|wvWDZS{7GtxvGMb zn!nFH4r(|O$o$dhfVeXX#@y5$H-_Xd;d5gV^Z=ezqw2DZ8fxJ@GKxQ6vy4y zOAh=TiVN`13>{^v(uoW5mI^ML{=MU09I*4HBx>a}WhBj|c|}>oxCgJy%Fe1c%HD98 z6j*Mf=_y;(6u69hNXik1RRwzf>G;EBo6_>Btc$_ z%U&Rl(!EM%cdjR268Llni_T8agSWy_JcNhiz`7;7=-sBf3;ZJ&jVSjq|EWZK8u-W~ zF3qSPSzlNU1r;0X`h0ixU@b))_2q3F|TU3-w$L9Nc~zzX?b_6Z-rm4($z#q~=k zFD4$}_!03yn##&IbnQ#9(_Hk4L`P=afjVf~X@1N99hVRx+mLXM-INN+3-BZ^v?(T< zRdsHuGpnekej2IOilY5NjyJ4&S^~%zcS?=xrhLmULfiV_Wmhs2nu>oC=GkaArF@ZjDIZZ!uG;5J=Bquu;tbsFjP3D@e zM-Za%ZG*RdRkjVwIRnxi`K6F8)EEm3+;z6^N|~V=UDd}N{U_V%pl&qRG&@^l`AANPv54HW=nM|lz8E4V9yGr@c4}^@HG(46 z6bmKM$4vzGw+-Jf)_o~?HB2w6ac1eRPUG+{T#wCxY&eOuta_>9!7F{ng5=ZNOUWpN>eHXj1A0lU3UpAw+0zmuQS>guTILlPxM%LHyw-Hp2Xk=ouG zvK#1=^jQs#6qnq(SvC~GeLCgSwueU4yU^7OEu*c`e48qC{b{d$&ALu6fdvyIDyneV zKa4^-?tMh|RyT>Km=eS*oh|~zTZb^s7!6M5ed0B~PxC%YG+|uh=>dy;yB}@BD`g@> z|4G8$O<>KO^3NIbfBmZr|ILvw{GV87>HiA;FPF_XB1g16v49=f1N~dobEi%#({Z^X zXBEM1cLN3+5nbKE1qE*k)LsEQvI`360`>e!{(5f{GuQKf4(D!&2O~;!0Z+lU46BQT zru;Z->Zmj)w#CiHfu|tRkX;(fe`5gx5qGwH(CS(L$%-$1W^Ky-LG(KShn0J^DzMV0 zYIlY^1_xIy|7rad;R7MwKjGT02~f52n$=d9mRqFf=jZmxb(D~S$n#HuKCW+o_wU#C zD4Yf2@bY=ie@Yc-)@ZA=-C_{^{Ia&^-aeTQF$Bcg3Dh*zlC15iGV~d(fJ&I#C)e*D z9~et@4p4z!6la%FyPaC-htbo9#K4a1nVB!$={*EPxjbYKzwr=nX9vCsEix>YkmCQn z-Eq+dL4SLi`+F}!5)mkja>%hw+4pv~z|prQ_RjmgP@e!=o#x!S$Yj%N8T!-H=kBk~ zL`Ks9U$GX#`tVw*T!Q3l)KO9!4_DhB8Y5NqUV?QuEdiA&++PB3-mV>u0*fVtl;d-C zCoSU~{cRT-|Azt8NdnG78aW3W6ZR1D*g38kUQ{0@KDNZESyGh-y!)#w1vZ{kDVOeq z*UU=K5*3!2c$5c$@26^AcXgD%hd)>v=vJ6v6;;u<T|VyO?VIl@;% zYlYZro5}0ReE5LCNbm(D^aE*0c`uS)U)a5?kq{~DTjuN}Px4uiGd_;hIiCCYtXQ*yO-{;B6b>=vV@UYGwslN+GjOr!@>b$jGGk>-1;oBn#VOx@^B~$M z(4a&&mRO(v*<5$?)g3B`D`bD?UAh5d&bhkT&4}G4+ygipEq~ZtA(a2mJ?|Krx9N1? zEAe_c!xB4lDDwZV^=U<0ldY|>njG+)oj`J{QmG^FP)p^bX`wi(fcI8$;Yp+EJSSUElreu3v4M4f(&w zd8??nx@~I|cPCh|;7K58a82-F0fK9Acc-XAf(3`*QUMYO?(XgoTncx0DWITkBj5h^ z{?Fd${4eL_wtHX3YHiM1)z%!-#^}Az$tyBEu}vo=Us~2lncuRg1lS$ycB9UmbxJ-+ z{UfmO_8i!c?m0F`of7p92yo#~^t}lXg-6?`Ea~+2B<5S&8H|i;&RHTts&MuFeo#o- zr2U_JE^2h@_fxw!<>leQTz&uA&FMazs~jYXZhYw+SW`VaLtI7rE?)a2E~7u8xiM%1 zKHRl5I?wut=E08fqsK3`c`dUn-)U znw_-#pT(?SbUUln^xfYJ;;{9f1rd?GCgaZnNA#C)#GA!CrcmOKishU3pNhY0R89W6 z7Uu7z@BcH^-dZfAQBhI#LreS*#hnJnmLN{Qg)P>k$@%%)=)z*w7A>dbrj=>Lsy1tJ zVUJ@kg_#HC$b#E**$#u{@TTU#(NR`U`<5p{`RC8A=vt+h&pHi`Ev&5MaNp3=pYu6^ zYI!!dD0T(R!LS!Joox-_d41pS`=wt~%S;z8PKLZEO!IKqUYvJzysyM@b2r_3%K^y8 zh@7dj6IMQQiA^Y*BO)YJHHjl(bpo7gG1M#fy!vJM!N?LjG5V-bqahzPsKZnFa}FJj zx3}@4cv(+(LeqK1mcR{bd7dpzd$A1xAt4j#Jg3R1ClMy9y;Z0~1BNtpm zyF_9?e_lK3JPAn7`Q3Pj#i;Awv*{=#r?<6v1N?4niXj6=>XaZs^(_)O{$Mm3B1SH{ zi0Fk#xp}hQA1=){no}IqK00E`Iq^+b>&?%x#EK<8U%@g&U%u!LD`Em9eeIc(ZGDYv z>U^_w(ZoVqvJfVS_s`{&8C84iuHb#K?@T%~>p>Yy=FFD+H|}1p3E7*OL-5 z{G>8%<>|rKg@y5mZf{XqVy+R73MQu2?(8Ta>iohS?w*s66A|C2yn>heg#8`9qx2C& zyc^B7xmVbguN@)nVHL4t(LWqb4b-t~oyD7Clt9TgAX5g++<=cA4xM8wR{76uS~cEj zyq0TfYT6T6>i8Lv%)A{TMPCdw<3-1#TBX~O{PLcTPpmO6QFioqO;tp5Go6rfjk*4^ z;zB70UT9$uCvXF*7UHfT%HfsQ?Tyk{h)(DHGL>8Ry5gWXvp~bQiI0eoa9}1-v|037 zwCPZ419olA&zKBsg)>pg?pgJilYWG83x3&_>~L#c<@<_D_8w2>u(Ev870=;ks9KW~ zBHr|lj&1Kd+OYsD;Vj7)E>t_yB-OS012zVe9=dn*U+|jFj*fa})dzc8fHLVz)mPC< z%4YuUrcuyQpR=n^m5u`l3@l4H!7RdIN=0B39TSFD8p_rz-BHIqGpIa2^<%%8j&C2i z=;8J{@$x3$wf0%0R+a0xSxwu4!niY0s?sP(k9GOR>g`*7=AJ7qz5cO{bwg zF7fUUcCpY42O&rPjgcdtU98&{g|ke|jf45 zw3URV4TKsM8ow3RyY~JH?YX}1>6O6QRq@tT9$u+fYeqK4#n*y#`(Fhn^OYuSq}Z)l zrk^xPE%l=t8r8XcVRK?43Saw&s@4w#Wi&Lbd(F9?N9p7C%yAkbdiUY7rrcqnbp&yJ*HHwu?CHmrxM zrnGBTJ*;epiiHmqgUVwAoZX+=9d47#~u*RLT?GsdQqHs0Po&8gR`wRSKc zShizrLuK4Mn@*kWi!apf8|0M|ui!s(`B-*D& zDf-cERU|5^G`;a!@sNIp*PguXRveGn6_JAr{G2GcEmKrha=5{-x-?xeEe$i4OXq2S zhKtY5k7?US+Ona)Kf*)KSin=e1v zWW7GDAv?R8D^Jg7+++N;sD@mRz;iuFtc|mJqf8r=+C3o2^ix}Y^(Zqk9SA2N?bXrW zPp!#hi#+{!^vamfIRBRgZ8s7Qj+waI=mvMd5$bpLjdP0ykF!{?*B+^{ugi*i)`pr2 zS7NDI?d;NY)HFff=4$FIdk-YZ!`Bafk~+0d{Y7_SrKJJa>`N!`O+~t7bX;f^Sinx_*ddMQnO{i;0FBoMIU?G}u2~RL>ux zO%kyAstxPcu#Lea#S=C3F&;>FA%}hl0bkG4-*3he<_g}n+lgcyGUDN)b5gY1DbQ+~ zusRXpp)nIxosA=&Q`adY6VPCqeVQ?LB2Pec!5W$v+IJ$PIv-nzUK;PI*CKaLc1-zo zu(qbUJ}%c=K|n=;L^Ox>b#iFU3320#LShnRWAww`+C?tBt|-~fWm!TKvv*!yDA$;gYjL%y$y zm!5;nl>D-~n2Gg3NYZo~ zIQher8pJ}u@FfMlC0JXVdJ>;Cy`(v{)*xzfFnA`}i8in#_Je?jqz!@jQ<%Pycb;}M zcg}|Mlc?NoK~74FQQg&x^;>wfy1GhQ(kB+{ZP{mz8JGNGGjES>!q2!kIdcA!6UuiZ zvdi)Tj1pvzP|lxW*!3dZ_6fRlj;^!KxDWC5Zb%?S=0UUqos;^wa^v=rKUYhzO-n}h zs)Ls$aFm$S2d)SXHXEx6|1KNP{jujWJ_;w`q7QY1`Lb@3)*+^XSd zooM{@^2{#H-jLybj5a9AzPh^ctTr&&1PkwQyZ(ajK7Jgh;>^28zMxO?ev7#@IA_k@ zBBboP>Of8jt;v1#5<*Hn);MBW44EHH{|wPMw9V~o%V=d;qS_B7+5D_M)5}^ju*11N zj-e$hlPAdDUB1vcU-vY&Ge5A!hf8@l0tdO6%07X;N}n~ou}ph4+#PUQv{bxUjWe9k zg)Y;(QK#(S_Z;FaAdAu|Y*oChI5e8*Ml~Nh-k5iSLC%G+<{j-jz(pCZR5h@n+RwGL ztMFsSebOy%s`1VC@L*%VNw+b#(;i^DI?Wd(cMPSWmu-yaxWeI&X#Wt%?!%>*5d(H9d|3pcWis`o$U3&ho$v{6Dla8SV9Gq>@2B@NjI9;yX`zxzd7dA)x|iy zqeD${CKViX(3_Ea>!-s{YR^qsWqUAx-l{X0?hT>na1W2CdHEUR&f}xA*Q=u>hAFK# z%zbu@`7dLF?(di6Q&GqhaetJxWvE7+?-8CI6iU7kw_LaQTI+!2YvBAPh_Ms z{>~9wTdd(+&o_QMhG>f(t;8CR>dRv|t4O+|w~LRH?^j!69rgf$Q6x{Vl}plxh=0q< zhRVJdI80!<`okH;>E$6hZSI(V(hO~hm165K{_(n`*zXfzwo6i$qf55)#pbm>Rp^caXq?HKcWoK=VbPg+gLq@9A$0b#z|$C*CH@$4iig&Y|`PlO)GA{-|&s zX(QF7M{^V!J-<8qL&sSYX%)6kl6A6W)jRs$)o*N2T>)hiz;=EedhW+$zQ7V9-ynP( z0_we{f>P#C3u*sTIr32ZkS0By?(jbKuWfDM{!M`zT>21A9w~2G(#y~=W zE5zm6?!XM)33U!Jqm@(HJmA%Z`-t(-e}keAH#2OzsoDkS-H9c$6jUd~&553%I|^#b>jf6^(@4^``-p;0^1&o-O0YpY0yVK5rTTn|B+Jo|4GW6nxL&xK*2W_@2X$g!z(HP9?q( z-_!Y0=CBsW#5e;>W&9EnFP$N>S

|$GLd&b9iImSMhC#b0Pli9-2^qO2LM5CYbJYUm!8sY{^H{r#vL| zso2sc4a+g(+J*lto0Xz&+}Ie$)0k%L_Jw?+RQL=J}Ly8fg8LoetJ zy=EkLOpltimz6P32bbwR+_uI9aQe5|x^ATSGeeh}y##J`L_c0f5+Cm2^VU&w{x-;B zbHyxw@FyLUyfbfp( zBx|jS@7TA0d&3MaNc#n!ilE`P&h-*Op+bu0n$+u(l9I}6Jh{6%nAO0gci}fUH_WN2 zAu)Dxs*~ zO%3TLw&cD8x*A`@{y;Wyhd4e>d&2XhV#M{M$Mg`(4^mQ6X2)-zJw2tQwvR*$k^VX= z^bcI;!#}R=_y?l>@37DRi>q(G-D{w_)WIfiK;J(+oqO_5zkN`7Od`VezDLV@0xRhe^9eG@;xgLODpEQF~@)5UCjzxKsLvPG4OrwKymU< z;lBWXZpVLfz`r5>|L&iERL6OKDSnhNG&N-~UE>l`c3&i>Y-_U?uh5Rnj}a+J0+8LF zCxm)kfzd2skNEKRfn@^-nVny03N$17O4EAO+ya7(@s!vT zUp)2l%L;;q&`rPLcK+5Z;jTLT#c_AA9n{`Mtd#G`3#uk`CkXfmMt%x285addg}^7s zw<%8BZZ~MWi`t*8{M{pabcS~K`m4$6;I;Sew7=ss@}Ms)JyQqd{Xn>LRn9mv>HY+( zos~fTok9zvn;As%(lh|K5m(Rr7Tkwi^6`SdBG}^f ztkR|j&?2;RT;8@i+q=!_`Kr!k5l8S~;5=BLR_v3NS{hx74^<01UR+HSIc9&n9r$!XLRd0N+0-YGzeZFP*!)N@&t}-gx13V z9LY`aFC0zileM44vY_uN!G3<&$-=a&E}pzB$>8;}x~bpdY2MU-hIK-C@INzPedapCkDuF7Q~Kc` zPejhgE|7BX@QUF!F}}ZjPlN>5CbjWHVT@Tb^%?EGJgk*C5lW{NhxXskq2Kn-f_y_9m8{#g0D#J+s62cY zQ{G2c1Vi{)O#i)?kNzh7(K6sMvexv0R-`>I0W=cN0V5yM66;MMEq6VDkisJ;+!H*6OfabpmNAu=Ox0&)ae9DscTEARu}luGKV>A#v(QZ|p!o~M+~EGF!I z&e#U+;aFbi4zv=PcCE+*IVg&*5hxkA6&ZF@<^)ZJM>E(HD^hS=q*Gf$uYbY?&+a$$_Fq2G_S8Att1Q1r@@Goq<@ZZy zzwE%b&_{ndY`VEF;&YcfK#ORw8kC94%>#8LixDLzon(}b4 zkdp^?bC!l6w@W5~WBQzREwt;n8jf-GGCuiW<)?O*{)*E}!9g?O?>RWAkn+I3-8HmZ z`Qms^?DZhu+PsI^+PdQk*l1^PUC2|ir8Suhnau-m`{kHEgdujJ;{xu}Cm%#{gPv?EFa@Gk!Pb}%~NfBA)zu8G}T9^(G|@iSEn8#so~ut z30+z2s2hx(3{|C&RL5+`IkZnoFsXudER7FrX1cfD_yo{1N^7ve<8hn02z+OQlQWJH zgd%pw3#{9A?FPehQjkOvUSJX zgLC6i)y$zxJ3uyH`j-kSQb}=#J!WFQVAWfg67>-f0&eR!5mb2Gr6Ur5ghK&iD)m?* zVp!u5mONST4vy*a8-RWT_t_tFbH5BN)d8sNt^6mrNfW2mc+hseTwck_FS7_7_-8QWGw1Q@ z{!8=GftJC6bWv&+I%@TQSC}kT;tG}u0nkJCmaD!cbr~c65yt2m6FP0^M3PA04m3SK zuf5!u^=fgM9$xVT50$$b4hL_otPio+-Y&>P&t+en*t)|cmGoqS!dsz=qF{LEXFRQ} ztpf?f-&gXSC!zqY!nFvam7$%sOU6*vv2)(ls`jKb{LYE8*D_DnT9p1Bh`I1T#S-l& zp!2%phNI^Q8S22cyDom{AX(ihZ1&pZ0qR(^_Vfnzj;#0{AwmP~Q2O;fCB7pWNwUQv z$Ty}?V|nB~eBaV>>ov{sEoO2+8KRN(@fHvM_a9HX0Y>bbBCO4&;cH=bcF-9_1YV=7 zoTd0U5|)7Pe?*B}Nc9w$F_F^iKrgj);MUq@X;1rkDi3b(AHw|KMF`&*u)&l&4 zwEMU4}bhyMS)dXetNQfSRKOMg14A0knD!>bYa z6vw(Zn*PPY{twaU{}9ao?d1P;bu%1)Y17AOWA+hn18Jn9<$ULG)3xan+J6O1z5#4X zHp6-F+ZYC!pN5NvGhDC1B?43I_Z?k1AL3SlO4<9%7j0G;=XblJQ<(6V+G5D?$I8Io zJ4~;W&%)$jl&}6^L{Wl;zOgzOnYjGo(h(PEkCS}%thXq({`7NLON`W|k|8>CGmba5kWOIR+Vy z+Ro9ogRD||?es@7;bL;uJ{p?+D@ywruyOkZV^${gn5>7B>S&Q8E5au3b<}U2bONda3+hlX2tOumQ~^o zTHcp>GTu7wzo^UMrs;fxK-vz!7+&fv3$O&=qM+53fzSwZ3|L>CotiPD?&0{dI` zhUG+&(}HxNqCEV#1HY&YQ5GmdtnY%D`8~d=pI4d?x(noJ(+4ibyW}8t*Ee4S358GL zCcVz-I^AB5$!8)VHn}HX`6T;{e9TcnW!QgF^n_vde)!vY$reA{>v)Stv^h@y#I||% ze7w5WdrX!l<%}W%T@1NlxsMsrB#6&&N*UUkYNyo|ir{r3d(nT@+FM53=za)sTY2MV z_mmGcFD+gPlhSIRzeIrV++xSp3k`j9Brr%2`?lqCIbs%i?n%3v+|$*3rX{g^H4qI^ z=0AA@%$k)vjcAd;@d8jse4Ra$7)LcMTNL3c-tPis^xd~YW4ispKwK}9mv_`ItxlMe z{Gbo(5y3DRsEY##6)N*Kv{c=ZZ5YFV8{Yc(SaLj|7Nbd~q5eLkI(!QdTWfcs)R1!xu-@F*xmx zZs6_4iVP^vR3)Z2ElKiCMUXnPc^aBPdd`WbUhGg)Hq$}`mE zfde{ggSNFpwfsT1)R80be#eaCO|lBJ_wUv%UB4a#M=Cvy{JPvGU|(nKbG2_?HMV%J zEW(*LWc&m=M(ChaY|w@8K=jGUg16(@h1BD|(XF&Sdvg|cg}y-S5C~91961E7k=0UW z+p`3DM8Z*kjgN%TbbY8iJ8VoJc-Ld!9(vo}r9_O02ctPvk4FXqIj#~Oge=du!J@sC zb76oZK`-{2H}H6c;(dH5(`kVD*dq6sU$pqTc-y%sG*sW1hfz%OaL65jT>K(9d`}T{ z&Y3cFJFo@XO7>NU+_dNeHE*qnyp8(Vs)1)WcZb_{&#odhbwW&^oy%UaPR~-%Joz|3a>P5v(lV7MK}Q`>s92>sFLUUL`>FluWuinCEv^S<70%~0ZS5pF1j9z-5BEm;~58x$5Cmii&hK8 zDp1xppuhG-`l@E@E}Byi5t>1sukvk1uZEMNonZ;CpUu$LTz>f$;}v=!(K<=vFgCxf zu_f8t<{M-3f|FqYuBQ6|F@65x$7`2PS@kugQvsF}=hjza3A5(RXMB_?hJ6aYzlE zTvi}bxRmcMq0Une6&ou;yGonrqmAdjwo$y(?Z&11p6K~DuZ50H4aLJ)Wh_qf{oJSC zDB4UHG`)6uQ(k79+3#Ri)%5^7p9G+MTJc7ITipto&`^a05bhXwnEGwDX`wAVYAl}F&m(0T*p3@fY``Dvw<`XtkjjauDrgOiEL8ch8fsdId4 z;O}=QuX0LvkGXcUFQnob_gp*%cC@GLq7@g8{dV5_+#J=LbkvA5vfp^>a}{ds_^kxR zedcR{H$KoYSzgb@{ ztnULNMt5J0&W!EmclP9qHP9v^fw1G}P!^>V@_J@Z;RjcKDF<|o3q;($gL-ZtfmV&H z(X?R@gBIp~%s0cSi{r7r7~YyLf4(6kzqPMbrJ5OdW=FwcMH{~Ewh&S0EV+0_3bs*~ z{g#q1a943o|5Xm)yEczbX-DJLg;Y+c?U%Z%oU@YAwvp1?vHmafwvQ`i&eJ+WcR;%6 z#|HCmwiI&AXS_;vZ==ME60k3`>dKe!%uSK?3EdwdB9^{W89xXyGNxBrkaopSI<|{= zcYMF^UrTik^47)Jwv-|6`rSveC#%ELe@=ZvKJ(;uItqeG3m;q_VRt0+AuvGO#7k{k zr`$~R=zgcqK_Im~HAasv>Us_>^&RUm@DW)F<4ZD(3=*fi5gT9GJar9{w@9i%kkOik z>yyV?-_MQcqr~#@j6u@AnM;WYg0&@hP`$@>GIPdlF}Za#Ihgz=`axg=6DL%biuFrl z@tMJaK=NRkpGJnL)0ZCMR7e`>IbI!6yN_^o~(Eu;|{yF@47)9v|yQ5=_V_ zCK%U^z!)9)C3%NP?9pA>Az~5Jdi985*OR90$94V)ym!C{1GW?+2ODrAfysce;RuPe@a9SYLjhS3}W1lj-sl&)0oP|z3Q+0{=0Te0u6f^8^}-4@^b7ixJRA&2?yg)&CdtHsn9t*Ufs zj(^l$*sjl*BW_^OS`Tca!+J(9JFLOOEeZL)rGi3l* zD8&0c<4+GA)xD9#8eM5=w~`36)Me(I6W%%)N%U~kPDnUD*GyK0d&WWhX^b8yZ322L z;5}d(){R^sXmZ3pOK;@H@(jrJHWHB4t2HQa*2O)Tl`6Y5cm_BH(A2aS2-gQ; z3MivbK_^*b6&t@f?a-sd**Ch%l2osA2{T|lhyC&dyZgVh-rHERs9paC4ys({MT7F4 zwJB(OlFtyubBYO;Rp5NEzHe1op8gv=$KUNJr=ph?HWbVOmmA!jewwP+@I)8xgNx+t z;``aMk4XkfN>j5|u+J3k9Er}Ct3?3lZi)W*=;j_iE$GY5&AW#c`)n--ihP%$m#i!A zjyB@%JS#_EW3tf)xv!$b=Le7xhk*dAe^qsyMI zJhZ`J7FnroDdTK)CxPIIei#Tbc9NgGGm5lpOAogh{PxQ3aZA?_cxxWmVO%tN-X!4! z(^I;?kq$HxTr3uYwQ{oiJGCmawoF)0_bX@`X-fVp<7pT5WcOSKc z67;TcXo$mVC9vbgN$WlPn8=0VMeO%d9KZd6jX52i(DV@25}yOkjsq74`)4q$?5Og6 zPLv=O{-X;b&CEExN-!|(%R#B>u>%Gg*h<%ueNw1kH@=HN@nzT)lNWWz>f{#>Pay2T z{|l2lN)Y~A(GxDU(MAFFj~`cmv&zR`4q%b0(|Bc3f>Pz6_@S7gvjodm?2=jXn+h>C zmxVr8`#sy9Mc%Nv9n4TiI#IuNR1K$rZ|KnNSj;b71z);`FyS^leWYmKLN}ZUj)}8p zpVY9#n{E}pbm0@@;oE>wu{ICffm^UFCebGLVl`dfp$ZWo=pjE~#k9-V7|-bjk+y~mqP=R-9e zrF&?v)(+95cqnG*&G}}Fv>lK$xGVeBmo7<+P*7i2oq5%MM_IKEB!M=A(c|-nNx_B~ z`&7r%?VxuqD}u5w%7SXZaQY%g`0KS(#?CdzD%J~MJLRV6r7db&_^TLuv-Sn4PRKGd z)g#y13h75tj{e2Nq^Kn?dyWtOT&-aF_#Z0o%U_m-cp4e@*MXa9L+sG=x^`+WPWexA zc2k)?fh&${PFj_HMrr*su#bw-&O4q+&?e>rq|kn?QiNxQoUjg$VVt` zN!r)Dmfr6h^ESJ86ufIXKDGuhP7^>rftN=VXh8L3i_0v4PSOS&Vkjq1*5@E^5Kp6`RJab7(?1WN`0sU(-ia@Q?;P-tAbWTCR#hB36A674 z!sbgiN2=`%5^Vg6aITMoEea>769IIKDUSlQ&G*|nl%fM&D(v>UEJ>;{c9s$xbFksb zBTcSa{x4lIt}A132{WrX2EcKgUpsp9y~VL$49J_h`C-_U%@4*-P9mthSefj&0-~hU zdSfT(+PnB3Ab?{UNgB_%M@+!B47BBUilqJpwwF#- zdX1|X&*+B~ggQ-Iy${G-qR4IhnLOF#35d~f(j4XM9Mia{N)CF48l+DPqsLUx-FBbS zpf$ZXkXG6l(|cS7-jK7zP>Z!iT}L0C7gm6qg8kO|yiY=cf8?R2lHm|3dS$WrR8G#a zUIVh+MwwVtXp5=_k3@~->aLGQ0+E%QKu zg*dLx3LY&vUWF)3MP7t2iGgPkFwWctMYv_ex?s6wguWCsmve@3raFF=R~w;42aPq3 z2@b6RPS9GO870vZp{q^j=4U%n?gnOIlh#}AoL&<5pO2%W+BO#qStzQSiv_EjHI{D= z9}#C|1|hWvVlsZEEe#(kT7#bEwP86q&pw(DB4xz#R+8$QaTC+=CLFYN@!1DFJ-af) zZ&}j~Z@lW7y%C1igrFw8&19fPVLr4xTBfVpSD(ntk?T0Er=rX@Ta~CU^!&zPZ{y-F z$RLwj?%yWTw?@dUUF1f*k(mME`<{5j_YHK(E2<|;>6GY~Ijbd}dNr!oy*U?ibw)RG zi9Y87hF@LjZ%Vor`I%kph5{z)@OeNJM@MCVb8;3_bf@jaSXz5P)uesob9dxfB+8Z6 zOjU5xPo`f44WsLkSn~01f_rx#f6aMiHQ&f&=|G=N4rU`j*Y(P83p9#vx9#1`1CQq& zXn4QWw6U$@(z~*#RpRsPFU_VW;|qGHAj-cZ+Bx3ldO;~Ch~wC5INHT8+gg%&HKK#V z{)XQ9*g=!GBq7#7R)$k<>3BzYSMO$9*LVQJ;vIPbZ;4t~88Nq?c&mmdTl+vBTf0xr ze-lUt7QW#hR4IP_g51uJb(yZB#rt+i&X4^%N|Ltq!NL$^!Jxhzpl@vEA(*@DmnVCD z1i0LE7FnrmG(8F`O2M{65rfZBUapf@S^TVPIzHlG=^rrdOF5U}bwprrrK>T<3@SV7 zS?F?ab}ijl5zeAr0WqkELK%8#qwQ>UsR7oTM0_fg^F(hwl8JFNHF;RnlC}R)-o741?vR3M5Y=O1krcYXjwSwZ#fu3Gi1^D8M7t)b)bcYXJ zT`(Kd=cpYo6awEd`-xSKaK*p`CMqIndzRxJ&mmFPX6Qj8ggED$ol>2*b>qIne$)=| zntR7DHQ3(52nWO(^v6(2kT{f9~hNY&@epm`Q5^1+3)AhI#hW7|RI2 z;zBw9YU@<7mq$cQbh_>Zd_3dcgx7`S%M~?57|kzK8Kbmli7}g-T^AEM1SZd#N8ffPU+A zK=Ugibh)Jf@w{$UVvGGdX(`pnilWkHnXhct`TYt!t1%=-;7*n3A?EmnqJR6Tp1ewU z&i;d%;fjvp!lj!@8lNh<-Ezv%rn9_0k5kH@tmf{=YYvWFG8W8e@=1?1-mZEJU4>B4 zEa=G6OOor=q$8>c<_RdX#2aL`n+Z}-uU*}Zb7>VJEPXCavX38kzh6WZsbV_U-qDTb zaj~3@U{-X;)~6OZDRTLuT!4i%1(U@JgfLb8t<5g(CU=+MW86`!~? z$Y<4SUNuA?bhhJ2{*-q9h&W7}v$9c{Qge;oqUC+KDa?ZlyKC}4FqG1_7i@#TLeoCC zuT}^1wzLd*V!zBG{FEIOxO)t}JTZ{{v?BGV>o4V#Q^-{~o6EKWT-aKUk9;NFrEhMdjOP~1a<3B5EWN~h|@%ogFFM+`+;}LZp>24eAA|97Xe4{UPMY7 zsq+K`_ub#OkX%tj;XP2oag@+yFZsY=$?PZZtHj|4Gzal^Pt(u|T)HOPH9bmXG0MkZzB(J1 zrLB12Z~IIT8=s_T*$8`a^0yYCEqy%UAB#lBKc4YifBe>HJX#U^1Do@OgMZX_8t3Bm zl&BYb#7CwrCWKhQDU+9ESZ|oBs{<uJF%0My0kPj2(f}+ zvK}nc?1>c7eM|cwql`BudP7xHQ`0Ks)4o>u+6wAcYyN`3_?^c!%XSS339+P%GM?|vG1I-s zJ~cJ9lAD{`1spvGIcs&_d3+qv#6%ZtE4uc63_f&QwPjK>ADkq56267-b)15V6ga7|I`prO&A*DRvzUg>ApAB7H;8#1vd98QMSE|D8tQIrYfh zRfz)R6B-9QFgktio%5mo9gE~S#ZXnPlTQxN`L~S*M#Q7o$ zB`#qMLS50U0zcuE2={{-)d|kDO#==!ZXv0akeB-rT0Cx=%vCJXSp8VRRtO*bi;F23 znQNcv;}09^NOxwjt^djs_{^2c8p2&2Q8K66au6_IWE-}eDE(HpATnzxWBOI($+|)R z_Rc%|(GN1}op^Z(NxX@v;cxD#si{YXQ+W_yzJ6`8(Hq5P)Y`1jhy4BfcmKmfyO|O_ zSw%&~iw%wTX6aBlfi!7k#McR=RqPCLleUMbn!`&f@cqOoA|?9?6HSY5=etMLvBYqsG|^q~~*4kK@iQ%UFdp08#XzYck3Ii76u)pV!AN{Q%I{*Ct|z+xm#`oo9KI9y6T z0t}QCL04uC4UJ*Fa{nJX^O1(&9vVF7-8`I`v52=H29{ey>P%3lB`jW|w3pN^^+M;X z^p0*a1YPU*^3qfMW;PItt|fZX5U!q6OFX!hJ|Wp_*fh6?Fh*>shtGA?$4^rWULhI#xSvDGwG>L zBCgM!)r@iD%rU_sZS3!$PurDHbVcW5X1!Y0C@` z7L5HRM#gf zDw>*_>F0c<`trTSXz|P?p?CD#W6Ox1i-;1^xX;SKT_MGLQcTJ}bF6r=&ts6{qThd( zM>XRxeh6tG;Ekd1)#7nHV|UPNt5@(uQg)c@Kg__f2;Sy$R1GWC%Rk$Xwo9o-@b3{6 zlrVcaH(zNqr>r~eW3gR~cBcki1k99V44$K^3p;ix?`J-AdK~{U91|5}4G{Rs_r}Dp z7q7Q0W!8Ega+5@Oziy(r#fC)6nQKOULCW!2chseE!u?>^D}$60BX$b#Y}D%*+k26; zE^eMx6M5Qf)TllBh(eJXjMAR=ld?ZW_x(gQuLLS3H(6}eXFP0+nEXPTb21K-u1Z3w zC~f>_l~yx$SVWrKuXv*mcd+V%sE!A8aJxT3JLxNV5_A%|s%%*tB$0WZYjV?Da;Xb; zFa9?9kXOQBZG|KM>ecXYzwuI|yIHZW`zU>g3_7`Uw7GRCxBP%N(tbn=kMBFzd1`8W z|H0hQf#I(feb-W7`A5txMxTG!9$VkdKD$(ri>#LK#mI@Dk$mvj9?R-kF1Ax6;y6EO z4sxxo@`)i&>(48m9>$-J7viNX#KIg&AIVPV<9y3rI612+`{Ba}?bLY>D|NRQX&UoI zxVT

=O%T3fA7J-hgY-I>g-c(EtGx*~q*?JMma4X${dDbVEz%`ebIOfQ6+Q zZ(qW?s4qqQM}?`d(6po&b7>Fy{Lq1Khp&v4Urtf!CiIF+Z55?XaVC^{P=+oF9k@xo z5Xu(6(g^)*rItK48>wMC+Q4SL(7eqTA)0#_9o-l9Wv-v!HoV|>UHoo$UN-tT=G!Sv zbv6Bt*e^CY@o5W;HX`!5o*&hkGrx78kzHLaj|-*|Q0|s5$MzR)Whd>X1SpQideMHqv6Q2YCbtw@Zj7h){qPd#r#j@~U&u|qkVuOmSf#MgvPOhE zooH6JAm!h@-AD8*ueCMn*RNm7V2El9r44KN*KpyYEG`4P$qz`Fm?gB4NB9~fzXv2r zMx$jdu~?0jWRNjy4&;6$Jsj@L2ot3ppk~wt;v%e7LEMxNEcn(uy9mlsD!moaCBcl?(7B?IMrG zNimUpA|R*PJ@@SlZllfIcxY2Fm#w?VQU~?gK1@7KpK*peB4IJ?nX$Y|JE8D!XG=he zr;6R=+|Hn`oSl!4jyv*0`kkI%F7hYvG?}1JFY<$Fu6*ke-}XrBPJG>a_?#87hA%7eUrmk6+iEx0^RA2n$6Q6qYgHnn@iqx@l%n zX(faFDuKQWP^nq!puyo^ndU=eN*{cJ{@TA{=a=W_N!gw-kA)wVLg(W|lO4dTHk^Iw1y}XD~*ZdjJ*_-OzGxu8c&_<{&ZsY z$BBFm-;>w(%3Zuxd1mJkszC%wX7m5kzIg^U!L4~b z0)nU%1(eQ<5CTXC=}mg13Q8~1q)12URiulM&>;{?fL8=*(ve7$P(l=tE=VUtTIeB| zJNSOMJ9Bqu@6O)Yot@dQ=hJh}bIyn7_x~NP7tZ|St0%Uga8ny;l{7PQp?O<;~2f)_IsGmwy0MxsegG5EF^%(>}Dbi`R_qAym#r zBoy=J=8^Py=;6E|lvR)=j}Z$6eZI{9-sAMgbj!2I?9_g@+2$P}qa|2hALmUZZ`6MK zlPh=1+KAja^hjn>w9=csnI`e+Dh`OYce7aY-?c`h=SavWPL;+_*Qyvly;1vg;wE7y zC2T{#O&}LGnVe6zwfXX9ISAxKV8ga_UOJN6n&d7D!W3`E&H)~o%J>j0fLm3Pff2XZ zq&;>_TMIZ+-|zxKH$o2pxV)JadU{LCzT%I!NyN^yNzOPo}!YZ{H&< zv@{!)kcM=z@@E*M;vuwu@GMV{%(Ic5TbQi^(!=oOmKQow?*cXEiv^=@UrWkWmVT?d zQwllWz^d8}Ms;-DoM}Hc355$6eB^RggnZq8*3e+I=Zl;K!yj{zjQ%W_yHz(XaZSf2 zR|}_`x@Gl&Ke<$!Ey23J3dlI<5u&K^V9@B6sE(t$7N388e1q&Fpt&3 z;-J^GoT(m$$raX(squ2<=|6S7o!Ki6GE&>zq<6v8<;Z*xWqDRVV7o652KNl0S}Mju zQjspV2JX5vNPLKYP+~zJnsejs>^z28c*9r7B2(Q}8%X~L$VEI6>^P8H2gxjMt>R9k zZ)Zk^X!%oxZF(+KdkjA@<%dWOnv1RT<1DrOUj!){Jahe%;)tGQX4J3Dz3X+pd9%a4 zU_FrIb6Od6YhoWRP&{K#uMGs59$K%B+`hWjDGM{Nv`}h;c1q@Nvv4x?Csobzn=9ta z@uurh%QXC?3v*$`FR#W%EeR_KBh z?(o*edZ>{rHGNOLa|5$@H#aT8iGSK;_`oV&efSi8SAO!8^rc=dW7w(-2K&lu^to+T zFfn(g@%ZbPgM8G0c@K=}y7qak&fP*H_Zn@jAJ+DkB4l*YWk@qzg}}UkLtrW|a(uS- zdIwGCzKS;PwGq4Cm+@+^bsp?hrOEqleBsU#wh9>7iI0@SXE%Q{kw07A3i(m|#k$@M z6VnNno&xn^7wUpGisL=5hdcEBhFWG3rfjaOCAU#R3D!sGcL2du35S*i%Lh;qL*N;m z_<6axdMsjS;v4)Mhh1%pXz(?O+)SmseEz3s;#Yd6br$Xy57pJli;bK_hU%Rq*$sGY zoCJ8{pc3A#G<(!_PFQzZE?2-{%i+Wg=>%nli{bnBr(&Z`fdZOXC?ZwYEMCpV(*z7H zxDSSkjAu$m|JU;Ve~jw?TKS(2?Eh)l_mAem|8L>%3dO%8O#G!#{ELz6|Ise|W7+>= zZH4gsT6`+oZ?~oZX=aBm>-^=EJ&By*OTS>T0e><}@@1<2Q8|R!`)$@Bq>CMnw@bWi z>coCG(Van1E2RxGGo$iXT?LVU^3QyX@9m}d4LuS2bWC>4=PFm?du&9OFGQWs@2kuo zTG%J+UIYOWi@Uy?IK!Jv-;xQ0^zn&biM_oP-HT$8hh~wQB;?P9hQIE@NMEthKO@on z@4iVwMr+%@D`2+7vB;IA)Ekr zkE<8vRp0594ei4B;Lqge1Toh+*}`qfHyJMW*qNHW-n;Os=a8^a`ME=}4k@(cyJS{( zcUg&h=(w~CB??wC`n`6$y_!>=Opi-0-^>jC#G0SMHh7oeTMigJ0LP{aP}V5wKzYMW zZ36s-SdTn^^d^RENHUW}n+}nEDxc+6@s>CZ#z5iCiNubbbYwY~Bj%y)gcu5=>uE_^ z7{IjW+Zk+I+AKvABMZ>w9bsgSNdJ+;I2tys2wUZzbkI#ECd~+2<WN&AD5A^q z0A|R&^4M?y!v>xx+HN$INFxPB5j$e_R~2<5Y~}O$v}e{o9JF%*RYc+1JFqWaa;fuj zS;USozPXRJr*-#Lf0d@ek2hGH4ZTdHKTT0HpfE$A^P>Uc!8S8~KVlG5NCL)aX$_uF zAKz|j|6bQ~XW!!1Q>AOq75g3B6jw+Ayr;n`&*UaBN6Xuif|3FFf|d1E$+M#g7gZJ4 z5Ci46J%q;hJ*Xi3KECzrYgs4x#qn6i^wk0mu;f7dqT=Fj^%W#viH8d)7mE`sgfOSh z%!0OW|AaV<`4`zx#98>rcB$w`$-)fy+Wwtf#QBiwXyHbW3Op$4M|W7Ejcij-2t`!n zP#Xg!=0_pdPek!9)w=cZkFI5eK8^np#rUN9%($4FNe_Bxhm%@T`W!lX63Mf*$~aoC z2|4QZv0W7~j>y6s@4VSDo_$SWN(>MWB5lo*-`boG6|MBn9o&Ny$3T{ z)&M>OFBMIF7>xE)Q~BXiG+c$m4;tNXbzJ8_J4ZGUtswQtg*I3m!yyEaK9{=tGX?Qc z78csDi0V{(Flg_ysX=*p#w+lFNw}ZCzrx()j@}#lxrS3kv#&L>`L`LU9 z00z<~SD8I}#7J%Z_HA_`Tt0-EzVA%c9d9uNE^0oS4{BJ*f9 zAa&c{q?OJ!8IYP<%vX2X$&<#46jS23%IgApOcPjpS_XtUP$z)bp+QXTJ+=0mO0{w{ zSI4ICnF>=O07?T}t$f?&&m!i-R+dJ6@CvA zJheikzIl}qw@fu{d9xg|E6&^BETR}}6s6qLziYX7<`c#Rpu^+=x)g$Z_J*XwYRZ0w zN5VfNJ%ZtXj%U^9ms|$KfJRo@Ba!U&V^4IQ;Qq~CmrHUa#2eAduk#gSkE6q9n_&Jb zXP2KWb;I$N)E)RTEX4798l`Ai-O2na><`p%?1I}2hUcfS(tTsU2e{p|yuoOE$PrU! zn&SHxO2)?hHz?oy{2il&FU9objEkey6~m#Mn~9u4T*9Ab|iRjHe!@OP2&^h z&Z>D_b}!J~G2wb}vDE1O>$I#BqjfOZTUU$^2H)v(kg{Gzh#jvZ^uOzW?dsZ^*qV-q zVkLd4?W{thmh*4heTkrD8>gd8bv%w1V-H)cDm&w!sb{+am3Y5@$|At9UBbyfat_XS zj%`)(p4mBA>-Uq3KAI5mr!0Oow}#d@$vr*mY4`UBM3oo??o^2fX56Ox<> zWLn*&;N9_ZfpPBvdqR4@oWIyO#gl%;awTo@Tj<^ zz@ZSNlyL;|B+KV<2QYiLn?(<%7!9&>Z&efXU0N|UqanMLaFrHx#i{U~%!;14OGdb5 z%Ll|((rTZFFtwz3Fnf=~k0&OiaQq4?ELFJRdJL5zyhLGl_$q>rj*L+2@AAGT;v&9NuPu5m z;}qG9?oqjv#CZ-J8nt@@mW=_`^EhrSlh-<#+)C?KQdm4W?*4SCD7wz^tj@#D_?;hF zcwXnn_bOjk1%bfB7z9d&w2)`6<|}QzOXFQ?Fv&Cb3!kS`qfoi<$zTvW_Gm<|6I&zq z^+W5AT&1w4EuqEtzK%K^j71wd2Pfy9!YaEn7Fmya!WXNt_Btn2Z3c{d{PA#i2q%=NR zzkTdS40Y336AQZ?^zzAg^438E&!qgzhu^31s^#8S8aDcFCcwKZBVl1(XT~OW3-(G5 zd4Bz8{R-Qva96>K1-U`Fg3|7L)kDGen%!g{jqO$lU_EPC$6*PtpwZ{^RKGf7kX+A$ zk;zRRpQ$b5)b-gFu9r|k^Uo~TJp=CCG+jEY-a;r-<&zse{CWwR$LCh@hNC|kAHq6F zM|O|#b~Y3$Pn*}>KR?QV_SgNLu+rX&$?pW$6g$`ZAiH=j$=VYA@()3&o(g6qD^J`I z>2md3_3g{tkwSXPxTktRAaK0nxoF>Xa!?(`O9o_LAYZ@TmrmIGew6)IcDlTxF*3ZY zj3aBz$VA<(rBHcqueVw9t3t5PN|Ls(8}6Bq_EE>@XofU-k#3mF> zq(tYO>d;ZoPeQXV)Jo0Zt1Tr6Bq;nH%@=Uwj-z!TZyt+ zHU_iGL|41a#a8mfDg@${)1-VQ(yd4oml?=v@9bD~l79A7bBemR^^ASTzNKfM4`Z{8 zT3WhOMAzVANLg~nU*e*n*ekajDb;^S~dh$s6y;ZZu z%=(+y1^M;IF`m=Ug701L8d&&)H2nxqb>lQNfR}v7vU?18pVkXEFQ@$JH2^?n3D&s( z=yf_zXp^xeU7#W>*~@}o0h+qATrR)Tt)i8~aM>zAlGmNHkU*zGG&LpuJIIRe)SZ7p z8yu|UxSezzepi{tJY(lB-}Tq6cYDtceGpR@;?4M&a_7M8p5qfgoC5tvx)p$xG(c1d z!0u*i>W|2A+Yj_xuk;-r9$uHNRlH7Bp+rMdvx^!@tYVglgX*7VsGI=U<T+H-6g_0O zEv)Ej=KMLSTI0-`f386e#5?R3_PottTPImuX{PLbwUHeC%}>`QgQPP6hH=G2oTxfF zP8u_9@kqm%(1K!5%Hyftd}wVw*t^oj$dn17>BIxU(2yynl$4Yd8w+ty_q=LgBM&mh z>fph}SYYkO@OL!uZ~s{Q%P;|g@3+@7bNynT6nSO#AN(r$PY{jL5{b&;G4kL>u-g{^ P0001M8fd&%d-C#cWkrVN literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.mielecloud/pom.xml b/bundles/org.openhab.binding.mielecloud/pom.xml new file mode 100644 index 00000000000..2e6d1f621db --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/pom.xml @@ -0,0 +1,17 @@ + + + + 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 00000000000..4024caa71ec --- /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 00000000000..59b1426ad5a --- /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 00000000000..e607db6d232 --- /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 00000000000..94b723bdbe9 --- /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 00000000000..c5cbd3f35f4 --- /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 00000000000..a947da391fa --- /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 00000000000..a2b15cc093f --- /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 00000000000..9acf5030a71 --- /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 00000000000..86ef742af1e --- /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 00000000000..613466ce6d1 --- /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 00000000000..54c22636748 --- /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 00000000000..97f66aae232 --- /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 00000000000..2ba3768b714 --- /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 00000000000..e232ee50db9 --- /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 00000000000..d8a40908731 --- /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 00000000000..8faa7bb2cb9 --- /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 00000000000..8944e0d3f84 --- /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 00000000000..3b667ce183d --- /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 00000000000..a24802b3b29 --- /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 00000000000..e817463adf8 --- /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 00000000000..c5eff7bc669 --- /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 00000000000..79d872a7caf --- /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 00000000000..d93a3c9999f --- /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 00000000000..5a5db090909 --- /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 00000000000..4441aca3d8d --- /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 00000000000..d240f215ee7 --- /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 = "