diff --git a/bundles/org.openhab.binding.tado/README.md b/bundles/org.openhab.binding.tado/README.md
index 4bd995224a160..87854dbb46a42 100644
--- a/bundles/org.openhab.binding.tado/README.md
+++ b/bundles/org.openhab.binding.tado/README.md
@@ -1,265 +1,272 @@
-# tado° Binding
-
-The tado° binding integrates devices from [tado°](https://www.tado.com).
-
-It requires a fully functional tado° installation.
-You can then monitor and control all zone types (Heating, AC, Hot Water) as well as retrieve the HOME/AWAY status of mobile devices, and setting the HOME/AWAY status of your home.
-
-**WARNING**: This binding doesn't work together with new tado X radiator thermostats, as they are using a different API (see [here](https://app.swaggerhub.com/apis/JPlenert/TadoX/0.8.0) ).
-
-## `home` Thing (the Bridge)
-
-The `home` thing serves as bridge to the tado° cloud services.
-The binding will automatically discover this thing and place it in the Inbox.
-It has to be manually configured with its `username` and `password` before it will actually go onlime.
-
-Parameter | Required | Description
--|-|-
-`username` | yes | Username used to log in at [my.tado](https://my.tado.com)
-`password` | yes | Password of the username
-
-Example `tado.things`
-
-```java
-Bridge tado:home:demo [ username="mail@example.com", password="secret" ]
-```
-
-Once the `home` thing is online, the binding will discover all its respective zones and mobile devices, and place them in the Inbox.
-
-### Channels
-
-Name | Type | Description | Read/Write
--|-|-|-
-`homePresence` | String | Current presence value of the tado home. `HOME` and `AWAY` can be set | RW
-
-## `zone` Thing
-
-A _zone_ is an area/room of your home.
-You have defined them during installation.
-One zone relates to one page in the tado° mobile- or webapp.
-
-Parameter | Required | Description | Default
--|-|-|-
-`id` | yes | Zone Id | -
-`refreshInterval` | no | Refresh interval of state updates in seconds | 30
-`hvacChangeDebounce` | no | Duration in seconds to combine multiple HVAC changes into one | 5
-
-Zones can either be added through discovery or manually. Following up on the above example, a zone configuration could look like this:
-
-Example `tado.things`
-
-```java
-Bridge tado:home:demo [ username="mail@example.com", password="secret" ] {
- zone heating [id=1]
- zone ac [id=2]
- zone hotwater [id=0]
-}
-```
-
-Zone id and name can be found in discovery results.
-
-### Channels
-
-A zone is either of type `HEATING`, `AC` or `DHW` (domestic hot water).
-The availability of items as well as their allowed values depend on type and capabilities of the HVAC setup.
-If you are unsure, have a look at the tado° app and see if the functionality is available and what values are supported.
-
-Name | Type | Description | Read/Write | Zone type
--|-|-|-|-
-`currentTemperature` | Number:Temperature | Current inside temperature | R | `HEATING`, `AC`
-`humidity` | Number:Dimensionless | Current relative inside humidity in percent | R | `HEATING`, `AC`
-`hvacMode` | String | Active mode, one of `OFF`, `HEAT`, `COOL`, `DRY`, `FAN`, `AUTO` | RW | `HEATING` and `DHW` support `OFF` and `HEAT`, `AC` can support more
-`targetTemperature` | Number:Temperature | Set point | RW | `HEATING`, `AC`, `DHW`
-`operationMode` | String | Operation mode the zone is currently in. One of `SCHEDULE` (follow smart schedule), `MANUAL` (override until ended manually), `TIMER` (override for a given time), `UNTIL_CHANGE` (active until next smart schedule block or until AWAY mode becomes active) | RW | `HEATING`, `AC`, `DHW`
-`overlayExpiry` | DateTime | End date and time of a timer | R | `HEATING`, `AC`, `DHW`
-`timerDuration` | Number | Timer duration in minutes | RW | `HEATING`, `AC`, `DHW`
-`heatingPower` | Number:Dimensionless | Amount of heating power currently present | R | `HEATING`
-`acPower` | Switch | Indicates if the Air-Conditioning is Off or On | R | `AC`
-`fanspeed`1) | String | Fan speed, one of `AUTO`, `LOW`, `MIDDLE`, `HIGH` | RW | `AC`
-`fanLevel`1) | String | Fan speed, one of 3) `AUTO`, `SILENT`, `LEVEL1`, `LEVEL2`, `LEVEL3`, `LEVEL4`, `LEVEL5` | RW | `AC`
-`swing`2) | Switch | Swing on/off | RW | `AC`
-`verticalSwing`2) | String | Vertical swing state, one of 3) `OFF`, `ON`, `UP`, `MID_UP`, `MID`, `MID_DOWN`, `DOWN`, `AUTO` | RW | `AC`
-`horizontalSwing`2) | String | Horizontal swing state, one of 3) `OFF`, `ON`, `LEFT`, `MID_LEFT`, `MID`, `MID_RIGHT`, `RIGHT`, `AUTO` | RW | `AC`
-`batteryLowAlarm` | Switch | A control device in the Zone has a low battery | R | Any Zone
-`openWindowDetected` | Switch | An open window has been detected in the Zone | R | `HEATING`, `AC`
-`openWindowRemainingTime` | Number:Time | The remaining Open Window heating/cooling Override time in the Zone | R | `HEATING`, `AC`
-`light` | Switch | State (`ON`, `OFF`) of the control panel light | RW | `AC`
-
-You will see some of the above mentioned Channels only if your tado° device supports the respective function.
-
-The `RW` items are used to either override the schedule or to return to it (if `hvacMode` is set to `SCHEDULE`).
-
-1) Simpler A/C units have fan speed settings in the range [`LOW`, `MIDDLE`, `HIGH`].
-However, more sophisticated devices have settings in the range [`SILENT`, `LEVEL1`, `LEVEL2`, `LEVEL3`, `LEVEL4`].
-So you need to choose the respective channel type name that matches the features of your device.
-
-2) Simpler A/C units have a single switch type swing function that is either `ON` or `OFF`.
-However, more sophisticated devices may have either a vertical swing, a horizontal swing, or both, which could also have more complex settings.
-For example the horizontal swing function could simply be `ON` or it could have more complex settings in the range [`LEFT`, `MID_LEFT`, `MID`, `MID_RIGHT`, `RIGHT`].
-So you need to choose the respective channel type name that matches the features of your device.
-
-3) The _'one of'_ list contains all possible state values supported within the tado° binding.
-However, in reality different A/C units might only support a **_subset_** of those values.
-And indeed the subset of supported values might depend on the current state of the `acPower` and `hvacMode` channels.
-In that case, if you send a channel command value to an A/C unit which does not (currently) support that particular state value, then openHAB will report a '422' run-time error in the log.
-
-### Item Command Collection
-
-Item changes are not immediately applied, but instead collected and only when no change is done for 5 seconds (by default - see `hvacChangeDebounce` above), the combined HVAC change is sent to the server.
-This way, you can for example set a timer for 15 minutes, with target temperature 22° and mode `HEAT` in one go, without intermediate partial overrides.
-It is still fine to only change one item, like setting the target temperature to 22°, but you have the opportunity to set more items and have less defaults applied.
-
-### Default Handling
-
-To set an override, the tado° cloud API requires a full setting (`hvacMode`, `targetTemperature`, `fanspeed`, `swing`) and a termination condition (`operationMode`, `timerDuration`).
-If only some of the properties are set, the binding fills the missing pieces automatically.
-It tries to keep the current state wherever possible.
-
-If parts of the setting are missing, then the currently active zone setting is used to fill the gap. Only if the setting is not compatible with the requested change, then hard-coded defaults are applied.
-
-- `hvacMode` is set to `HEAT` for heating and hot water, and set to `COOL` for AC
-- `targetTemperature` for heating is `22°C / 72°F`, AC is set to `20°C / 68°F` and hot water to `50°C / 122°F`
-- `fanspeed` is set to first supported value, for example `AUTO`
-- `swing` is set to `OFF`, if supported
-
-If the termination condition is missing, the binding first checks if an override is active.
-If that is the case, the existing termination condition is used.
-An existing timer, for example, just keeps running.
-In case the zone is currently in smart-schedule mode and thus doesn't have a termination condition, then the default termination condition is used, as configured in the tado° app (settings -> select zone -> manual control on tado° device).
-
-## `mobiledevice` Thing
-
-The `mobiledevice` thing represents a smart phone that is configured for tado°. It provides access to the geotracking functionality.
-
-Parameter | Required | Description | Default
--|-|-|-
-`id` | yes | Mobile Device Id | -
-`refreshInterval` | no | Refresh interval of state updates in seconds | 60
-
-Mobile devices are part of discovery, but can also be configured manually.
-It is again easiest to refer to discovery in order to find the `id`.
-
-Example `tado.things`:
-
-```java
-Bridge tado:home:demo [ username="mail@example.com", password="secret" ] {
- mobiledevice phone [id=12345]
-}
-```
-
-### Items
-
-Name | Type | Description | Read/Write
--|-|-|-
-`atHome` | Switch | ON if mobile device is in HOME mode, OFF if AWAY | R
-
-Group `OR` can be used to define an item for _'is any device at home'_.
-
-# Full Example
-
-## tado.things
-
-```java
-Bridge tado:home:demo [ username="mail@example.com", password="secret" ] {
- zone heating [id=1]
- zone ac [id=2]
- zone hotwater [id=0]
-
- mobiledevice phone [id=12345]
-}
-```
-
-## tado.items
-
-```java
-Switch TADO_PRESENCE_home "Tado Presence: [MAP(presence.map):%s]" { channel="tado:home:demo:homePresence" }
-Number:Temperature HEAT_inside_temperature "Inside Temperature" { channel="tado:zone:demo:heating:currentTemperature" }
-Number HEAT_humidity "Humidity" { channel="tado:zone:demo:heating:humidity" }
-Number HEAT_heating_power "Heating Power" { channel="tado:zone:demo:heating:heatingPower" }
-String HEAT_hvac_mode "HVAC Mode" { channel="tado:zone:demo:heating:hvacMode" }
-Number:Temperature HEAT_target_temperature "Set Point" { channel="tado:zone:demo:heating:targetTemperature" }
-DateTime HEAT_overlay_expiry "Overlay Expiry" { channel="tado:zone:demo:heating:overlayExpiry" }
-Number HEAT_timer_duration "Timer Duration" { channel="tado:zone:demo:heating:timerDuration" }
-String HEAT_operation_mode "Operation Mode" { channel="tado:zone:demo:heating:operationMode" }
-
-Number:Temperature AC_inside_temperature "Inside Temperature" { channel="tado:zone:demo:ac:currentTemperature" }
-Number AC_humidity "Humidity" { channel="tado:zone:demo:ac:humidity" }
-String AC_hvac_mode "HVMode" { channel="tado:zone:demo:ac:hvacMode" }
-Number:Temperature AC_target_temperature "Set Point" { channel="tado:zone:demo:ac:targetTemperature" }
-String AC_fanspeed "Fan Speed" { channel="tado:zone:demo:ac:fanspeed" }
-Switch AC_swing "Swing" { channel="tado:zone:demo:ac:swing" }
-DateTime AC_overlay_expiry "Overlay Expiry" { channel="tado:zone:demo:ac:overlayExpiry" }
-Number AC_timer_duration "Timer Duration" { channel="tado:zone:demo:ac:timerDuration" }
-String AC_operation_mode "Operation Mode" { channel="tado:zone:demo:ac:operationMode" }
-Switch AC_power "Air-Con Power" { channel="tado:zone:demo:ac:acPower" }
-
-String DHW_hvac_mode "HVAC Mode" { channel="tado:zone:demo:hotwater:hvacMode" }
-Number:Temperature DHW_target_temperature "Set Point" { channel="tado:zone:demo:hotwater:targetTemperature" }
-DateTime DHW_overlay_expiry "Overlay Expiry" { channel="tado:zone:demo:hotwater:overlayExpiry" }
-Number DHW_timer_duration "Timer Duration" { channel="tado:zone:demo:hotwater:timerDuration" }
-String DHW_operation_mode "Operation Mode" { channel="tado:zone:demo:hotwater:operationMode" }
-
-Switch Battery_Low_Alarm "Battery Low Alarm" { channel="tado:zone:demo:heating:batteryLowAlarm" }
-
-Switch Phone_atHome "Phone location [MAP(presence.map):%s]" { channel="tado:mobiledevice:demo:phone:atHome" }
-```
-
-## tado.sitemap
-
-```perl
-sitemap tado label="Tado"
-{
- Frame label="Status" {
- Switch item=TADO_PRESENCE_home icon="presence"
- }
-
- Frame label="Heating" {
- Text item=HEAT_inside_temperature
- Text item=HEAT_humidity
- Text item=HEAT_heating_power
-
- Setpoint item=HEAT_target_temperature minValue=5 maxValue=25
- Selection item=HEAT_hvac_mode mappings=[OFF=off, HEAT=on]
- Selection item=HEAT_operation_mode mappings=[SCHEDULE=schedule, MANUAL=manual, UNTIL_CHANGE="until change", TIMER=timer]
- Setpoint item=HEAT_timer_duration minValue=5 maxValue=60 step=1
- Text item=HEAT_overlay_expiry
- }
-
- Frame label="AC" {
- Text item=AC_inside_temperature
- Text item=AC_humidity
-
- Setpoint item=AC_target_temperature minValue=16 maxValue=30
- Selection item=AC_hvac_mode mappings=[OFF=off, HEAT=heat, COOL=cool, DRY=dry, FAN=fan, AUTO=auto]
- Selection item=AC_operation_mode mappings=[SCHEDULE=schedule, MANUAL=manual, UNTIL_CHANGE="until change", TIMER=timer]
- Selection item=AC_fanspeed mappings=[AUTO=auto, LOW=low, MIDDLE=middle, HIGH=high]
- Switch item=AC_swing
- Setpoint item=AC_timer_duration minValue=5 maxValue=60 step=1
- Text item=AC_overlay_expiry
- }
-
- Frame label="Hot Water" {
- Setpoint item=DHW_target_temperature minValue=30 maxValue=65
- Selection item=DHW_hvac_mode mappings=[OFF=off, HEAT=on]
- Selection item=DHW_operation_mode mappings=[SCHEDULE=schedule, MANUAL=manual, UNTIL_CHANGE="until change", TIMER=timer]
- Setpoint item=DHW_timer_duration minValue=5 maxValue=60 step=1
- Text item=DHW_overlay_expiry
- }
-
- Frame label="Battery Low Alarm" {
- Text item=Battery_Low_Alarm valuecolor=[OFF="green", ON="red"]
- }
-
- Frame label="Mobile Devices" {
- Text item=Phone_atHome
- }
-}
-```
-
-## presence.map
-
-```text
-ON=at home
-OFF=away
-NULL=lost
-```
+# tado° Binding
+
+The tado° binding integrates devices from [tado°](https://www.tado.com).
+
+It requires a fully functional tado° installation.
+You can then monitor and control all zone types (Heating, AC, Hot Water) as well as retrieve the HOME/AWAY status of mobile devices, and setting the HOME/AWAY status of your home.
+
+**WARNING**: This binding doesn't work together with new tado X radiator thermostats, as they are using a different API (see [here](https://app.swaggerhub.com/apis/JPlenert/TadoX/0.8.0) ).
+
+## `home` Thing (the Bridge)
+
+The `home` thing serves as bridge to the tado° cloud services.
+The binding will automatically discover this thing and place it in the Inbox.
+It must be authenticated before it will actually go online.
+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
+
+Example `tado.things`
+
+```java
+Bridge tado:home:demo [ username="mail@example.com", password="secret" ]
+```
+
+Once the `home` thing is online, the binding will discover all its respective zones and mobile devices, and place them in the Inbox.
+
+### Channels
+
+Name | Type | Description | Read/Write
+-|-|-|-
+`homePresence` | String | Current presence value of the tado home. `HOME` and `AWAY` can be set | RW
+
+## `zone` Thing
+
+A _zone_ is an area/room of your home.
+You have defined them during installation.
+One zone relates to one page in the tado° mobile- or webapp.
+
+Parameter | Required | Description | Default
+-|-|-|-
+`id` | yes | Zone Id | -
+`refreshInterval` | no | Refresh interval of state updates in seconds | 30
+`hvacChangeDebounce` | no | Duration in seconds to combine multiple HVAC changes into one | 5
+
+Zones can either be added through discovery or manually. Following up on the above example, a zone configuration could look like this:
+
+Example `tado.things`
+
+```java
+Bridge tado:home:demo [ username="mail@example.com", password="secret" ] {
+ zone heating [id=1]
+ zone ac [id=2]
+ zone hotwater [id=0]
+}
+```
+
+Zone id and name can be found in discovery results.
+
+### Channels
+
+A zone is either of type `HEATING`, `AC` or `DHW` (domestic hot water).
+The availability of items as well as their allowed values depend on type and capabilities of the HVAC setup.
+If you are unsure, have a look at the tado° app and see if the functionality is available and what values are supported.
+
+Name | Type | Description | Read/Write | Zone type
+-|-|-|-|-
+`currentTemperature` | Number:Temperature | Current inside temperature | R | `HEATING`, `AC`
+`humidity` | Number:Dimensionless | Current relative inside humidity in percent | R | `HEATING`, `AC`
+`hvacMode` | String | Active mode, one of `OFF`, `HEAT`, `COOL`, `DRY`, `FAN`, `AUTO` | RW | `HEATING` and `DHW` support `OFF` and `HEAT`, `AC` can support more
+`targetTemperature` | Number:Temperature | Set point | RW | `HEATING`, `AC`, `DHW`
+`operationMode` | String | Operation mode the zone is currently in. One of `SCHEDULE` (follow smart schedule), `MANUAL` (override until ended manually), `TIMER` (override for a given time), `UNTIL_CHANGE` (active until next smart schedule block or until AWAY mode becomes active) | RW | `HEATING`, `AC`, `DHW`
+`overlayExpiry` | DateTime | End date and time of a timer | R | `HEATING`, `AC`, `DHW`
+`timerDuration` | Number | Timer duration in minutes | RW | `HEATING`, `AC`, `DHW`
+`heatingPower` | Number:Dimensionless | Amount of heating power currently present | R | `HEATING`
+`acPower` | Switch | Indicates if the Air-Conditioning is Off or On | R | `AC`
+`fanspeed`1) | String | Fan speed, one of `AUTO`, `LOW`, `MIDDLE`, `HIGH` | RW | `AC`
+`fanLevel`1) | String | Fan speed, one of 3) `AUTO`, `SILENT`, `LEVEL1`, `LEVEL2`, `LEVEL3`, `LEVEL4`, `LEVEL5` | RW | `AC`
+`swing`2) | Switch | Swing on/off | RW | `AC`
+`verticalSwing`2) | String | Vertical swing state, one of 3) `OFF`, `ON`, `UP`, `MID_UP`, `MID`, `MID_DOWN`, `DOWN`, `AUTO` | RW | `AC`
+`horizontalSwing`2) | String | Horizontal swing state, one of 3) `OFF`, `ON`, `LEFT`, `MID_LEFT`, `MID`, `MID_RIGHT`, `RIGHT`, `AUTO` | RW | `AC`
+`batteryLowAlarm` | Switch | A control device in the Zone has a low battery | R | Any Zone
+`openWindowDetected` | Switch | An open window has been detected in the Zone | R | `HEATING`, `AC`
+`openWindowRemainingTime` | Number:Time | The remaining Open Window heating/cooling Override time in the Zone | R | `HEATING`, `AC`
+`light` | Switch | State (`ON`, `OFF`) of the control panel light | RW | `AC`
+
+You will see some of the above mentioned Channels only if your tado° device supports the respective function.
+
+The `RW` items are used to either override the schedule or to return to it (if `hvacMode` is set to `SCHEDULE`).
+
+1) Simpler A/C units have fan speed settings in the range [`LOW`, `MIDDLE`, `HIGH`].
+However, more sophisticated devices have settings in the range [`SILENT`, `LEVEL1`, `LEVEL2`, `LEVEL3`, `LEVEL4`].
+So you need to choose the respective channel type name that matches the features of your device.
+
+2) Simpler A/C units have a single switch type swing function that is either `ON` or `OFF`.
+However, more sophisticated devices may have either a vertical swing, a horizontal swing, or both, which could also have more complex settings.
+For example the horizontal swing function could simply be `ON` or it could have more complex settings in the range [`LEFT`, `MID_LEFT`, `MID`, `MID_RIGHT`, `RIGHT`].
+So you need to choose the respective channel type name that matches the features of your device.
+
+3) The _'one of'_ list contains all possible state values supported within the tado° binding.
+However, in reality different A/C units might only support a **_subset_** of those values.
+And indeed the subset of supported values might depend on the current state of the `acPower` and `hvacMode` channels.
+In that case, if you send a channel command value to an A/C unit which does not (currently) support that particular state value, then openHAB will report a '422' run-time error in the log.
+
+### Item Command Collection
+
+Item changes are not immediately applied, but instead collected and only when no change is done for 5 seconds (by default - see `hvacChangeDebounce` above), the combined HVAC change is sent to the server.
+This way, you can for example set a timer for 15 minutes, with target temperature 22° and mode `HEAT` in one go, without intermediate partial overrides.
+It is still fine to only change one item, like setting the target temperature to 22°, but you have the opportunity to set more items and have less defaults applied.
+
+### Default Handling
+
+To set an override, the tado° cloud API requires a full setting (`hvacMode`, `targetTemperature`, `fanspeed`, `swing`) and a termination condition (`operationMode`, `timerDuration`).
+If only some of the properties are set, the binding fills the missing pieces automatically.
+It tries to keep the current state wherever possible.
+
+If parts of the setting are missing, then the currently active zone setting is used to fill the gap. Only if the setting is not compatible with the requested change, then hard-coded defaults are applied.
+
+- `hvacMode` is set to `HEAT` for heating and hot water, and set to `COOL` for AC
+- `targetTemperature` for heating is `22°C / 72°F`, AC is set to `20°C / 68°F` and hot water to `50°C / 122°F`
+- `fanspeed` is set to first supported value, for example `AUTO`
+- `swing` is set to `OFF`, if supported
+
+If the termination condition is missing, the binding first checks if an override is active.
+If that is the case, the existing termination condition is used.
+An existing timer, for example, just keeps running.
+In case the zone is currently in smart-schedule mode and thus doesn't have a termination condition, then the default termination condition is used, as configured in the tado° app (settings -> select zone -> manual control on tado° device).
+
+## `mobiledevice` Thing
+
+The `mobiledevice` thing represents a smart phone that is configured for tado°. It provides access to the geotracking functionality.
+
+Parameter | Required | Description | Default
+-|-|-|-
+`id` | yes | Mobile Device Id | -
+`refreshInterval` | no | Refresh interval of state updates in seconds | 60
+
+Mobile devices are part of discovery, but can also be configured manually.
+It is again easiest to refer to discovery in order to find the `id`.
+
+Example `tado.things`:
+
+```java
+Bridge tado:home:demo [ username="mail@example.com", password="secret" ] {
+ mobiledevice phone [id=12345]
+}
+```
+
+### Items
+
+Name | Type | Description | Read/Write
+-|-|-|-
+`atHome` | Switch | ON if mobile device is in HOME mode, OFF if AWAY | R
+
+Group `OR` can be used to define an item for _'is any device at home'_.
+
+# Full Example
+
+## tado.things
+
+```java
+Bridge tado:home:demo [ username="mail@example.com", password="secret" ] {
+ zone heating [id=1]
+ zone ac [id=2]
+ zone hotwater [id=0]
+
+ mobiledevice phone [id=12345]
+}
+```
+
+## tado.items
+
+```java
+Switch TADO_PRESENCE_home "Tado Presence: [MAP(presence.map):%s]" { channel="tado:home:demo:homePresence" }
+Number:Temperature HEAT_inside_temperature "Inside Temperature" { channel="tado:zone:demo:heating:currentTemperature" }
+Number HEAT_humidity "Humidity" { channel="tado:zone:demo:heating:humidity" }
+Number HEAT_heating_power "Heating Power" { channel="tado:zone:demo:heating:heatingPower" }
+String HEAT_hvac_mode "HVAC Mode" { channel="tado:zone:demo:heating:hvacMode" }
+Number:Temperature HEAT_target_temperature "Set Point" { channel="tado:zone:demo:heating:targetTemperature" }
+DateTime HEAT_overlay_expiry "Overlay Expiry" { channel="tado:zone:demo:heating:overlayExpiry" }
+Number HEAT_timer_duration "Timer Duration" { channel="tado:zone:demo:heating:timerDuration" }
+String HEAT_operation_mode "Operation Mode" { channel="tado:zone:demo:heating:operationMode" }
+
+Number:Temperature AC_inside_temperature "Inside Temperature" { channel="tado:zone:demo:ac:currentTemperature" }
+Number AC_humidity "Humidity" { channel="tado:zone:demo:ac:humidity" }
+String AC_hvac_mode "HVMode" { channel="tado:zone:demo:ac:hvacMode" }
+Number:Temperature AC_target_temperature "Set Point" { channel="tado:zone:demo:ac:targetTemperature" }
+String AC_fanspeed "Fan Speed" { channel="tado:zone:demo:ac:fanspeed" }
+Switch AC_swing "Swing" { channel="tado:zone:demo:ac:swing" }
+DateTime AC_overlay_expiry "Overlay Expiry" { channel="tado:zone:demo:ac:overlayExpiry" }
+Number AC_timer_duration "Timer Duration" { channel="tado:zone:demo:ac:timerDuration" }
+String AC_operation_mode "Operation Mode" { channel="tado:zone:demo:ac:operationMode" }
+Switch AC_power "Air-Con Power" { channel="tado:zone:demo:ac:acPower" }
+
+String DHW_hvac_mode "HVAC Mode" { channel="tado:zone:demo:hotwater:hvacMode" }
+Number:Temperature DHW_target_temperature "Set Point" { channel="tado:zone:demo:hotwater:targetTemperature" }
+DateTime DHW_overlay_expiry "Overlay Expiry" { channel="tado:zone:demo:hotwater:overlayExpiry" }
+Number DHW_timer_duration "Timer Duration" { channel="tado:zone:demo:hotwater:timerDuration" }
+String DHW_operation_mode "Operation Mode" { channel="tado:zone:demo:hotwater:operationMode" }
+
+Switch Battery_Low_Alarm "Battery Low Alarm" { channel="tado:zone:demo:heating:batteryLowAlarm" }
+
+Switch Phone_atHome "Phone location [MAP(presence.map):%s]" { channel="tado:mobiledevice:demo:phone:atHome" }
+```
+
+## tado.sitemap
+
+```perl
+sitemap tado label="Tado"
+{
+ Frame label="Status" {
+ Switch item=TADO_PRESENCE_home icon="presence"
+ }
+
+ Frame label="Heating" {
+ Text item=HEAT_inside_temperature
+ Text item=HEAT_humidity
+ Text item=HEAT_heating_power
+
+ Setpoint item=HEAT_target_temperature minValue=5 maxValue=25
+ Selection item=HEAT_hvac_mode mappings=[OFF=off, HEAT=on]
+ Selection item=HEAT_operation_mode mappings=[SCHEDULE=schedule, MANUAL=manual, UNTIL_CHANGE="until change", TIMER=timer]
+ Setpoint item=HEAT_timer_duration minValue=5 maxValue=60 step=1
+ Text item=HEAT_overlay_expiry
+ }
+
+ Frame label="AC" {
+ Text item=AC_inside_temperature
+ Text item=AC_humidity
+
+ Setpoint item=AC_target_temperature minValue=16 maxValue=30
+ Selection item=AC_hvac_mode mappings=[OFF=off, HEAT=heat, COOL=cool, DRY=dry, FAN=fan, AUTO=auto]
+ Selection item=AC_operation_mode mappings=[SCHEDULE=schedule, MANUAL=manual, UNTIL_CHANGE="until change", TIMER=timer]
+ Selection item=AC_fanspeed mappings=[AUTO=auto, LOW=low, MIDDLE=middle, HIGH=high]
+ Switch item=AC_swing
+ Setpoint item=AC_timer_duration minValue=5 maxValue=60 step=1
+ Text item=AC_overlay_expiry
+ }
+
+ Frame label="Hot Water" {
+ Setpoint item=DHW_target_temperature minValue=30 maxValue=65
+ Selection item=DHW_hvac_mode mappings=[OFF=off, HEAT=on]
+ Selection item=DHW_operation_mode mappings=[SCHEDULE=schedule, MANUAL=manual, UNTIL_CHANGE="until change", TIMER=timer]
+ Setpoint item=DHW_timer_duration minValue=5 maxValue=60 step=1
+ Text item=DHW_overlay_expiry
+ }
+
+ Frame label="Battery Low Alarm" {
+ Text item=Battery_Low_Alarm valuecolor=[OFF="green", ON="red"]
+ }
+
+ Frame label="Mobile Devices" {
+ Text item=Phone_atHome
+ }
+}
+```
+
+## presence.map
+
+```text
+ON=at home
+OFF=away
+NULL=lost
+```
diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/api/HomeApiFactory.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/api/HomeApiFactory.java
index 7ebc1dcb1f4ae..91240b6857bc4 100644
--- a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/api/HomeApiFactory.java
+++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/api/HomeApiFactory.java
@@ -13,10 +13,12 @@
package org.openhab.binding.tado.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.tado.internal.auth.OAuthorizerV2;
import org.openhab.binding.tado.swagger.codegen.api.GsonBuilderFactory;
import org.openhab.binding.tado.swagger.codegen.api.auth.Authorizer;
import org.openhab.binding.tado.swagger.codegen.api.auth.OAuthAuthorizer;
import org.openhab.binding.tado.swagger.codegen.api.client.HomeApi;
+import org.openhab.core.auth.client.oauth2.OAuthClientService;
import com.google.gson.Gson;
@@ -24,6 +26,7 @@
* Factory to create and configure {@link HomeApi} instances.
*
* @author Dennis Frommknecht - Initial contribution
+ * @author Andrew Fiddian-Green - Use OAuthAuthorizerV2
*/
@NonNullByDefault
public class HomeApiFactory {
@@ -37,4 +40,10 @@ public HomeApi create(String username, String password) {
.clientSecret(OAUTH_CLIENT_SECRET).scopes(OAUTH_SCOPE);
return new HomeApi(gson, authorizer);
}
+
+ public HomeApi create(OAuthClientService oAuthClientService) {
+ Gson gson = GsonBuilderFactory.defaultGsonBuilder().create();
+ Authorizer authorizer = new OAuthorizerV2(oAuthClientService);
+ return new HomeApi(gson, authorizer);
+ }
}
diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/auth/OAuthorizerV2.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/auth/OAuthorizerV2.java
new file mode 100644
index 0000000000000..0a52197b840ef
--- /dev/null
+++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/auth/OAuthorizerV2.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2010-2025 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.tado.internal.auth;
+
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpHeader;
+import org.openhab.binding.tado.swagger.codegen.api.auth.Authorizer;
+import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
+import org.openhab.core.auth.client.oauth2.DeviceCodeResponse;
+import org.openhab.core.auth.client.oauth2.OAuthClientService;
+import org.openhab.core.auth.client.oauth2.OAuthException;
+import org.openhab.core.auth.client.oauth2.OAuthResponseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a new {@link Authorizer} that is mandated by Tado after March 15 2025.
+ *
+ * @see Tado Support
+ * Article
+ * @see RFC-8628
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+public class OAuthorizerV2 implements Authorizer {
+
+ private final Logger logger = LoggerFactory.getLogger(OAuthorizerV2.class);
+
+ private final OAuthClientService oAuthService;
+
+ public OAuthorizerV2(OAuthClientService oAuthService) {
+ this.oAuthService = oAuthService;
+ }
+
+ @Override
+ public void addAuthorization(Request request) {
+ try {
+ AccessTokenResponse token = oAuthService.getAccessTokenResponse();
+ if (token != null) {
+ request.header(HttpHeader.AUTHORIZATION,
+ String.format("%s %s", token.getTokenType(), token.getAccessToken()));
+ return;
+ }
+ } catch (OAuthException | IOException | OAuthResponseException e) {
+ logger.debug("addAuthorization() => getAccessTokenResponse() error: {}", e.getMessage(), e);
+ }
+ }
+
+ public @Nullable DeviceCodeResponse getDeviceCodeResponse() throws OAuthException {
+ return oAuthService.getDeviceCodeResponse();
+ }
+
+ public @Nullable AccessTokenResponse getAccessTokenResponse()
+ throws OAuthException, IOException, OAuthResponseException {
+ return oAuthService.getAccessTokenResponse();
+ }
+}
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 96c80f0447525..dd8b666764565 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
@@ -13,6 +13,7 @@
package org.openhab.binding.tado.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
/**
* Holder-object for home configuration
@@ -21,6 +22,7 @@
*/
@NonNullByDefault
public class TadoHomeConfig {
- public String username = "";
- public String password = "";
+ public @Nullable String username;
+ public @Nullable String password;
+ public @Nullable Boolean useRfc8628;
}
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 ecd3533218ced..f7304bffc88f1 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
@@ -22,6 +22,7 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.discovery.TadoDiscoveryService;
+import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
@@ -34,6 +35,7 @@
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.http.HttpService;
/**
* The {@link TadoHandlerFactory} is responsible for creating things and thing
@@ -51,10 +53,15 @@ public class TadoHandlerFactory extends BaseThingHandlerFactory {
private final Map> discoveryServiceRegs = new HashMap<>();
private final TadoStateDescriptionProvider stateDescriptionProvider;
+ private final HttpService httpService;
+ private final OAuthFactory oAuthFactory;
@Activate
- public TadoHandlerFactory(final @Reference TadoStateDescriptionProvider stateDescriptionProvider) {
+ public TadoHandlerFactory(@Reference TadoStateDescriptionProvider stateDescriptionProvider,
+ @Reference HttpService httpService, @Reference OAuthFactory oAuthFactory) {
this.stateDescriptionProvider = stateDescriptionProvider;
+ this.httpService = httpService;
+ this.oAuthFactory = oAuthFactory;
}
@Override
@@ -67,7 +74,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_HOME)) {
- TadoHomeHandler tadoHomeHandler = new TadoHomeHandler((Bridge) thing);
+ TadoHomeHandler tadoHomeHandler = new TadoHomeHandler((Bridge) thing, httpService, oAuthFactory);
registerTadoDiscoveryService(tadoHomeHandler);
return tadoHomeHandler;
} else if (thingTypeUID.equals(THING_TYPE_ZONE)) {
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 77c2ca0463ecb..d346bdadfb386 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
@@ -13,16 +13,21 @@
package org.openhab.binding.tado.internal.handler;
import java.io.IOException;
+import java.time.ZonedDateTime;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletException;
+
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.TadoBindingConstants;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
import org.openhab.binding.tado.internal.api.HomeApiFactory;
import org.openhab.binding.tado.internal.config.TadoHomeConfig;
+import org.openhab.binding.tado.internal.servlet.TadoAuthenticationServlet;
import org.openhab.binding.tado.swagger.codegen.api.ApiException;
import org.openhab.binding.tado.swagger.codegen.api.client.HomeApi;
import org.openhab.binding.tado.swagger.codegen.api.model.HomeInfo;
@@ -31,6 +36,10 @@
import org.openhab.binding.tado.swagger.codegen.api.model.PresenceState;
import org.openhab.binding.tado.swagger.codegen.api.model.User;
import org.openhab.binding.tado.swagger.codegen.api.model.UserHomes;
+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.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
@@ -41,6 +50,8 @@
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -50,22 +61,37 @@
* @author Dennis Frommknecht - Initial contribution
*/
@NonNullByDefault
-public class TadoHomeHandler extends BaseBridgeHandler {
+public class TadoHomeHandler extends BaseBridgeHandler implements AccessTokenRefreshListener {
- private Logger logger = LoggerFactory.getLogger(TadoHomeHandler.class);
+ private static final ZonedDateTime AUTH_V2_FROM_DATE = ZonedDateTime.parse("2025-03-15T00:00:00Z");
- private TadoHomeConfig configuration;
- private final HomeApi api;
+ private static final String DEVICE_URL = "https://login.tado.com/oauth2/device_authorize";
+ private static final String TOKEN_URL = "https://login.tado.com/oauth2/token";
+ private static final String CLIENT_ID = "1bb50063-6b0c-4d11-bd99-387f4a91cc46";
+ private static final String SCOPE = "offline_access";
+
+ private final Logger logger = LoggerFactory.getLogger(TadoHomeHandler.class);
- private @Nullable Long homeId;
private final TadoBatteryChecker batteryChecker;
+ private final HttpService httpService;
+ private final TadoAuthenticationServlet httpServlet;
+ private final OAuthFactory oAuthFactory;
+
+ private @NonNullByDefault({}) TadoHomeConfig configuration;
+ private @NonNullByDefault({}) String offlinePrompt;
+ private @NonNullByDefault({}) HomeApi api;
+
+ private @Nullable Long homeId;
private @Nullable ScheduledFuture> initializationFuture;
+ private @Nullable OAuthClientService oAuthClientService;
- public TadoHomeHandler(Bridge bridge) {
+ public TadoHomeHandler(Bridge bridge, HttpService httpService, OAuthFactory oAuthFactory) {
super(bridge);
- batteryChecker = new TadoBatteryChecker(this);
- configuration = getConfigAs(TadoHomeConfig.class);
- api = new HomeApiFactory().create(configuration.username, configuration.password);
+ this.batteryChecker = new TadoBatteryChecker(this);
+ this.configuration = getConfigAs(TadoHomeConfig.class);
+ this.httpService = httpService;
+ this.httpServlet = new TadoAuthenticationServlet(this);
+ this.oAuthFactory = oAuthFactory;
}
public TemperatureUnit getTemperatureUnit() {
@@ -77,6 +103,42 @@ public TemperatureUnit getTemperatureUnit() {
@Override
public void initialize() {
configuration = getConfigAs(TadoHomeConfig.class);
+
+ String userName = configuration.username;
+ String password = configuration.password;
+ boolean v1CredentialsOk = userName != null && !userName.isBlank() && password != null && !password.isBlank();
+
+ boolean suggestRfc8628 = false;
+ suggestRfc8628 |= Boolean.TRUE.equals(configuration.useRfc8628);
+ suggestRfc8628 |= !v1CredentialsOk;
+ suggestRfc8628 |= ZonedDateTime.now().isAfter(AUTH_V2_FROM_DATE);
+
+ if (suggestRfc8628) {
+ String url = "http(s)://:" + TadoAuthenticationServlet.PATH;
+ offlinePrompt = String.format("@text/tado.home.status.oauth [\"%s\"]", url);
+
+ OAuthClientService oAuthService = oAuthFactory.getOAuthClientService(thing.getUID().toString());
+ if (oAuthService == null) {
+ oAuthService = oAuthFactory.createOAuthClientService(thing.getUID().getAsString(), TOKEN_URL,
+ DEVICE_URL, CLIENT_ID, null, SCOPE, false);
+ }
+ oAuthService.addAccessTokenRefreshListener(this);
+ oAuthClientService = oAuthService;
+
+ api = new HomeApiFactory().create(oAuthService);
+ logger.trace("initialize() api v2 created");
+ } else {
+ offlinePrompt = "@text/tado.home.status.username";
+ api = new HomeApiFactory().create(Objects.requireNonNull(userName), Objects.requireNonNull(password));
+ logger.trace("initialize() api v1 created");
+ }
+
+ try {
+ httpService.registerServlet(TadoAuthenticationServlet.PATH, httpServlet, null, null);
+ } catch (ServletException | NamespaceException e) {
+ logger.warn("initialize() failed to register servlet", e);
+ }
+
ScheduledFuture> initializationFuture = this.initializationFuture;
if (initializationFuture == null || initializationFuture.isDone()) {
this.initializationFuture = scheduler.scheduleWithFixedDelay(
@@ -84,7 +146,7 @@ public void initialize() {
}
}
- private void initializeBridgeStatusAndPropertiesIfOffline() {
+ private synchronized void initializeBridgeStatusAndPropertiesIfOffline() {
if (getThing().getStatus() == ThingStatus.ONLINE) {
for (Thing thing : getThing().getThings()) {
ThingHandler handler = thing.getHandler();
@@ -101,21 +163,21 @@ private void initializeBridgeStatusAndPropertiesIfOffline() {
// Get user info to verify successful authentication and connection to server
User user = api.showUser();
if (user == null) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "Cannot connect to server. Username and/or password might be invalid");
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, offlinePrompt);
return;
}
List homes = user.getHomes();
if (homes == null || homes.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "User does not have access to any home");
+ "@text/tado.home.status.nohome");
return;
}
Integer firstHomeId = homes.get(0).getId();
if (firstHomeId == null) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Missing Home Id");
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ "@text/tado.home.status.nohomeid");
return;
}
@@ -128,9 +190,8 @@ private void initializeBridgeStatusAndPropertiesIfOffline() {
.getTemperatureUnit() ? TemperatureUnit.FAHRENHEIT : TemperatureUnit.CELSIUS;
updateProperty(TadoBindingConstants.PROPERTY_HOME_TEMPERATURE_UNIT, temperatureUnit.name());
} catch (IOException | ApiException e) {
- logger.debug("Error accessing tado server: {}", e.getMessage(), e);
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
- "Could not connect to server due to " + e.getMessage());
+ logger.debug("Error accessing tado server: {}", e.getMessage());
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, offlinePrompt);
return;
}
@@ -140,6 +201,10 @@ private void initializeBridgeStatusAndPropertiesIfOffline() {
@Override
public void dispose() {
super.dispose();
+ OAuthClientService service = oAuthClientService;
+ if (service != null) {
+ service.removeAccessTokenRefreshListener(this);
+ }
ScheduledFuture> initializationFuture = this.initializationFuture;
if (initializationFuture != null && !initializationFuture.isCancelled()) {
initializationFuture.cancel(true);
@@ -195,4 +260,9 @@ public void handleCommand(ChannelUID channelUID, Command command) {
public TadoBatteryChecker getBatteryChecker() {
return this.batteryChecker;
}
+
+ @Override
+ public void onAccessTokenResponse(AccessTokenResponse atr) {
+ initializeBridgeStatusAndPropertiesIfOffline();
+ }
}
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
new file mode 100644
index 0000000000000..b70afb3571126
--- /dev/null
+++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/internal/servlet/TadoAuthenticationServlet.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2010-2025 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.tado.internal.servlet;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.MediaType;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.tado.internal.auth.OAuthorizerV2;
+import org.openhab.binding.tado.internal.handler.TadoHomeHandler;
+import org.openhab.binding.tado.swagger.codegen.api.auth.Authorizer;
+import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
+import org.openhab.core.auth.client.oauth2.DeviceCodeResponse;
+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.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public class TadoAuthenticationServlet extends HttpServlet {
+ private static final long serialVersionUID = 19530226123451L;
+
+ public static final String PATH = "/tado";
+
+ private static final String REPLACE_TAG = "$REPLACE$";
+
+ private static final String HTML_AUTH_PASSED = "authenicated";
+ private static final String HTML_AUTH_NOT_REQUIRED = "authentication not required";
+ 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";
+
+ private static final String ERROR_BAD_URL = "no verification uri";
+
+ private static final String HTML_STATUS_PAGE_TEMPLATE = """
+
+
+
tado°
+
Status: $REPLACE$
+
+
+ """;
+
+ private final TadoHomeHandler tadoHomeHandler;
+
+ public TadoAuthenticationServlet(TadoHomeHandler tadoHomeHandler) {
+ this.tadoHomeHandler = tadoHomeHandler;
+ }
+
+ /**
+ * Fulfils HTTP GET requests.
+ *
+ * Depending on whether the request has a query string, the behavior differs as follows:
+ *
+ *
+ *
If there is no query string, it serves the status page
+ *
If there is a given query string, it serves the user authentication page
+ *
+ */
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ OAuthorizerV2 authorizerV2 = tadoHomeHandler.getApi().getAuthorizerV2();
+
+ if (request.getQueryString() == null) {
+ serveStatusPage(request, response, authorizerV2);
+ } else
+
+ // if the query string is '?oauth=start' then serve the user authentication page
+ if (PARAM_VALUE.equals(request.getParameter(PARAM_NAME))) {
+ if (authorizerV2 == null) {
+ serveStatusPage(request, response, authorizerV2);
+ } else {
+ serveUserAuthenticationPage(response, authorizerV2);
+ }
+ }
+ }
+
+ /**
+ * Serves the status page.
+ *
+ * Depending on the type of {@link Authorizer} it serves three flavours of page:
+ *
+ *
+ *
A page saying that authentication is not required
+ *
A page saying that authentication is already done
+ *
A page saying that authentication is required with a link to start the process
+ *
+ *
+ * @throws IOException
+ */
+ private void serveStatusPage(HttpServletRequest request, HttpServletResponse response,
+ @Nullable OAuthorizerV2 authorizerV2) throws IOException {
+ String dynamicHtml = null;
+
+ if (authorizerV2 == null) {
+ dynamicHtml = HTML_AUTH_NOT_REQUIRED;
+ }
+
+ if (dynamicHtml == null) {
+ try {
+ AccessTokenResponse accessToken = Objects.requireNonNull(authorizerV2).getAccessTokenResponse();
+ if (accessToken != null) {
+ dynamicHtml = HTML_AUTH_PASSED;
+ }
+ } catch (OAuthException | IOException | OAuthResponseException e) {
+ dynamicHtml = HTML_AUTH_ERROR_TEMPLATE.replace(REPLACE_TAG,
+ e.getMessage() instanceof String exception ? exception : e.getClass().getName());
+ }
+ }
+
+ if (dynamicHtml == null) {
+ if (request.getRequestURL() instanceof StringBuffer baseUrl) {
+ String dynamicUrl = baseUrl.append("?").append(PARAM_NAME).append("=").append(PARAM_VALUE).toString();
+ dynamicHtml = HTML_AUTH_START_TEMPLATE.replace(REPLACE_TAG, dynamicUrl);
+ } else {
+ dynamicHtml = HTML_AUTH_ERROR_TEMPLATE.replace(REPLACE_TAG, ERROR_BAD_URL);
+ }
+ }
+
+ String content = HTML_STATUS_PAGE_TEMPLATE.replace(REPLACE_TAG, dynamicHtml);
+
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType(MediaType.TEXT_HTML);
+ response.getWriter().write(content);
+ }
+
+ /**
+ * Serves the user authentication page.
+ *
+ * Tries to get the Tado remote RFC-8628 Authentication User URI from the authentication
+ * service, which if valid is opened via an HTTP redirect. Or if the URI is empty, or the
+ * authentication failed, it serves a local error page instead.
+ *
+ * @throws IOException
+ */
+ private void serveUserAuthenticationPage(HttpServletResponse response, OAuthorizerV2 authorizerV2)
+ throws IOException {
+ String dynamicHtml = null;
+
+ try {
+ DeviceCodeResponse deviceCodeResponse = authorizerV2.getDeviceCodeResponse();
+ String userVerificationUri = deviceCodeResponse.getVerificationUriComplete();
+ if (userVerificationUri != null && !userVerificationUri.isBlank()) {
+ response.sendRedirect(userVerificationUri);
+ } else {
+ dynamicHtml = HTML_AUTH_ERROR_TEMPLATE.replace(REPLACE_TAG, ERROR_BAD_URL);
+ }
+ } catch (OAuthException e) {
+ dynamicHtml = HTML_AUTH_ERROR_TEMPLATE.replace(REPLACE_TAG,
+ e.getMessage() instanceof String message ? message : e.getClass().getName());
+ }
+
+ if (dynamicHtml != null) {
+ String content = HTML_STATUS_PAGE_TEMPLATE.replace(REPLACE_TAG, dynamicHtml);
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType(MediaType.TEXT_HTML);
+ response.getWriter().write(content);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/swagger/codegen/api/ApiException.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/swagger/codegen/api/ApiException.java
index a2b31b5777122..84ac501f4e620 100644
--- a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/swagger/codegen/api/ApiException.java
+++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/swagger/codegen/api/ApiException.java
@@ -36,6 +36,10 @@ public ApiException(String message) {
super(message);
}
+ public ApiException(String message, Throwable throwable) {
+ super(message, throwable);
+ }
+
public ApiException(ContentResponse response, String message, Throwable throwable) {
super(message, throwable);
this.code = response.getStatus();
diff --git a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/swagger/codegen/api/client/HomeApi.java b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/swagger/codegen/api/client/HomeApi.java
index 94ca66573fcb0..742da8667a2b9 100644
--- a/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/swagger/codegen/api/client/HomeApi.java
+++ b/bundles/org.openhab.binding.tado/src/main/java/org/openhab/binding/tado/swagger/codegen/api/client/HomeApi.java
@@ -17,6 +17,7 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
+import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
@@ -25,6 +26,7 @@
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.openhab.binding.tado.internal.auth.OAuthorizerV2;
import org.openhab.binding.tado.swagger.codegen.api.ApiException;
import org.openhab.binding.tado.swagger.codegen.api.auth.Authorizer;
import org.openhab.binding.tado.swagger.codegen.api.model.GenericZoneCapabilities;
@@ -614,4 +616,8 @@ private static void startHttpClient(HttpClient client) {
}
}
}
+
+ public @Nullable OAuthorizerV2 getAuthorizerV2() {
+ return authorizer instanceof OAuthorizerV2 v2 ? v2 : null;
+ }
}
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 defbd588d3e50..adf8b766982b6 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,117 +1,126 @@
-# 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.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.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
-
-# 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.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.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 a6b383a813afa..7df199103117a 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
@@ -14,15 +14,22 @@
-
+
+
+ Determines if the binding shall use oAuth RFC-8628 authentication
+
+
+ User name of tado login used for API access
+ true
-
+ Password of tado login used for API accesspassword
+ true