Repository: XKNX/xknx
Pull request #475 (2020-10-22 20:41:29)

BinarySensor `ignore_internal_state` only on GroupValueWrite
BinarySensor:
- `ignore_internal_state` defaults to False again (like HA config default)
- `ignore_internal_state` and counter are only applied to GroupValueWrite telegrams, not GroupValueRespond.
- if `context_timeout` is set `ignore_internal_state` is set to True.


Patch:
diff --git a/changelog.md b/changelog.md
index 2d972af4..b3ef0a15 100644
--- a/changelog.md
+++ b/changelog.md
@@ -5,6 +5,8 @@
 ### Devices
 
 - BinarySensor: added option to invert payloads
+- BinarySensor: `ignore_internal_state` and counter are only applied to GroupValueWrite telegrams, not GroupValueRespond.
+- BinarySensor: if `context_timeout` is set `ignore_internal_state` is set to True.
 - Switch: added option to invert payloads
 
 ### Bugfixes
diff --git a/docs/binary_sensor.md b/docs/binary_sensor.md
index ed523784..e38bdccc 100644
--- a/docs/binary_sensor.md
+++ b/docs/binary_sensor.md
@@ -19,15 +19,18 @@ The logic within switches can further handle if a button is pressed once or twic
 binarysensor = BinarySensor(xknx, 'TestInput', group_address_state='1/2/3', device_class='motion')
 ```
 
+## [](#header-2)Interface
+
 - `xknx` is the XKNX object.
 - `name` is the name of the object.
 - `group_address_state` is the KNX group address of the sensor device.
 - `invert` inverts the payload so state "on" is represented by 0 on bus and "off" by 1. Defaults to `False`
 - `sync_state` defines if the value should be actively read from the bus. If `False` no GroupValueRead telegrams will be sent to its group address. Defaults to `True`
-- `ignore_internal_state` allows callback call regardless of the current binary sensor state. Defaults to `True`
-- `context_timeout` time in seconds telegrams should be counted towards the current context to increment the counter. To be used with `ignore_internal_state=True`. Defaults to `None`
+- `ignore_internal_state` allows callback call regardless of the current binary sensor state. Defaults to `False`
+- `context_timeout` time in seconds telegrams should be counted towards the current context to increment the counter. If set `ignore_internal_state` is set `True`. Defaults to `None`
 - `reset_after` may be used to reset the internal state to `OFF` again after given time in sec. Defaults to `None`
-- `device_class` may be used to store the type of sensor, e.g. "motion" for motion detectors.
+- `device_class` may be used to store the type of sensor, e.g. "motion" for motion detectors for Home-Assistant.
+- `device_updated_cb` awaitable callback for each update.
 
 ## [](#header-2)Example
 
diff --git a/docs/climate.md b/docs/climate.md
index 74820746..51eb550e 100644
--- a/docs/climate.md
+++ b/docs/climate.md
@@ -11,61 +11,16 @@ nav_order: 2
 
 Climate are representations of KNX HVAC/Climate controls.
 
-## [](#header-2)Example
-
-```python
-climate_setpoint_shift = Climate(
-    xknx,
-    'TestClimateSPS',
-    group_address_temperature='1/2/2',
-    group_address_target_temperature_state='1/2/5',
-    group_address_setpoint_shift='1/2/3',
-    group_address_setpoint_shift_state='1/2/4'
-)
-
-climate_target_temp = Climate(
-    xknx,
-    'TestClimateTT',
-    group_address_temperature='2/2/2',
-    group_address_target_temperature='2/2/3',
-    group_address_target_temperature_state='2/2/4'
-)
-```
-
-## [](#header-2)Configuration via **xknx.yaml**
-
-Climate devices are usually configured via [`xknx.yaml`](/configuration):
-
-```yaml
-groups:
-  climate:
-    Children.Climate:
-      {
-        group_address_temperature: "1/7/2",
-        group_address_setpoint_shift: "1/7/3",
-        group_address_target_temperature_state: "1/7/4",
-      }
-    Office.Climate:
-      {
-        group_address_temperature: "1/7/5",
-        group_address_operation_mode: "1/7/6",
-      }
-    Attic.Climate:
-      {
-        group_address_temperature: "1/7/7",
-        group_address_operation_mode_protection: "1/7/8",
-        group_address_operation_mode_night: "1/7/9",
-        group_address_operation_mode_comfort: "1/7/10",
-      }
-```
-
 ## [](#header-2)Interface
 
+- **xknx** is the XKNX object.
+- **name** is the name of the object.
 - **group_address_temperature** KNX address of current room temperature.
 - **group_address_target_temperature** KNX address for setting the target temperature if setpoint shift is not supported.
 - **group_address_target_temperature_state** KNX address for reading the target temperature from the KNX bus. Used in for setpoint_shift calculations as base temperature.
 - **group_address_setpoint_shift** KNX address to set setpoint_shift (base temperature deviation).
 - **group_address_setpoint_shift_state** KNX address to read current setpoint_shift.
+- **setpoint_shift_mode** Enum for setpoint_shift payload encoding. Can be DPT 6.010 or DPT 9.002. Default: SetpointShiftMode.DPT6010
 - **setpoint_shift_max** Maximum value for setpoint_shift.
 - **setpoint_shift_min** Minimum value for setpoint_shift.
 - **temperature_step** Set the multiplier for setpoint_shift (DPT 6) calculations and the step size for HA UI-elements.
@@ -75,7 +30,10 @@ groups:
 - **max_temp** Maximum value for target temperature.
 - **min_temp** Minimum value for target temperature.
 - **mode** ClimateMode instance for this climate device
+- **device_updated_cb** awaitable callback for each update.
 
+* **xknx** is the XKNX object.
+* **name** is the name of the object.
 * **group_address_operation_mode** KNX address for operation mode. DPT 20.102
 * **group_address_operation_mode_state** KNX address for operation mode status. DPT 20.102
 * **group_address_operation_mode_protection** KNX address for switching on/off frost/heat protection mode. DPT 1
@@ -90,6 +48,7 @@ groups:
 * **group_address_heat_cool_state** KNX address for reading heating / cooling mode. DPT 1
 * **operation_modes** Overrides the supported operation modes.
 * **controller_modes** Overrides the supported controller modes.
+* **device_updated_cb** awaitable callback for each update.
 
 **Note:** `group_address_operation_mode_protection` / `group_address_operation_mode_night` / `group_address_operation_mode_comfort` / `group_address_operation_mode_standby` are not necessary if `group_address_operation_mode` was specified. When one of these is set `True`, the others will be set `False`. When one of these is set "Standby", "Comfort", "Frost_Protection" and "Night" will be set as supported. If `group_address_operation_mode_standby` is omitted, "Standby" is set when the other 3 are set to `False`.
 If only a subset of operation modes shall be used a list of supported modes may be passed to `operation_modes`.
@@ -137,3 +96,51 @@ await climate.set_setpoint_shift(1)
 await climate.sync()
 print("Current temperature: ", climate.temperature)
 ```
+
+## [](#header-2)Example
+
+```python
+climate_setpoint_shift = Climate(
+    xknx,
+    'TestClimateSPS',
+    group_address_temperature='1/2/2',
+    group_address_target_temperature_state='1/2/5',
+    group_address_setpoint_shift='1/2/3',
+    group_address_setpoint_shift_state='1/2/4'
+)
+
+climate_target_temp = Climate(
+    xknx,
+    'TestClimateTT',
+    group_address_temperature='2/2/2',
+    group_address_target_temperature='2/2/3',
+    group_address_target_temperature_state='2/2/4'
+)
+```
+
+## [](#header-2)Configuration via **xknx.yaml**
+
+Climate devices are usually configured via [`xknx.yaml`](/configuration):
+
+```yaml
+groups:
+  climate:
+    Children.Climate:
+      {
+        group_address_temperature: "1/7/2",
+        group_address_setpoint_shift: "1/7/3",
+        group_address_target_temperature_state: "1/7/4",
+      }
+    Office.Climate:
+      {
+        group_address_temperature: "1/7/5",
+        group_address_operation_mode: "1/7/6",
+      }
+    Attic.Climate:
+      {
+        group_address_temperature: "1/7/7",
+        group_address_operation_mode_protection: "1/7/8",
+        group_address_operation_mode_night: "1/7/9",
+        group_address_operation_mode_comfort: "1/7/10",
+      }
+```
diff --git a/docs/cover.md b/docs/cover.md
index 8ecde5f4..5307d3e3 100644
--- a/docs/cover.md
+++ b/docs/cover.md
@@ -11,35 +11,24 @@ nav_order: 3
 
 Shutters are simple representations of blind/roller cover actuators. With XKNX you can move them up, down, to direct positions or stop them. Internally the class calculates the current position while traveling.
 
-## [](#header-2)Example
-
-```python
-cover = Cover(xknx,
-              'TestCover',
-              group_address_long='1/2/1',
-              group_address_short='1/2/2',
-              group_address_position='1/2/3',
-              group_address_position_state='1/2/4',
-              travel_time_down=50,
-              travel_time_up=60)
-
-# Accessing cover
-await xknx.devices['TestShutter'].set_up()
-```
-
-## [](#header-2)Configuration via **xknx.yaml**
-
-Covers are usually configured via [`xknx.yaml`](/configuration):
-
-```yaml
-groups:
-    cover:
-        Livingroom.Shutter_1: {group_address_long: '1/4/1', group_address_short: '1/4/2', group_address_position_feedback: '1/4/3', group_address_position: '1/4/4', travel_time_down: 50, travel_time_up: 60 }
-```
-
-
 ## [](#header-2)Interface
 
+- `xknx` XKNX object.
+- `name` name of the device.
+- `group_address_long` KNX group address to move the cover to an end position. *DPT 1.001*
+- `group_address_short` KNX group address to move the cover stepwise. *DPT 1.001*
+- `group_address_stop` KNX group address to stop movement. *DPT 1.001*
+- `group_address_position` KNX group address to move to relative position. *DPT 5.001*
+- `group_address_position_state` KNX group address for retrieving the relative position. *DPT 5.001*
+- `group_address_angle` KNX group address to tilt blinds to relative position. *DPT 5.001*
+- `group_address_angle_state` KNX group address to retrieve angle of blinds. *DPT 5.001*
+- `travel_time_down` seconds to reach lower end position. Default: 22
+- `travel_time_up` seconds to reach upper end position. Default: 22
+- `invert_position` invert position (payload for eg. set_up() and relative position). Default: False
+- `invert_angle` invert angle. Default: False
+- `device_updated_cb` awaitable callback for each update.
+
+## [](#header-2)Example
 
 ```python
 cover = Cover(xknx,
@@ -86,9 +75,12 @@ angle = cover.current_angle()
 is_traveling = cover.is_traveling()
 position_reached = cover.position_reached()
 
-# Helper functions to see if cover is fully closed or fully open
+# Helper functions to see if cover is fully closed or fully open or moving
 is_open = cover.is_open()
 is_closed = cover.is_closed()
+is_traveling = cover.is_traveling()
+is_opening = cover.is_opening()
+is_closing = cover.is_closing()
 
 # Accessing cover via 'do'
 await cover.do('up')
@@ -100,4 +92,12 @@ await cover.do('short_down')
 await cover.sync()
 ```
 
+## [](#header-2)Configuration via **xknx.yaml**
+
+Covers are usually configured via [`xknx.yaml`](/configuration):
 
+```yaml
+groups:
+    cover:
+        Livingroom.Shutter_1: {group_address_long: '1/4/1', group_address_short: '1/4/2', group_address_position_feedback: '1/4/3', group_address_position: '1/4/4', travel_time_down: 50, travel_time_up: 60 }
+```
diff --git a/docs/home_assistant.md b/docs/home_assistant.md
index d7cc5171..2482f75e 100644
--- a/docs/home_assistant.md
+++ b/docs/home_assistant.md
@@ -9,7 +9,7 @@ nav_order: 13
 Home Assistant KNX Integration
 ========================
 
-XKNX is shipped within [Home Assistant (HA)](https://www.home-assistant.io/), the great solution for home automation, in the form of the included [KNX integration](https://www.home-assistant.io/integrations/#search/KNX).
+XKNX is shipped within [Home Assistant (HA)](https://www.home-assistant.io/), the great solution for home automation, in the form of the included [KNX integration](https://www.home-assistant.io/integrations/knx/).
 
 For development and debugging reasons - or to catch up with the newest features - you may still want to use the custom component version of the plugin.
 
@@ -57,25 +57,21 @@ Starting via service is also possible, but you have to change the configuration
 Configuration:
 --------------
 
-The configuration for the manually checked out version works the same as described within [Home Assistant KNX documentation](https://home-assistant.io/integrations/#search/knx) with the difference that the integration and platform is called `xknx` instead of `knx` (which is the HA default KNX integration and platform name).
+The configuration for the manually checked out version works the same as described within [Home Assistant KNX documentation](https://www.home-assistant.io/integrations/knx/) with the difference that the integration and platform is called `xknx` instead of `knx` (which is the HA default KNX integration and platform name).
 
 ```yaml
 xknx:
   tunneling:
     host: '192.168.2.23'
-    port: 3671
-    local_ip: '192.168.2.109'
-
-light:
-  - platform: xknx
-    name: Kitchen-Light-1
-    address: '1/0/9'
-    brightness_address: '1/0/11'
-
-switch:
-  - platform: xknx
-    name: Kitchen.Coffee
-    address: '1/1/6'
+
+  light:
+    - name: Kitchen-Light-1
+      address: '1/0/9'
+      brightness_address: '1/0/11'
+
+  switch:
+    - name: Kitchen.Coffee
+      address: '1/1/6'
 ```
 
 
diff --git a/docs/index.md b/docs/index.md
index cf4fb05f..fc1fedd7 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -52,17 +52,13 @@ async def main():
     await asyncio.sleep(2)
     await xknx.stop()
 
-
-# pylint: disable=invalid-name
-loop = asyncio.get_event_loop()
-loop.run_until_complete(main())
-loop.close()
+asyncio.run(main())
 ```
 
 For more examples please check out the [examples page](https://github.com/XKNX/xknx/tree/master/examples)
 
 # [](#header-1)Getting Help
 
-For questions, feature requests, bugreports wither join the [XKNX chat on Discord](https://discord.gg/EuAQDXU) or write an [email](mailto:xknx@xknx.io).
+For questions, feature requests, bugreports either join the [XKNX chat on Discord](https://discord.gg/EuAQDXU), open an issue at [GitHub](https://github.com/XKNX/xknx) or write an [email](mailto:xknx@xknx.io).
 
 
diff --git a/docs/introduction.md b/docs/introduction.md
index 992539ec..2cb85087 100644
--- a/docs/introduction.md
+++ b/docs/introduction.md
@@ -25,9 +25,7 @@ async def main():
 
     await xknx.stop()
 
-loop = asyncio.get_event_loop()
-loop.run_until_complete(main())
-loop.close()
+asyncio.run(main())
 ```
 
 # [](#header-2)Explanation En Dé­tail:
@@ -57,16 +55,6 @@ Asynchronous start of the XKNX object. `xknx.start()` will connect to a KNX/IP d
 
 Asynchronous stop of the XKNX object. `xknx.stop()` will disconnect from Tunnels - which is important bc most of the devices have a limited amount of channels.
 
-```
-loop = asyncio.get_event_loop()
-loop.run_until_complete(main())
-loop.close()
-```
-
-Boilerplate code, for starting an asynchonous function. See [asyncio](https://www.python.org/dev/peps/pep-3156/) documentation for details.
-
-
-_Important Note:_ If you are using Python 3.5 you have to replace the `await` syntax with the `@asyncio.coroutine` syntax.
 
 
 
diff --git a/docs/light.md b/docs/light.md
index b71d43c4..4ba00f2b 100644
--- a/docs/light.md
+++ b/docs/light.md
@@ -11,56 +11,27 @@ nav_order: 4
 
 The Light object is either a representation of a binary or dimm actor, LED-controller or DALI-gateway.
 
-Expected datapoint types for light functions and their corresponding state addresses:
-- switch: DPT 1.001
-- brightness: DPT 5.001
-- color: DPT 232.600
-- rgbw: DPT 251.600
-- tunable_white: DPT 5.001
-- color_temperature: DPT 7.600
-
-## [](#header-2)Example
-
-```python
-light = Light(xknx,
-              name='TestLight',
-              group_address_switch='1/2/3',
-              group_address_brightness='1/2/5')
-
-# Accessing light
-await xknx.devices['TestLight'].set_on()
-await xknx.devices['TestLight'].set_brightness(23)
-```
-
-## [](#header-2)Configuration via **xknx.yaml**
-
-Lights are usually configured via [`xknx.yaml`](/configuration):
-
-```yaml
-groups:
-    light:
-
-        # Lights with dimming
-        Kitchen.Light_1:     {group_address_switch: '1/6/1', group_address_brightness: '1/6/3'}
-        Diningroom.Light_1:  {group_address_switch: '1/6/4', group_address_brightness: '1/6/6'}
-
-        # Light without dimming
-        Living-Room.Light_1: {group_address_switch: '1/6/7'}
-
-        # Light with extra addresses for states:
-        Office.Light_1:  {group_address_switch: '1/7/4', group_address_switch_state: '1/7/5', group_address_brightness: '1/7/6', group_address_brightness_state: '1/7/7'}
-
-        # Light with color temperature in Kelvin
-        Living-Room.Light_CT:  {group_address_switch: '1/6/11', group_address_switch_state: '1/6/10', group_address_brightness: '1/6/12', group_address_brightness_state: '1/6/13', group_address_color_temperature: '1/6/14',  group_address_color_temperature_state: '1/6/15'}
-
-        # Light with color temperature in percent
-        Living-Room.Light_TW:  {group_address_switch: '1/6/21', group_address_switch_state: '1/6/20', group_address_brightness: '1/6/22', group_address_brightness_state: '1/6/23', group_address_tunable_white: '1/6/24',  group_address_tunable_white_state: '1/6/25'}
-
-```
-
-
 ## [](#header-2)Interface
 
+- `xknx` XKNX object.
+- `name` name of the device.
+- `group_address_switch` KNX group address to switch the light. *DPT 1.001*
+- `group_address_switch_state` KNX group address for the state of the light. *DPT 1.001*
+- `group_address_brightness` KNX group address to set the brightness. *DPT 5.001*
+- `group_address_brightness_state` KNX group address for the current brightness state. *DPT 5.001*
+- `group_address_color` KNX group address to set the RGB color. *DPT 232.600*
+- `group_address_color_state` KNX group address for the current RGB color. *DPT 232.600*
+- `group_address_rgbw` KNX group address to set the RGBW color. *DPT 251.600*
+- `group_address_rgbw_state` KNX group address for the current RGBW color. *DPT 251.600*
+- `group_address_tunable_white` KNX group address to set relative color temperature. *DPT 5.001*
+- `group_address_tunable_white_state` KNX group address for the current relative color temperature. *DPT 5.001*
+- `group_address_color_temperature` KNX group address to set absolute color temperature. *DPT 7.600*
+- `group_address_color_temperature_state` KNX group address for the current absolute color temperature. *DPT 7.600*
+- `min_kelvin` lowest possible color temperature in Kelvin. Default: 2700
+- `max_kelvin` hightest possible color temperature in Kelvin. Default: 6000
+- `device_updated_cb` awaitable callback for each update.
+
+## [](#header-2)Example
 
 ```python
 light = Light(xknx,
@@ -122,4 +93,29 @@ print(light.current_color_temperature)
 await light.sync()
 ```
 
+## [](#header-2)Configuration via **xknx.yaml**
+
+Lights are usually configured via [`xknx.yaml`](/configuration):
+
+```yaml
+groups:
+    light:
+
+        # Lights with dimming
+        Kitchen.Light_1:     {group_address_switch: '1/6/1', group_address_brightness: '1/6/3'}
+        Diningroom.Light_1:  {group_address_switch: '1/6/4', group_address_brightness: '1/6/6'}
+
+        # Light without dimming
+        Living-Room.Light_1: {group_address_switch: '1/6/7'}
+
+        # Light with extra addresses for states:
+        Office.Light_1:  {group_address_switch: '1/7/4', group_address_switch_state: '1/7/5', group_address_brightness: '1/7/6', group_address_brightness_state: '1/7/7'}
+
+        # Light with color temperature in Kelvin
+        Living-Room.Light_CT:  {group_address_switch: '1/6/11', group_address_switch_state: '1/6/10', group_address_brightness: '1/6/12', group_address_brightness_state: '1/6/13', group_address_color_temperature: '1/6/14',  group_address_color_temperature_state: '1/6/15'}
+
+        # Light with color temperature in percent
+        Living-Room.Light_TW:  {group_address_switch: '1/6/21', group_address_switch_state: '1/6/20', group_address_brightness: '1/6/22', group_address_brightness_state: '1/6/23', group_address_tunable_white: '1/6/24',  group_address_tunable_white_state: '1/6/25'}
+
+```
 
diff --git a/docs/sensor.md b/docs/sensor.md
index 22ae2581..4fa91ba4 100644
--- a/docs/sensor.md
+++ b/docs/sensor.md
@@ -11,26 +11,34 @@ nav_order: 5
 
 Sensors are monitoring temperature, air humidity, pressure etc. from KNX bus.
 
-```python
-    sensor = Sensor(
-        xknx=xknx,
-        name='DiningRoom.Temperature.Sensor',
-        always_callback=False,
-        group_address_state='6/2/1',
-        sync_state=True,
-        value_type='temperature'
-    )
-    await sensor.sync()
-    print(sensor)
-```
+## [](#header-2)Interface
 
 * `xknx` is the XKNX object.
 * `name` is the name of the object.
-* `always_callback` defines if a callback/update should always be triggered no matter if the previous and the new state are identical.
 * `group_address_state` is the KNX group address of the sensor device.
 * `sync_state` defines if the value should be actively read from the bus. If `False` no GroupValueRead telegrams will be sent to its group address. Defaults to `True`
+* `always_callback` defines if a callback/update should always be triggered no matter if the previous and the new state are identical.
 * `value_type` controls how the value should be rendered in a human readable representation. The attribut may have may have the values `percent`, `temperature`, `illuminance`, `speed_ms` or `current`.
+* `device_updated_cb` awaitable callback for each update.
 
+## [](#header-2)Example
+
+```python
+sensor = Sensor(
+    xknx=xknx,
+    name='DiningRoom.Temperature.Sensor',
+    always_callback=False,
+    group_address_state='6/2/1',
+    sync_state=True,
+    value_type='temperature'
+)
+
+await sensor.sync() # Syncs the state. Tries to read the corresponding value from the bus.
+
+sensor.resolve_state() # Returns the value of in a human readable way
+
+sensor.unit_of_measurement() # returns the unit of the value in a human readable way
+```
 
 ## [](#header-2)Configuration via **xknx.yaml**
 
@@ -44,19 +52,6 @@ Sensor objects are usually configured via [`xknx.yaml`](/configuration):
         Some.Other.Value: {group_address_state: '2/0/3'}
 ```
 
-## [](#header-2)Interface
-
-```python
-sensor = Sensor(
-        xknx=xknx,
-        name='DiningRoom.Temperature.Sensor',
-        group_address_state='6/2/1')
-
-await sensor.sync() # Syncs the state. Tries to read the corresponding value from the bus.
-
-sensor.resolve_state() # Returns the value of in a human readable way
 
-sensor.unit_of_measurement() # returns the unit of the value in a human readable way
-```
 
 
diff --git a/docs/switch.md b/docs/switch.md
index 6f28ec8c..46275311 100644
--- a/docs/switch.md
+++ b/docs/switch.md
@@ -11,42 +11,27 @@ nav_order: 6
 
 Switches are simple representations of binary actors. They mainly support switching on and off.
 
+## [](#header-2)Interface
+
 - `xknx` is the XKNX object.
 - `name` is the name of the object.
 - `group_address` is the KNX group address of the switch device. Used for sending.
 - `group_address_state` is the KNX group address of the switch state. Used for updating and reading state.
 - `invert` inverts the payload so state "on" is represented by 0 on bus and "off" by 1. Defaults to `False`
 - `reset_after` may be used to reset the switch to `OFF` again after given time in sec. Defaults to `None`
+- `device_updated_cb` awaitable callback for each update.
+
 
 ## [](#header-2)Example
 
 ```python
 switch = Switch(xknx, 'TestOutlet', group_address='1/2/3')
 
-# Accessing switch
+# Accessing switch via xknx.devices
 await xknx.devices['TestOutlet'].set_on()
-```
-
-## [](#header-2)Configuration via **xknx.yaml**
-
-Switches are usually configured via [`xknx.yaml`](/configuration):
-
-```yaml
-groups:
-    switch:
-        Livingroom.Outlet_1: {group_address: '1/3/1'}
-        Livingroom.Outlet_2: {group_address: '1/3/2'}
-```
-
-## [](#header-2)Interface
-
-
-```python
-switch = Switch(xknx, 'TestOutlet', group_address='1/2/3')
 
 # Switching switch on
 await switch.set_on()
-
 # Switching switch off
 await switch.set_off()
 
@@ -57,8 +42,21 @@ await switch.do('off')
 # Accessing state
 print(switch.state)
 
-# Requesting state via KNX GROUP WRITE
-await switch.sync()
+# Requesting state via KNX GroupValueRead
+await switch.sync(wait_for_result=True)
 ```
 
+## [](#header-2)Configuration via **xknx.yaml**
+
+Switches are usually configured via [`xknx.yaml`](/configuration):
+
+```yaml
+groups:
+    switch:
+        Livingroom.Outlet_1: {group_address: '1/3/1'}
+        Livingroom.Outlet_2: {group_address: '1/3/2'}
+```
+
+
+
 
diff --git a/docs/time.md b/docs/time.md
index d7f0dab5..b47605a5 100644
--- a/docs/time.md
+++ b/docs/time.md
@@ -30,6 +30,7 @@ await xknx.devices['TimeTest'].sync()
 * `group_address` is the KNX group address of the sensor device.
 * `broadcast_type` defines the value type that will be sent to the KNX bus. Valid attributes are: 'time', 'date' and 'datetime'. Default: `time`
 * `localtime` If set `True` sync() and GroupValueRead requests always return the current local time. On `False` the set value will be sent. Default: `True`
+* `device_updated_cb` awaitable callback for each update.
 
 
 ## [](#header-2)Configuration via **xknx.yaml**
@@ -52,16 +53,11 @@ from xknx import XKNX
 from xknx.devices import DateTime
 
 async def main():
-    xknx = XKNX()
-    time = DateTime(xknx, 'TimeTest', group_address='1/2/3')
-    print("Sending time to KNX bus every hour")
-    await xknx.start(daemon_mode=True, state_updater=True)
-    await xknx.stop()
-
-# pylint: disable=invalid-name
-loop = asyncio.get_event_loop()
-loop.run_until_complete(main())
-loop.close()
+    async with XKNX(daemon_mode=True) as xknx:
+        time = DateTime(xknx, 'TimeTest', group_address='1/2/3')
+        print("Sending time to KNX bus every hour")
+
+asyncio.run(main())
 ```
 
 ## [](#header-2)Interface
@@ -74,6 +70,7 @@ from xknx.devices import DateTime
 xknx = XKNX()
 time_device = DateTime(xknx, 'TimeTest', group_address='1/2/3', broadcast_type='time')
 
+await xknx.start()
 # Sending Time to KNX bus
 await time_device.broadcast_localtime()
 ```
diff --git a/docs/weather.md b/docs/weather.md
index bf8905e3..b81f8be4 100644
--- a/docs/weather.md
+++ b/docs/weather.md
@@ -15,26 +15,21 @@ The weather device is basically a set of sensors that you can obtain from your w
 
 ```python
 
-    xknx = XKNX()
-    await xknx.start()
-
-    weather = Weather(
-        xknx,
-        'Home',
-        group_address_temperature='7/0/1',
-        group_address_brightness_south='7/0/5',
-        group_address_brightness_east='7/0/4',
-        group_address_brightness_west='7/0/3',
-        group_address_wind_speed='7/0/2',
-        group_address_day_night='7/0/7',
-        group_address_rain_alarm='7/0/0'
-    )
-
-    await weather.sync()
-    await asyncio.sleep(10) # wait for bus responses
-    print(weather)
-
-    await xknx.stop()
+    async with XKNX() as xknx:
+        weather = Weather(
+            xknx,
+            'Home',
+            group_address_temperature='7/0/1',
+            group_address_brightness_south='7/0/5',
+            group_address_brightness_east='7/0/4',
+            group_address_brightness_west='7/0/3',
+            group_address_wind_speed='7/0/2',
+            group_address_day_night='7/0/7',
+            group_address_rain_alarm='7/0/0'
+        )
+
+        await weather.sync(wait_for_result=True)
+        print(weather)
 
 ```
 
@@ -63,6 +58,8 @@ groups:
 
 ## [](#header-2)Interface
 
+- **xknx** is the XKNX object.
+- **name** is the name of the object.
 - **group_address_temperature** KNX address of current outside temperature.
 - **group_address_brightness_south** KNX address for the brightness to south **DPT 9.004**.
 - **group_address_brightness_west** KNX address for the brightness to west **DPT 9.004**.
@@ -76,32 +73,28 @@ groups:
 - **group_address_humidity** KNX address for reading current humidity. **DPT 9.007**
 - **expose_sensors** If true, also exposes all values as sensors to the xknx device list (useful for home assistant). Default: False
 - **sync_state** Periodically sync the state.
+- **device_updated_cb** awaitable callback for each update.
 
 ```python
 
-    xknx = XKNX()
-    await xknx.start()
-
-    weather = Weather(
-        xknx,
-        'Home',
-        group_address_temperature='7/0/1',
-        group_address_brightness_south='7/0/5',
-        group_address_brightness_east='7/0/4',
-        group_address_brightness_west='7/0/3',
-        group_address_wind_speed='7/0/2',
-        group_address_day_night='7/0/7',
-        group_address_rain_alarm='7/0/0'
-    )
-
-    await weather.sync()
-    await asyncio.sleep(10) # wait for bus responses
-
-    print(weather.humidity) # get humidity
-    print(weather.ha_current_state()) # get the current state mapped as a WeatherCondition enum value. (for HA mainly)
-    print(weather.wind_speed) # get the current wind speed in m/s
-
-    await xknx.stop()
+    async with XKNX() as xknx:
+        weather = Weather(
+            xknx,
+            'Home',
+            group_address_temperature='7/0/1',
+            group_address_brightness_south='7/0/5',
+            group_address_brightness_east='7/0/4',
+            group_address_brightness_west='7/0/3',
+            group_address_wind_speed='7/0/2',
+            group_address_day_night='7/0/7',
+            group_address_rain_alarm='7/0/0'
+        )
+
+        await weather.sync(wait_for_result=True)
+
+        print(weather.humidity) # get humidity
+        print(weather.ha_current_state()) # get the current state mapped as a WeatherCondition enum value. (for HA mainly)
+        print(weather.wind_speed) # get the current wind speed in m/s
 
 ```
 
diff --git a/docs/xknx.md b/docs/xknx.md
index 20bbc4c1..57427120 100644
--- a/docs/xknx.md
+++ b/docs/xknx.md
@@ -71,25 +71,7 @@ Will disconnect from tunneling devices and stop the different queues.
 
 # [](#header-2)Using XKNX as an asynchronous context manager
 
-Except of writing:
-
-```python
-import asyncio
-
-async def main():
-    xknx = XKNX()
-    await xknx.start()
-    switch = Switch(xknx,
-                name='TestSwitch',
-                group_address='1/1/11')
-
-    await switch.set_on()
-    await xknx.stop()
-
-asyncio.run(main())
-```
-
-you can now use the following syntax:
+You can also use an asynchronous context manager instead of calling `xknx.start()` and `xknx.stop()`:
 
 ```python
 import asyncio
@@ -107,7 +89,7 @@ asyncio.run(main())
 
 # [](#header-2)Devices
 
-The XKNX may keep all devices in a local storage named `devices`. All devices may be accessed by their name: `xknx.devices['NameOfDevice']`. If XKNX receives an update via KNX GROUP WRITE the device is updated automatically.
+XKNX keeps all initialized devices in a local storage named `devices`. All devices may be accessed by their name: `xknx.devices['NameOfDevice']`. When an update via KNX GroupValueWrite or GroupValueResponse was received devices will be updated accordingly.
 
 Example:
 
@@ -123,7 +105,7 @@ await xknx.devices['TestSwitch'].set_off()
 
 # [](#header-2)Callbacks
 
-The `telegram_received_cb` will be called for each KNX telegram received by the XKNX daemon. Example:
+An awaitable `telegram_received_cb` will be called for each KNX telegram received by the XKNX daemon. Example:
 
 ```python
 import asyncio
@@ -133,14 +115,11 @@ async def telegram_received_cb(telegram):
     print("Telegram received: {0}".format(telegram))
 
 async def main():
-    xknx = XKNX(telegram_received_cb=telegram_received_cb)
-    await xknx.start(daemon_mode=True)
+    xknx = XKNX(telegram_received_cb=telegram_received_cb, daemon_mode=True)
+    await xknx.start()
     await xknx.stop()
 
-# pylint: disable=invalid-name
-loop = asyncio.get_event_loop()
-loop.run_until_complete(main())
-loop.close()
+asyncio.run(main())
 ```
 
 For all devices stored in the `devices` storage (see [above](#devices)) a callback for each update may be defined:
@@ -156,19 +135,15 @@ async def device_updated_cb(device):
 
 
 async def main():
-    xknx = XKNX(device_updated_cb=device_updated_cb)
+    xknx = XKNX(device_updated_cb=device_updated_cb, daemon_mode=True)
     switch = Switch(xknx,
                     name='TestSwitch',
                     group_address='1/1/11')
 
-    await xknx.start(daemon_mode=True)
-
+    await xknx.start()
     await xknx.stop()
 
-# pylint: disable=invalid-name
-loop = asyncio.get_event_loop()
-loop.run_until_complete(main())
-loop.close()
+asyncio.run(main())
 ```
 
 
diff --git a/xknx/devices/binary_sensor.py b/xknx/devices/binary_sensor.py
index 548d6224..2880ca20 100644
--- a/xknx/devices/binary_sensor.py
+++ b/xknx/devices/binary_sensor.py
@@ -29,7 +29,7 @@ class BinarySensor(Device):
         group_address_state=None,
         invert: Optional[bool] = False,
         sync_state: bool = True,
-        ignore_internal_state: bool = True,
+        ignore_internal_state: bool = False,
         device_class: str = None,
         reset_after: Optional[float] = None,
         actions: List[Action] = None,
@@ -44,7 +44,7 @@ class BinarySensor(Device):
 
         self.actions = actions
         self.device_class = device_class
-        self.ignore_internal_state = ignore_internal_state
+        self.ignore_internal_state = ignore_internal_state or bool(context_timeout)
         self.reset_after = reset_after
         self.state = None
 
@@ -86,7 +86,7 @@ class BinarySensor(Device):
         reset_after = config.get("reset_after")
         sync_state = config.get("sync_state", True)
         device_class = config.get("device_class")
-        ignore_internal_state = config.get("ignore_internal_state", True)
+        ignore_internal_state = config.get("ignore_internal_state", False)
         actions = []
         if "actions" in config:
             for action in config["actions"]:
@@ -179,13 +179,19 @@ class BinarySensor(Device):
     async def process_group_write(self, telegram):
         """Process incoming and outgoing GROUP WRITE telegram."""
         if await self.remote_value.process(telegram, always_callback=True):
-
-            if self.reset_after is not None and self.state:
-                if self._reset_task:
-                    self._reset_task.cancel()
-                self._reset_task = asyncio.create_task(
-                    self._reset_state(self.reset_after)
-                )
+            self._process_reset_after()
+
+    async def process_group_response(self, telegram):
+        """Process incoming GroupValueResponse telegrams."""
+        if await self.remote_value.process(telegram, always_callback=False):
+            self._process_reset_after()
+
+    def _process_reset_after(self):
+        """Create Task for resetting state if 'reset_after' is configured."""
+        if self.reset_after is not None and self.state:
+            if self._reset_task:
+                self._reset_task.cancel()
+            self._reset_task = asyncio.create_task(self._reset_state(self.reset_after))
 
     async def _reset_state(self, wait_seconds: float):
         await asyncio.sleep(wait_seconds)


Test patch:
diff --git a/test/devices_tests/binary_sensor_test.py b/test/devices_tests/binary_sensor_test.py
index 853428cc..471be8fe 100644
--- a/test/devices_tests/binary_sensor_test.py
+++ b/test/devices_tests/binary_sensor_test.py
@@ -7,7 +7,7 @@ from xknx import XKNX
 from xknx.devices import Action, BinarySensor, Switch
 from xknx.dpt import DPTArray, DPTBinary
 from xknx.exceptions import CouldNotParseTelegram
-from xknx.telegram import GroupAddress, Telegram
+from xknx.telegram import GroupAddress, Telegram, TelegramType
 
 
 class AsyncMock(MagicMock):
@@ -83,16 +83,38 @@ class TestBinarySensor(unittest.TestCase):
         """Test process / reading telegrams from telegram queue."""
         xknx = XKNX()
         reset_after_sec = 0.001
+        async_after_update_callback = AsyncMock()
         binaryinput = BinarySensor(
-            xknx, "TestInput", "1/2/3", reset_after=reset_after_sec
+            xknx,
+            "TestInput",
+            "1/2/3",
+            reset_after=reset_after_sec,
+            device_updated_cb=async_after_update_callback,
         )
         telegram_on = Telegram(
             group_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
         )
 
         self.loop.run_until_complete(binaryinput.process(telegram_on))
-        self.loop.run_until_complete(asyncio.sleep(reset_after_sec * 2))
-        self.assertEqual(binaryinput.state, False)
+        self.assertTrue(binaryinput.state)
+        self.loop.run_until_complete(asyncio.sleep(reset_after_sec * 1.1))
+        self.assertFalse(binaryinput.state)
+        # once for 'on' and once for 'off'
+        self.assertEqual(async_after_update_callback.call_count, 2)
+
+        async_after_update_callback.reset_mock()
+        # multiple telegrams during reset_after time period shall reset timer
+        self.loop.run_until_complete(binaryinput.process(telegram_on))
+        async_after_update_callback.assert_called_once()
+        self.loop.run_until_complete(binaryinput.process(telegram_on))
+        self.loop.run_until_complete(binaryinput.process(telegram_on))
+        # second and third telegram resets timer but doesn't run callback
+        async_after_update_callback.assert_called_once()
+        self.assertTrue(binaryinput.state)
+        self.loop.run_until_complete(asyncio.sleep(reset_after_sec * 1.1))
+        self.assertFalse(binaryinput.state)
+        # once for 'on' and once for 'off'
+        self.assertEqual(async_after_update_callback.call_count, 2)
 
     def test_process_action(self):
         """Test process / reading telegrams from telegram queue. Test if action is executed."""
@@ -216,12 +238,7 @@ class TestBinarySensor(unittest.TestCase):
         switch = BinarySensor(
             xknx, "TestInput", group_address_state="1/2/3", ignore_internal_state=False
         )
-
-        after_update_callback = Mock()
-
-        async def async_after_update_callback(device):
-            """Async callback."""
-            after_update_callback(device)
+        async_after_update_callback = AsyncMock()
 
         switch.register_device_updated_cb(async_after_update_callback)
 
@@ -229,12 +246,12 @@ class TestBinarySensor(unittest.TestCase):
         self.loop.run_until_complete(switch.process(telegram))
         # no _context_task started because ignore_internal_state is False
         self.assertIsNone(switch._context_task)
-        after_update_callback.assert_called_once_with(switch)
+        async_after_update_callback.assert_called_once_with(switch)
 
-        after_update_callback.reset_mock()
+        async_after_update_callback.reset_mock()
         # send same telegram again
         self.loop.run_until_complete(switch.process(telegram))
-        after_update_callback.assert_not_called()
+        async_after_update_callback.assert_not_called()
 
     def test_process_callback_ignore_internal_state(self):
         """Test after_update_callback after state of switch was changed."""
@@ -247,12 +264,7 @@ class TestBinarySensor(unittest.TestCase):
             ignore_internal_state=True,
             context_timeout=0.001,
         )
-
-        after_update_callback = Mock()
-
-        async def async_after_update_callback(device):
-            """Async callback."""
-            after_update_callback(device)
+        async_after_update_callback = AsyncMock()
 
         switch.register_device_updated_cb(async_after_update_callback)
 
@@ -260,25 +272,25 @@ class TestBinarySensor(unittest.TestCase):
         self.assertEqual(switch.counter, 0)
 
         self.loop.run_until_complete(switch.process(telegram))
-        after_update_callback.assert_not_called()
+        async_after_update_callback.assert_not_called()
         self.assertEqual(switch.counter, 1)
         self.loop.run_until_complete(switch._context_task)
-        after_update_callback.assert_called_with(switch)
+        async_after_update_callback.assert_called_with(switch)
         # once with counter 1 and once with counter 0
-        self.assertEqual(after_update_callback.call_count, 2)
+        self.assertEqual(async_after_update_callback.call_count, 2)
 
-        after_update_callback.reset_mock()
+        async_after_update_callback.reset_mock()
         # send same telegram again
         self.loop.run_until_complete(switch.process(telegram))
         self.assertEqual(switch.counter, 1)
         self.loop.run_until_complete(switch.process(telegram))
         self.assertEqual(switch.counter, 2)
-        after_update_callback.assert_not_called()
+        async_after_update_callback.assert_not_called()
 
         self.loop.run_until_complete(switch._context_task)
-        after_update_callback.assert_called_with(switch)
+        async_after_update_callback.assert_called_with(switch)
         # once with counter 2 and once with counter 0
-        self.assertEqual(after_update_callback.call_count, 2)
+        self.assertEqual(async_after_update_callback.call_count, 2)
         self.assertEqual(switch.counter, 0)
 
     def test_process_callback_ignore_internal_state_no_counter(self):
@@ -292,12 +304,7 @@ class TestBinarySensor(unittest.TestCase):
             ignore_internal_state=True,
             context_timeout=0,
         )
-
-        after_update_callback = Mock()
-
-        async def async_after_update_callback(device):
-            """Async callback."""
-            after_update_callback(device)
+        async_after_update_callback = AsyncMock()
 
         switch.register_device_updated_cb(async_after_update_callback)
 
@@ -305,12 +312,49 @@ class TestBinarySensor(unittest.TestCase):
         self.loop.run_until_complete(switch.process(telegram))
         # no _context_task started because context_timeout is False
         self.assertIsNone(switch._context_task)
-        after_update_callback.assert_called_once_with(switch)
+        async_after_update_callback.assert_called_once_with(switch)
 
-        after_update_callback.reset_mock()
+        async_after_update_callback.reset_mock()
         # send same telegram again
         self.loop.run_until_complete(switch.process(telegram))
-        after_update_callback.assert_called_once_with(switch)
+        async_after_update_callback.assert_called_once_with(switch)
+
+    def test_process_group_value_response(self):
+        """Test precess of GroupValueResponse telegrams."""
+        # pylint: disable=protected-access
+        xknx = XKNX()
+        switch = BinarySensor(
+            xknx,
+            "TestInput",
+            group_address_state="1/2/3",
+            ignore_internal_state=True,
+        )
+        async_after_update_callback = AsyncMock()
+
+        switch.register_device_updated_cb(async_after_update_callback)
+
+        write_telegram = Telegram(
+            group_address=GroupAddress("1/2/3"), payload=DPTBinary(1)
+        )
+        response_telegram = Telegram(
+            group_address=GroupAddress("1/2/3"),
+            payload=DPTBinary(1),
+            telegramtype=TelegramType.GROUP_RESPONSE,
+        )
+        self.assertIsNone(switch.state)
+        # initial GroupValueResponse changes state and runs callback
+        self.loop.run_until_complete(switch.process(response_telegram))
+        self.assertTrue(switch.state)
+        async_after_update_callback.assert_called_once_with(switch)
+        # GroupValueWrite with same payload runs callback because of `ignore_internal_state`
+        async_after_update_callback.reset_mock()
+        self.loop.run_until_complete(switch.process(write_telegram))
+        self.assertTrue(switch.state)
+        async_after_update_callback.assert_called_once_with(switch)
+        # GroupValueResponse should not run callback when state has not changed
+        async_after_update_callback.reset_mock()
+        self.loop.run_until_complete(switch.process(response_telegram))
+        async_after_update_callback.assert_not_called()
 
     #
     # TEST COUNTER
diff --git a/test/devices_tests/cover_test.py b/test/devices_tests/cover_test.py
index ac00f704..16e4547c 100644
--- a/test/devices_tests/cover_test.py
+++ b/test/devices_tests/cover_test.py
@@ -461,7 +461,6 @@ class TestCover(unittest.TestCase):
             )
 
         self.loop.run_until_complete(cover.set_position(100))
-        print(cover.travelcalculator.position_closed)
         self.assertEqual(xknx.telegrams.qsize(), 1)
         telegram = xknx.telegrams.get_nowait()
         self.assertEqual(
diff --git a/test/devices_tests/sensor_test.py b/test/devices_tests/sensor_test.py
index 5875ea4e..f7a976e2 100644
--- a/test/devices_tests/sensor_test.py
+++ b/test/devices_tests/sensor_test.py
@@ -79,6 +79,11 @@ class TestSensor(unittest.TestCase):
         sensor.sensor_value.payload = payload
 
         telegram = Telegram(group_address=GroupAddress("1/2/3"), payload=payload)
+        response_telegram = Telegram(
+            group_address=GroupAddress("1/2/3"),
+            payload=payload,
+            telegramtype=TelegramType.GROUP_RESPONSE,
+        )
 
         # verify not called when always_callback is False
         self.loop.run_until_complete(sensor.process(telegram))
@@ -93,7 +98,7 @@ class TestSensor(unittest.TestCase):
         after_update_callback.reset_mock()
 
         # verify not called when processing read responses
-        self.loop.run_until_complete(sensor.process_group_response(telegram))
+        self.loop.run_until_complete(sensor.process(response_telegram))
         after_update_callback.assert_not_called()
 
     def test_str_acceleration(self):