diff --git a/bundles/org.openhab.binding.tado/README.md b/bundles/org.openhab.binding.tado/README.md index 467d34dbddaa7..c61948787e20a 100644 --- a/bundles/org.openhab.binding.tado/README.md +++ b/bundles/org.openhab.binding.tado/README.md @@ -17,13 +17,23 @@ There are two ways to authenticate it as follows: 1. Online via the OAuth Device Code Grant Flow (RFC-8628) authentication process through the link provided at `http://[openhab-ip-address]:8080/tado`. 1. Enter `username` and `password` credentials in the thing configuration parameters as shown in the table below. -Note: after March 15th, 2025 online authentication is the tado° preferred method. -It is possible that the `username` and `password` method may cease to work some time after this date. - -| Parameter | Required | Description | -|------------|----------|-----------------------------------------------------------| -| `username` | yes | Username used to log in at [my.tado](https://my.tado.com) | -| `password` | yes | Password of the username | +Note: after March 15th, 2025 online authentication is the tado° preferred (or even only) method. +In other words the `username` and `password` method has probably ceased to work after that date. + +| Parameter | Optional | Description | +|---------------|----------|------------------------------------------------------------------------------------| +| `useRfc8628` | yes | Determines if the binding shall use oAuth RFC-8628 authentication | +| `rfcWithUser` | yes | Determines if the user name shall be included in the oAuth RFC-8628 authentication | +| `username` | yes | Username used to log in at [my.tado](https://my.tado.com) | +| `password` | yes | Password of the username | +| `homeId` | yes | Selects the Home Id to use (only needed if the account has multiple homes) | + +The `rfcWithUser` setting is only needed if you have multiple tado° accounts. +It forces the binding to use different authentication tokens for each respective account `username`. + +The `homeId` is only needed if you have multiple homes under a single tado° account. +It forces the binding to read and write the data for the respective Home Id. +If you do not have multiple homes, the binding always uses the first and only Home Id. Example `tado.things` diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/TadoBindingConstants.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/TadoBindingConstants.java index 9306d1dff67f5..2e75d1af8ce01 100644 --- a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/TadoBindingConstants.java +++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/TadoBindingConstants.java @@ -139,6 +139,7 @@ public enum OperationMode { // Configuration public static final String CONFIG_ZONE_ID = "id"; public static final String CONFIG_MOBILE_DEVICE_ID = "id"; + public static final String CONFIG_USE_RFC8628 = "useRfc8628"; // Properties public static final String PROPERTY_ZONE_NAME = "name"; diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/config/TadoHomeConfig.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/config/TadoHomeConfig.java index dd8b666764565..ca147c5998084 100644 --- a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/config/TadoHomeConfig.java +++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/config/TadoHomeConfig.java @@ -25,4 +25,6 @@ public class TadoHomeConfig { public @Nullable String username; public @Nullable String password; public @Nullable Boolean useRfc8628; + public @Nullable Boolean rfcWithUser; + public @Nullable Integer homeId; } diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/handler/TadoHandlerFactory.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/handler/TadoHandlerFactory.java index 4bb7833c3fd83..79a86c5f18390 100644 --- a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/handler/TadoHandlerFactory.java +++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/handler/TadoHandlerFactory.java @@ -20,6 +20,7 @@ import java.util.Hashtable; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletException; @@ -56,6 +57,7 @@ * handlers. * * @author Dennis Frommknecht - Initial contribution + * @author Andrew Fiddian-Green - OAuth RFC18628 authentication */ @NonNullByDefault @Component(configurationPid = "binding.tado", service = ThingHandlerFactory.class) @@ -73,14 +75,13 @@ public class TadoHandlerFactory extends BaseThingHandlerFactory { private final Logger logger = LoggerFactory.getLogger(TadoHandlerFactory.class); private final Set oAuthClientServiceSubscribers = new HashSet<>(); private final Map> discoveryServiceRegs = new HashMap<>(); + private final Map oAuthClientServices = new ConcurrentHashMap<>(); private final TadoStateDescriptionProvider stateDescriptionProvider; private final HttpService httpService; private final OAuthFactory oAuthFactory; private final TadoAuthenticationServlet httpServlet; - private @Nullable OAuthClientService oAuthClientService; - @Activate public TadoHandlerFactory(@Reference TadoStateDescriptionProvider stateDescriptionProvider, @Reference HttpService httpService, @Reference OAuthFactory oAuthFactory) { @@ -92,9 +93,8 @@ public TadoHandlerFactory(@Reference TadoStateDescriptionProvider stateDescripti @Deactivate public void deactivate() { - if (oAuthClientService != null) { - oAuthFactory.ungetOAuthService(THING_TYPE_HOME.toString()); - } + oAuthClientServices.keySet().forEach(id -> oAuthFactory.ungetOAuthService(id)); + oAuthClientServices.clear(); } @Override @@ -107,7 +107,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (thingTypeUID.equals(THING_TYPE_HOME)) { - TadoHomeHandler tadoHomeHandler = new TadoHomeHandler((Bridge) thing, this, oAuthFactory); + TadoHomeHandler tadoHomeHandler = new TadoHomeHandler((Bridge) thing, this); registerTadoDiscoveryService(tadoHomeHandler); return tadoHomeHandler; } else if (thingTypeUID.equals(THING_TYPE_ZONE)) { @@ -148,10 +148,11 @@ protected synchronized void removeHandler(ThingHandler thingHandler) { * Retrieves the pre-existing {@link OAuthClientService} if present, or creates a new one. * If necessary also registers the {@link TadoAuthenticationServlet}. * - * @param tadoHomeHandler + * @param tadoHomeHandler the subscribing thing handler + * @param user the optional user name (may be null) * @return an {@link OAuthClientService} */ - public OAuthClientService subscribeOAuthClientService(TadoHomeHandler tadoHomeHandler) { + public OAuthClientService subscribeOAuthClientService(TadoHomeHandler tadoHomeHandler, @Nullable String user) { if (oAuthClientServiceSubscribers.isEmpty()) { try { httpService.registerServlet(TadoAuthenticationServlet.PATH, httpServlet, null, null); @@ -162,16 +163,18 @@ public OAuthClientService subscribeOAuthClientService(TadoHomeHandler tadoHomeHa oAuthClientServiceSubscribers.add(tadoHomeHandler); - OAuthClientService oAuthClientService = this.oAuthClientService; + OAuthClientService oAuthClientService = oAuthClientServices.get(getServiceId(user)); if (oAuthClientService == null) { - oAuthClientService = oAuthFactory.getOAuthClientService(THING_TYPE_HOME.toString()); - this.oAuthClientService = oAuthClientService; + oAuthClientService = oAuthFactory.getOAuthClientService(getServiceId(user)); + if (oAuthClientService != null) { + oAuthClientServices.put(getServiceId(user), oAuthClientService); + } } if (oAuthClientService == null) { - oAuthClientService = oAuthFactory.createOAuthClientService(THING_TYPE_HOME.toString(), // - OAUTH_TOKEN_URL, OAUTH_DEVICE_URL, OAUTH_CLIENT_ID, null, OAUTH_SCOPE, false); - this.oAuthClientService = oAuthClientService; + oAuthClientService = oAuthFactory.createOAuthClientService(getServiceId(user), OAUTH_TOKEN_URL, + OAUTH_DEVICE_URL, OAUTH_CLIENT_ID, null, OAUTH_SCOPE, false); + oAuthClientServices.put(getServiceId(user), oAuthClientService); } return oAuthClientService; @@ -193,11 +196,15 @@ public void unsubscribeOAuthClientService(TadoHomeHandler tadoHomeHandler) { /** * Returns a nullable {@link AccessTokenResponse} if the OAuthClientService exists. * + * @param user the optional user name (may be null) * @return a nullable {@link AccessTokenResponse}. - * @throws OAuthException on any error + * @throws OAuthException + * @throws IOException + * @throws OAuthResponseException */ - public @Nullable AccessTokenResponse getAccessTokenResponse() throws OAuthException { - OAuthClientService oAuthClientService = this.oAuthClientService; + public @Nullable AccessTokenResponse getAccessTokenResponse(@Nullable String user) + throws OAuthException, IOException, OAuthResponseException { + OAuthClientService oAuthClientService = oAuthClientServices.get(getServiceId(user)); if (oAuthClientService == null) { throw new OAuthException("Missing OAuthClientService"); } @@ -205,18 +212,19 @@ public void unsubscribeOAuthClientService(TadoHomeHandler tadoHomeHandler) { return oAuthClientService.getAccessTokenResponse(); } catch (OAuthException | IOException | OAuthResponseException e) { logger.debug("getAccessTokenResponse() error {}", e.getMessage(), e); - throw new OAuthException("OAuthClientService error" + e.getMessage(), e); + throw e; } } /** * Returns a non null DeviceCodeResponse from the OAuthClientService if it exists. * + * @param user the optional user name (may be null) * @return a {@link DeviceCodeResponseDTO} * @throws OAuthException if it cannot return a non null result */ - public DeviceCodeResponseDTO getDeviceCodeResponse() throws OAuthException { - OAuthClientService oAuthClientService = this.oAuthClientService; + public DeviceCodeResponseDTO getDeviceCodeResponse(@Nullable String user) throws OAuthException { + OAuthClientService oAuthClientService = oAuthClientServices.get(getServiceId(user)); if (oAuthClientService == null) { throw new OAuthException("Missing OAuthClientService"); } @@ -227,7 +235,21 @@ public DeviceCodeResponseDTO getDeviceCodeResponse() throws OAuthException { return result; } - public boolean hasOAuthClientService() { - return oAuthClientService != null; + /** + * Check if there is an OAuthClientService registered + * + * @param user the optional user name (may be null) + */ + public boolean hasOAuthClientService(@Nullable String user) { + return oAuthClientServices.containsKey(getServiceId(user)); + } + + /** + * Build a unique OAuth service id using the (optional) user name if present and not blank + * + * @param user the optional user name (may be null) + */ + private String getServiceId(@Nullable String user) { + return THING_TYPE_HOME.toString() + (user != null && !user.isBlank() ? ":" + user : ""); } } diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/handler/TadoHomeHandler.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/handler/TadoHomeHandler.java index 801c2e06ce894..75e0b7e66d650 100644 --- a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/handler/TadoHomeHandler.java +++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/handler/TadoHomeHandler.java @@ -20,6 +20,7 @@ import java.util.Objects; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -38,7 +39,7 @@ 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.config.core.Configuration; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -56,6 +57,7 @@ * The {@link TadoHomeHandler} is the bridge of all home-based things. * * @author Dennis Frommknecht - Initial contribution + * @author Andrew Fiddian-Green - OAuth RFC18628 authentication */ @NonNullByDefault public class TadoHomeHandler extends BaseBridgeHandler implements AccessTokenRefreshListener { @@ -64,7 +66,8 @@ public class TadoHomeHandler extends BaseBridgeHandler implements AccessTokenRef private static final String CONF_ERROR_NO_HOME = "@text/tado.home.status.nohome"; private static final String CONF_ERROR_NO_HOME_ID = "@text/tado.home.status.nohomeid"; private static final String CONF_PENDING_USER_CREDS = "@text/tado.home.status.username"; - private static final String CONF_PENDING_OAUTH_CREDS = "@text/tado.home.status.oauth [\"http(s)://:%s\"]"; + private static final String CONF_PENDING_OAUTH_CREDS = // + "@text/tado.home.status.oauth [\"http(s)://:%s?%s=%s\"]"; // tado specific RFC-8628 oAuth authentication parameters private static final ZonedDateTime OAUTH_MANDATORY_FROM_DATE = ZonedDateTime.parse("2025-03-15T00:00:00Z"); @@ -83,7 +86,7 @@ public class TadoHomeHandler extends BaseBridgeHandler implements AccessTokenRef private @Nullable ScheduledFuture initializationFuture; private @Nullable OAuthClientService oAuthClientService; - public TadoHomeHandler(Bridge bridge, TadoHandlerFactory tadoHandlerFactory, OAuthFactory oAuthFactory) { + public TadoHomeHandler(Bridge bridge, TadoHandlerFactory tadoHandlerFactory) { super(bridge); this.batteryChecker = new TadoBatteryChecker(this); this.configuration = getConfigAs(TadoHomeConfig.class); @@ -100,9 +103,9 @@ public TemperatureUnit getTemperatureUnit() { public void initialize() { configuration = getConfigAs(TadoHomeConfig.class); - String userName = configuration.username; + String username = configuration.username; String password = configuration.password; - boolean v1CredentialsMissing = userName == null || userName.isBlank() || password == null || password.isBlank(); + boolean v1CredentialsMissing = username == null || username.isBlank() || password == null || password.isBlank(); boolean suggestRfc8628 = false; suggestRfc8628 |= Boolean.TRUE.equals(configuration.useRfc8628); @@ -110,14 +113,23 @@ public void initialize() { suggestRfc8628 |= ZonedDateTime.now().isAfter(OAUTH_MANDATORY_FROM_DATE); if (suggestRfc8628) { - OAuthClientService oAuthClientService = tadoHandlerFactory.subscribeOAuthClientService(this); + String rfcUser = Boolean.TRUE.equals(configuration.rfcWithUser) // + ? username != null && !username.isBlank() ? username : null + : null; + OAuthClientService oAuthClientService = tadoHandlerFactory.subscribeOAuthClientService(this, rfcUser); oAuthClientService.addAccessTokenRefreshListener(this); this.api = new HomeApiFactory().create(oAuthClientService); this.oAuthClientService = oAuthClientService; logger.trace("initialize() api v2 created"); - confPendingText = CONF_PENDING_OAUTH_CREDS.formatted(TadoAuthenticationServlet.PATH); + confPendingText = CONF_PENDING_OAUTH_CREDS.formatted(TadoAuthenticationServlet.PATH, + TadoAuthenticationServlet.PARAM_NAME_USER, rfcUser != null ? rfcUser : ""); + if (!Boolean.TRUE.equals(configuration.useRfc8628)) { + Configuration configuration = editConfiguration(); + configuration.put(CONFIG_USE_RFC8628, Boolean.TRUE); + updateConfiguration(configuration); + } } else { - api = new HomeApiFactory().create(Objects.requireNonNull(userName), Objects.requireNonNull(password)); + api = new HomeApiFactory().create(Objects.requireNonNull(username), Objects.requireNonNull(password)); logger.trace("initialize() api v1 created"); confPendingText = CONF_PENDING_USER_CREDS; } @@ -156,7 +168,24 @@ private synchronized void initializeBridgeStatusAndPropertiesIfOffline() { return; } + /* + * If there is only one home, or if there is no valid configuration.homeId entry, then use the first + * home id. Otherwise use the configuration.homeId entry value (if one exists). If there is no valid + * configuration.homeId entry but there are multiple homes then log the available home to help the + * user set up the proper configuration.homeId entry. + */ Integer firstHomeId = homes.get(0).getId(); + if (homes.size() > 1) { + Integer configHomeId = configuration.homeId; + if (configHomeId == null || configHomeId == 0) { + logger.info("Trying first Home Id in the list [{}]", homes.stream() + .map(home -> String.valueOf(home.getId())).collect(Collectors.joining(","))); + } else { + firstHomeId = homes.stream().map(home -> home.getId()).filter(id -> configHomeId.equals(id)) + .findFirst().orElse(null); + } + } + if (firstHomeId == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, CONF_ERROR_NO_HOME_ID); return; diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/servlet/TadoAuthenticationServlet.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/servlet/TadoAuthenticationServlet.java index 9835fc2eee59e..b955b8dd9f1a3 100644 --- a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/servlet/TadoAuthenticationServlet.java +++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/servlet/TadoAuthenticationServlet.java @@ -24,6 +24,7 @@ import org.openhab.binding.tado.internal.handler.TadoHandlerFactory; import org.openhab.core.auth.client.oauth2.DeviceCodeResponseDTO; import org.openhab.core.auth.client.oauth2.OAuthException; +import org.openhab.core.auth.client.oauth2.OAuthResponseException; /** * The {@link TadoAuthenticationServlet} manages the authorization with the Tado API. @@ -43,8 +44,9 @@ public class TadoAuthenticationServlet extends HttpServlet { private static final String HTML_AUTH_ERROR_TEMPLATE = "$REPLACE$"; private static final String HTML_AUTH_START_TEMPLATE = "click to authenticate"; - private static final String PARAM_NAME = "oauth"; - private static final String PARAM_VALUE = "start"; + public static final String PARAM_NAME_USER = "user"; + private static final String PARAM_NAME_OAUTH = "oauth"; + private static final String PARAM_VALUE_START = "start"; private static final String ERROR_BAD_URL = "no verification uri"; @@ -76,8 +78,9 @@ public TadoAuthenticationServlet(TadoHandlerFactory tadoHandlerFactory) { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - // if the query string is "?oauth=start" then serve the user authentication page - if (PARAM_VALUE.equals(request.getParameter(PARAM_NAME)) && tadoHandlerFactory.hasOAuthClientService()) { + // if the query string contains "oauth=start" then serve the user authentication page + if (PARAM_VALUE_START.equals(request.getParameter(PARAM_NAME_OAUTH)) + && tadoHandlerFactory.hasOAuthClientService(request.getParameter(PARAM_NAME_USER))) { serveUserAuthenticationPage(request, response); } else { serveStatusPage(request, response); @@ -100,24 +103,25 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) private void serveStatusPage(HttpServletRequest request, HttpServletResponse response) throws IOException { String dynamicHtml = null; - if (!tadoHandlerFactory.hasOAuthClientService()) { + if (!tadoHandlerFactory.hasOAuthClientService(request.getParameter(PARAM_NAME_USER))) { dynamicHtml = HTML_AUTH_NOT_REQUIRED; } if (dynamicHtml == null) { try { - if (tadoHandlerFactory.getAccessTokenResponse() != null) { + if (tadoHandlerFactory.getAccessTokenResponse(request.getParameter(PARAM_NAME_USER)) != null) { dynamicHtml = HTML_AUTH_PASSED; } - } catch (OAuthException e) { - dynamicHtml = HTML_AUTH_ERROR_TEMPLATE.replace(REPLACE_TAG, - e.getMessage() instanceof String exception ? exception : e.getClass().getName()); + } catch (OAuthException | OAuthResponseException e) { + // error already logged => fall through } } if (dynamicHtml == null) { if (request.getRequestURL() instanceof StringBuffer baseUrl) { - String dynamicUrl = baseUrl.append("?").append(PARAM_NAME).append("=").append(PARAM_VALUE).toString(); + String dynamicUrl = baseUrl.append("?").append(PARAM_NAME_OAUTH).append("=").append(PARAM_VALUE_START) + .append("&").append(PARAM_NAME_USER).append("=").append(request.getParameter(PARAM_NAME_USER)) + .toString(); dynamicHtml = HTML_AUTH_START_TEMPLATE.replace(REPLACE_TAG, dynamicUrl); } else { dynamicHtml = HTML_AUTH_ERROR_TEMPLATE.replace(REPLACE_TAG, ERROR_BAD_URL); @@ -145,7 +149,8 @@ private void serveUserAuthenticationPage(HttpServletRequest request, HttpServlet String dynamicHtml = null; try { - DeviceCodeResponseDTO deviceCodeResponse = tadoHandlerFactory.getDeviceCodeResponse(); + DeviceCodeResponseDTO deviceCodeResponse = tadoHandlerFactory + .getDeviceCodeResponse(request.getParameter(PARAM_NAME_USER)); String userVerificationUri = deviceCodeResponse.getVerificationUriComplete(); if (userVerificationUri != null && !userVerificationUri.isBlank()) { response.sendRedirect(userVerificationUri); diff --git a/bundles/org.openhab.binding.tado/src/main/resources/OH-INF/i18n/tado.properties b/bundles/org.openhab.binding.tado/src/main/resources/OH-INF/i18n/tado.properties index 84283669412c7..b3cd5183319d3 100644 --- a/bundles/org.openhab.binding.tado/src/main/resources/OH-INF/i18n/tado.properties +++ b/bundles/org.openhab.binding.tado/src/main/resources/OH-INF/i18n/tado.properties @@ -1,128 +1,132 @@ -# add-on - -addon.tado.name = tado° Binding -addon.tado.description = Binding for tado° devices - -# thing types - -thing-type.tado.home.label = Tado Home -thing-type.tado.home.description = The user's tado home -thing-type.tado.mobiledevice.label = Mobile Device -thing-type.tado.mobiledevice.description = Mobile device of a home -thing-type.tado.zone.label = Zone -thing-type.tado.zone.description = A zone of a home -thing-type.tado.zone.channel.batteryLowAlarm.label = Battery Low Alarm -thing-type.tado.zone.channel.batteryLowAlarm.description = ON if one or more devices in the zone have a low battery -thing-type.tado.zone.channel.humidity.label = Humidity -thing-type.tado.zone.channel.humidity.description = Current humidity in % - -# thing types config - -thing-type.config.tado.home.password.label = Password -thing-type.config.tado.home.password.description = Password of tado login used for API access -thing-type.config.tado.home.useRfc8628.label = Use oAuth RFC-8628 -thing-type.config.tado.home.useRfc8628.description = Determines if the binding shall use oAuth RFC-8628 authentication -thing-type.config.tado.home.username.label = User Name -thing-type.config.tado.home.username.description = User name of tado login used for API access -thing-type.config.tado.mobiledevice.id.label = Mobile Device Id -thing-type.config.tado.mobiledevice.id.description = Id of the mobile device -thing-type.config.tado.mobiledevice.refreshInterval.label = Refresh Interval -thing-type.config.tado.mobiledevice.refreshInterval.description = Refresh interval of location state -thing-type.config.tado.zone.fallbackTimerDuration.label = Fallback Timer Duration -thing-type.config.tado.zone.fallbackTimerDuration.description = Timer duration used if no other duration can be determined -thing-type.config.tado.zone.hvacChangeDebounce.label = HVAC Change Debounce Delay -thing-type.config.tado.zone.hvacChangeDebounce.description = Duration in seconds to combine multiple HVAC changes into one. -thing-type.config.tado.zone.id.label = Zone Id -thing-type.config.tado.zone.id.description = Id of the zone -thing-type.config.tado.zone.refreshInterval.label = Refresh Interval -thing-type.config.tado.zone.refreshInterval.description = Refresh interval of home data - -# channel types - -channel-type.tado.acPower.label = Air-conditioning Power -channel-type.tado.acPower.description = Current power state of the air-conditioning -channel-type.tado.atHome.label = At Home -channel-type.tado.atHome.description = ON if at home, OFF if away -channel-type.tado.currentTemperature.label = Temperature -channel-type.tado.currentTemperature.description = Current temperature -channel-type.tado.fanLevel.label = Fan Speed -channel-type.tado.fanLevel.description = AC fan level (only if supported by AC) -channel-type.tado.fanLevel.state.option.SILENT = SILENT -channel-type.tado.fanLevel.state.option.LEVEL1 = LEVEL1 -channel-type.tado.fanLevel.state.option.LEVEL2 = LEVEL2 -channel-type.tado.fanLevel.state.option.LEVEL3 = LEVEL3 -channel-type.tado.fanLevel.state.option.LEVEL4 = LEVEL4 -channel-type.tado.fanLevel.state.option.LEVEL5 = LEVEL5 -channel-type.tado.fanLevel.state.option.AUTO = AUTO -channel-type.tado.fanspeed.label = Fan Speed -channel-type.tado.fanspeed.description = AC fan speed (only if supported by AC) -channel-type.tado.fanspeed.state.option.LOW = Low -channel-type.tado.fanspeed.state.option.MIDDLE = Middle -channel-type.tado.fanspeed.state.option.HIGH = High -channel-type.tado.fanspeed.state.option.AUTO = Auto -channel-type.tado.geofencingEnabled.label = Geofencing Enabled -channel-type.tado.geofencingEnabled.description = Selects if automatic geofencing is enabled or disabled -channel-type.tado.heatingPower.label = Heating Power -channel-type.tado.heatingPower.description = Current heating power -channel-type.tado.homePresence.label = At Home -channel-type.tado.homePresence.description = ON if at home, OFF if away -channel-type.tado.horizontalSwing.label = Horizontal Swing -channel-type.tado.horizontalSwing.description = State of AC horizontal swing (only if supported by AC) -channel-type.tado.horizontalSwing.state.option.AUTO = AUTO -channel-type.tado.horizontalSwing.state.option.LEFT = LEFT -channel-type.tado.horizontalSwing.state.option.MID_LEFT = MID-LEFT -channel-type.tado.horizontalSwing.state.option.MID = MID -channel-type.tado.horizontalSwing.state.option.MID_RIGHT = MID-RIGHT -channel-type.tado.horizontalSwing.state.option.RIGHT = RIGHT -channel-type.tado.horizontalSwing.state.option.ON = ON -channel-type.tado.horizontalSwing.state.option.OFF = OFF -channel-type.tado.hvacMode.label = HVAC Mode -channel-type.tado.hvacMode.description = Mode of the device (OFF, HEAT, COOL, DRY, FAN, AUTO - if supported) -channel-type.tado.hvacMode.state.option.OFF = Off -channel-type.tado.hvacMode.state.option.HEAT = Heat -channel-type.tado.hvacMode.state.option.COOL = Cool -channel-type.tado.hvacMode.state.option.DRY = Dry -channel-type.tado.hvacMode.state.option.FAN = Fan -channel-type.tado.hvacMode.state.option.AUTO = Auto -channel-type.tado.light.label = Light -channel-type.tado.light.description = State of control panel light (only if supported by AC) -channel-type.tado.openWindowDetected.label = Open Window Detected -channel-type.tado.openWindowDetected.description = Indicates if an open window has been detected -channel-type.tado.openWindowRemainingTime.label = Override Remaining Time -channel-type.tado.openWindowRemainingTime.description = The remaining Open Window heating/cooling Override time in the Zone -channel-type.tado.operationMode.label = Zone Operation Mode -channel-type.tado.operationMode.description = Active operation mode (schedule, manual, timer or until next change) -channel-type.tado.operationMode.state.option.SCHEDULE = Schedule -channel-type.tado.operationMode.state.option.MANUAL = Manual -channel-type.tado.operationMode.state.option.UNTIL_CHANGE = Until change -channel-type.tado.operationMode.state.option.TIMER = Timer -channel-type.tado.overlayExpiry.label = Overlay End Time -channel-type.tado.overlayExpiry.description = Time until when the overlay is active. Null if no overlay is set or overlay type is manual. -channel-type.tado.overlayExpiry.state.pattern = %1$tF %1$tR -channel-type.tado.swing.label = Swing -channel-type.tado.swing.description = State of AC swing (only if supported by AC) -channel-type.tado.targetTemperature.label = Target Temperature -channel-type.tado.targetTemperature.description = Thermostat temperature setpoint -channel-type.tado.timerDuration.label = Timer Duration -channel-type.tado.timerDuration.description = Total duration of a timer -channel-type.tado.verticalSwing.label = Vertical Swing -channel-type.tado.verticalSwing.description = State of AC vertical swing (only if supported by AC) -channel-type.tado.verticalSwing.state.option.AUTO = AUTO -channel-type.tado.verticalSwing.state.option.UP = UP -channel-type.tado.verticalSwing.state.option.MID_UP = MID-UP -channel-type.tado.verticalSwing.state.option.MID = MID -channel-type.tado.verticalSwing.state.option.MID_DOWN = MID-DOWN -channel-type.tado.verticalSwing.state.option.DOWN = DOWN -channel-type.tado.verticalSwing.state.option.ON = ON -channel-type.tado.verticalSwing.state.option.OFF = OFF - -# tado home thing status messages - -tado.home.status.oauth = Try authenticating at {0} -tado.home.status.username = Username and/or password might be invalid -tado.home.status.nohome = User does not have access to any home -tado.home.status.nohomeid = Missing Home Id - -# discovery - -discovery.bridge.label = tado° Internet Bridge ({0}) +# add-on + +addon.tado.name = tado° Binding +addon.tado.description = Binding for tado° devices + +# thing types + +thing-type.tado.home.label = Tado Home +thing-type.tado.home.description = The user's tado home +thing-type.tado.mobiledevice.label = Mobile Device +thing-type.tado.mobiledevice.description = Mobile device of a home +thing-type.tado.zone.label = Zone +thing-type.tado.zone.description = A zone of a home +thing-type.tado.zone.channel.batteryLowAlarm.label = Battery Low Alarm +thing-type.tado.zone.channel.batteryLowAlarm.description = ON if one or more devices in the zone have a low battery +thing-type.tado.zone.channel.humidity.label = Humidity +thing-type.tado.zone.channel.humidity.description = Current humidity in % + +# thing types config + +thing-type.config.tado.home.homeId.label = Home Id +thing-type.config.tado.home.homeId.description = Selects the Home Id to be used (only needed if there are multiple homes) +thing-type.config.tado.home.password.label = Password +thing-type.config.tado.home.password.description = Password of tado login used for API access +thing-type.config.tado.home.rfcWithUser.label = RFC-8628 with User +thing-type.config.tado.home.rfcWithUser.description = Determines if the user name is included in the oAuth RFC-8628 authentication +thing-type.config.tado.home.useRfc8628.label = Use oAuth RFC-8628 +thing-type.config.tado.home.useRfc8628.description = Determines if the binding shall use oAuth RFC-8628 authentication +thing-type.config.tado.home.username.label = User Name +thing-type.config.tado.home.username.description = User name of tado login used for API access +thing-type.config.tado.mobiledevice.id.label = Mobile Device Id +thing-type.config.tado.mobiledevice.id.description = Id of the mobile device +thing-type.config.tado.mobiledevice.refreshInterval.label = Refresh Interval +thing-type.config.tado.mobiledevice.refreshInterval.description = Refresh interval of location state +thing-type.config.tado.zone.fallbackTimerDuration.label = Fallback Timer Duration +thing-type.config.tado.zone.fallbackTimerDuration.description = Timer duration used if no other duration can be determined +thing-type.config.tado.zone.hvacChangeDebounce.label = HVAC Change Debounce Delay +thing-type.config.tado.zone.hvacChangeDebounce.description = Duration in seconds to combine multiple HVAC changes into one. +thing-type.config.tado.zone.id.label = Zone Id +thing-type.config.tado.zone.id.description = Id of the zone +thing-type.config.tado.zone.refreshInterval.label = Refresh Interval +thing-type.config.tado.zone.refreshInterval.description = Refresh interval of home data + +# channel types + +channel-type.tado.acPower.label = Air-conditioning Power +channel-type.tado.acPower.description = Current power state of the air-conditioning +channel-type.tado.atHome.label = At Home +channel-type.tado.atHome.description = ON if at home, OFF if away +channel-type.tado.currentTemperature.label = Temperature +channel-type.tado.currentTemperature.description = Current temperature +channel-type.tado.fanLevel.label = Fan Speed +channel-type.tado.fanLevel.description = AC fan level (only if supported by AC) +channel-type.tado.fanLevel.state.option.SILENT = SILENT +channel-type.tado.fanLevel.state.option.LEVEL1 = LEVEL1 +channel-type.tado.fanLevel.state.option.LEVEL2 = LEVEL2 +channel-type.tado.fanLevel.state.option.LEVEL3 = LEVEL3 +channel-type.tado.fanLevel.state.option.LEVEL4 = LEVEL4 +channel-type.tado.fanLevel.state.option.LEVEL5 = LEVEL5 +channel-type.tado.fanLevel.state.option.AUTO = AUTO +channel-type.tado.fanspeed.label = Fan Speed +channel-type.tado.fanspeed.description = AC fan speed (only if supported by AC) +channel-type.tado.fanspeed.state.option.LOW = Low +channel-type.tado.fanspeed.state.option.MIDDLE = Middle +channel-type.tado.fanspeed.state.option.HIGH = High +channel-type.tado.fanspeed.state.option.AUTO = Auto +channel-type.tado.geofencingEnabled.label = Geofencing Enabled +channel-type.tado.geofencingEnabled.description = Selects if automatic geofencing is enabled or disabled +channel-type.tado.heatingPower.label = Heating Power +channel-type.tado.heatingPower.description = Current heating power +channel-type.tado.homePresence.label = At Home +channel-type.tado.homePresence.description = ON if at home, OFF if away +channel-type.tado.horizontalSwing.label = Horizontal Swing +channel-type.tado.horizontalSwing.description = State of AC horizontal swing (only if supported by AC) +channel-type.tado.horizontalSwing.state.option.AUTO = AUTO +channel-type.tado.horizontalSwing.state.option.LEFT = LEFT +channel-type.tado.horizontalSwing.state.option.MID_LEFT = MID-LEFT +channel-type.tado.horizontalSwing.state.option.MID = MID +channel-type.tado.horizontalSwing.state.option.MID_RIGHT = MID-RIGHT +channel-type.tado.horizontalSwing.state.option.RIGHT = RIGHT +channel-type.tado.horizontalSwing.state.option.ON = ON +channel-type.tado.horizontalSwing.state.option.OFF = OFF +channel-type.tado.hvacMode.label = HVAC Mode +channel-type.tado.hvacMode.description = Mode of the device (OFF, HEAT, COOL, DRY, FAN, AUTO - if supported) +channel-type.tado.hvacMode.state.option.OFF = Off +channel-type.tado.hvacMode.state.option.HEAT = Heat +channel-type.tado.hvacMode.state.option.COOL = Cool +channel-type.tado.hvacMode.state.option.DRY = Dry +channel-type.tado.hvacMode.state.option.FAN = Fan +channel-type.tado.hvacMode.state.option.AUTO = Auto +channel-type.tado.light.label = Light +channel-type.tado.light.description = State of control panel light (only if supported by AC) +channel-type.tado.openWindowDetected.label = Open Window Detected +channel-type.tado.openWindowDetected.description = Indicates if an open window has been detected +channel-type.tado.openWindowRemainingTime.label = Override Remaining Time +channel-type.tado.openWindowRemainingTime.description = The remaining Open Window heating/cooling Override time in the Zone +channel-type.tado.operationMode.label = Zone Operation Mode +channel-type.tado.operationMode.description = Active operation mode (schedule, manual, timer or until next change) +channel-type.tado.operationMode.state.option.SCHEDULE = Schedule +channel-type.tado.operationMode.state.option.MANUAL = Manual +channel-type.tado.operationMode.state.option.UNTIL_CHANGE = Until change +channel-type.tado.operationMode.state.option.TIMER = Timer +channel-type.tado.overlayExpiry.label = Overlay End Time +channel-type.tado.overlayExpiry.description = Time until when the overlay is active. Null if no overlay is set or overlay type is manual. +channel-type.tado.overlayExpiry.state.pattern = %1$tF %1$tR +channel-type.tado.swing.label = Swing +channel-type.tado.swing.description = State of AC swing (only if supported by AC) +channel-type.tado.targetTemperature.label = Target Temperature +channel-type.tado.targetTemperature.description = Thermostat temperature setpoint +channel-type.tado.timerDuration.label = Timer Duration +channel-type.tado.timerDuration.description = Total duration of a timer +channel-type.tado.verticalSwing.label = Vertical Swing +channel-type.tado.verticalSwing.description = State of AC vertical swing (only if supported by AC) +channel-type.tado.verticalSwing.state.option.AUTO = AUTO +channel-type.tado.verticalSwing.state.option.UP = UP +channel-type.tado.verticalSwing.state.option.MID_UP = MID-UP +channel-type.tado.verticalSwing.state.option.MID = MID +channel-type.tado.verticalSwing.state.option.MID_DOWN = MID-DOWN +channel-type.tado.verticalSwing.state.option.DOWN = DOWN +channel-type.tado.verticalSwing.state.option.ON = ON +channel-type.tado.verticalSwing.state.option.OFF = OFF + +# tado home thing status messages + +tado.home.status.oauth = Try authenticating at {0} +tado.home.status.username = Username and/or password might be invalid +tado.home.status.nohome = User does not have access to any home +tado.home.status.nohomeid = Missing Home Id + +# discovery + +discovery.bridge.label = tado° Internet Bridge ({0}) diff --git a/bundles/org.openhab.binding.tado/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.tado/src/main/resources/OH-INF/thing/thing-types.xml index b3a06cecf2e57..e778a9a0886d1 100644 --- a/bundles/org.openhab.binding.tado/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.tado/src/main/resources/OH-INF/thing/thing-types.xml @@ -24,6 +24,12 @@ Determines if the binding shall use oAuth RFC-8628 authentication + + + Determines if the user name is included in the oAuth RFC-8628 authentication + true + + User name of tado login used for API access @@ -36,6 +42,12 @@ password true + + + + Selects the Home Id to be used (only needed if there are multiple homes) + true +