Skip to content

Commit

Permalink
Merge pull request #24 from RainerStaude/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
RainerStaude authored May 3, 2023
2 parents 5576bff + 70f2c8d commit 97c41d8
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 49 deletions.
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
MIT License

Copyright (c) 2020 Nicolas Berthel
Copyright (c) 2023 Rainer Staude

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
59 changes: 39 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,23 @@ cover:
travelling_time_down: 26.5
```

### Position by value template
## Position by value template
In some cases it might be useful to add a value template to determine the position
of your cover. For example for a roof window with rain sensor. In case of rain,
the roof window will close, but you cannot determine this without an additional
sensor.
Every time the template generates a new result, the position of the cover is overwritten by the result of the template.
Every time the template generates a new result, the position of the cover is overwritten
by the result of the template.
The following results are valid:
- any number between `0` and `100` where `0` is `closed` and `100` is `open`
- logic values where
- `'closed'`, `'false'`, `False` are `closed`
- `'open'`, `'true'`, `True` are `open`
- unknown values `'unknown'`, `'unavailable'`, `'none'`, `None`
The unknown values are useful to set the position only to confirm one specific
position, like closed in the example above. For any other values the position
will not changed. This allows to use the value template in conjunction with the position by travel time.
position, like closed in the example below. For any other values the position
will not changed. This allows to use the value template in conjunction with the position
by travel time.
```yaml
cover:
- platform: becker
Expand All @@ -117,11 +119,11 @@ cover:
friendly_name: "Roof window"
channel: "3:1"
travelling_time_up: 15
# Set position to closed (False) if sensor.roof_window is closed, otherwise keep value
# Set position to closed (0) if sensor.roof_window is closed, otherwise keep value
value_template: "{{ 0 if is_state('sensor.roof_window', 'closed') else None }}"
```

### Position tracking for cover commands from Becker remotes
## Position tracking for cover commands from Becker remotes
Usually there is at least one remote, the master remote, used to control the cover.
The remote communicates directly with the cover. It is possible to receive and track
all remote commands within home assistant. Therefore the position of the cover
Expand All @@ -144,19 +146,24 @@ cover:
remote_id: "12345:2"
```

### Intermediate cover position
## Intermediate cover position
Becker covers supports two intermediate positions. One when opening the cover
and one when closing the cover. Please see the manual of your cover to see how
to program these intermediate positions in your cover.
Your cover will travel to the corresponding intermediate position if your double
tab the UP or DOWN button on your remote.
The default intermediate positions in the Becker integraten are `25` for UP
direction and `75` for DOWN direction. If the cover already passed the intermediate
position it will close instead.
This behaviour is imitated by the Becker integration in Home Assistant. To imitate
the cover movement properly in Home Assistant it is required to set the positions properly.
You can calculate the `intermediate_position_up` by dividing the measured runtime from
closed position to the intermediate position in direction UP by the `travelling_time_up`.
The default intermediate positions in the Becker integration are `25` for UP
direction and `75` for DOWN direction, where `0` is `closed` and `100` is `open`.
This behavior is imitated by the Becker integration in Home Assistant. To imitate
the cover movement properly in Home Assistant it is required to set the positions properly.
You can calculate the `intermediate_position_up`. You need to measure the runtime from
closed position to the intermediate position in direction UP (double tap UP
on your remote). Divide the measured time by the `travelling_time_up` and
multiply the result by `100`.
You can do the same for the `intermediate_position_up`. Measure the runtime from
closed position to the intermediate position in direction DOWN (double tap DOWN on
your remote). Divide the measured time by the `travelling_time_up` and multiply
the result by `100`.
```yaml
- platform: becker
covers:
Expand All @@ -166,7 +173,7 @@ closed position to the intermediate position in direction UP by the `travelling_
intermediate_position_up: 70
intermediate_position_down: 40
```
If you have not programmed any intermediate positions in your cover you should
If you have not programmed any intermediate positions in your cover, you should
disable the intermediate cover position.
```yaml
- platform: becker
Expand All @@ -177,7 +184,7 @@ disable the intermediate cover position.
intermediate_position: off
```

### Tilt intermediate
## Tilt intermediate
The Becker integration provide the ability to control the intermediate position from
Home Assistant user interface. Therefore the tilt functionality of Home Assistant is used
to issue the commands to drive to intermediate positions.
Expand All @@ -194,7 +201,7 @@ This will also disable the service `cover.close_cover_tilt` and `cover.open_cove
```
Note: You still need to set the intermediate cover position appropriately!

### Tilt blind
## Tilt blind
The Becker integration provides support for blinds. The Becker blinds allow to control
their tilt position by short press of the UP or DOWN button on their master remote.
A long press of the UP or DOWN button fully open or closes the blinds.
Expand All @@ -212,7 +219,7 @@ This time can be adapted to your needs.
tilt_time_blind: 0.5
```

## Pairing the Becker USB Stick with your covers
# Pairing the Becker USB Stick with your covers
To use your cover in HA you need to pair it first with the Becker USB stick. The
pairing is always between your remote and the shutter. The shutter will react on
the commands of all paired remotes.
Expand All @@ -237,7 +244,7 @@ data:
unit: 1
```

## Troubleshooting
# Troubleshooting
If you have any trouble follow these steps:
- Restart Home Assistant after you have plugged in the USB stick
- Enable debug log for becker.
Expand All @@ -250,7 +257,17 @@ logger:
# This must correspond to the folder name of your /config/custom_components/becker folder
custom_components.becker: debug
```
This enable DEBUG messages to the home-assistant.log file in your config folder.

You can also change the log configuration dynamically by calling the `logger.set_level` service.
This method allows you to enable debug logging only for a limited time:

```yaml
service: logger.set_level
data:
custom_components.becker: debug
```

All messages are logged to the home-assistant.log file in your config folder.
It is also helpful to find out the Remote ID of your Becker Remote. The message
will be something like below every time you press a key on your Remote:
`... DEBUG ... \[custom_components.becker.pybecker.becker_helper]` Received packet:
Expand All @@ -263,3 +280,5 @@ Becker integration:
[Integrating Becker Motors](https://community.home-assistant.io/t/integrating-becker-motors-in-to-hassio/151705)
Another way is to open a new issue on
[GitHub](https://github.com/RainerStaude/hass-becker-component-plus-pybecker/issues).

To disable debug log for becker set the level back from `debug` to `info`.
32 changes: 10 additions & 22 deletions cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import time
import voluptuous as vol
from xknx.devices import TravelCalculator
from .travelcalculator import TravelCalculator

from homeassistant.core import callback
from homeassistant.exceptions import (
Expand Down Expand Up @@ -350,7 +350,7 @@ async def async_open_cover_tilt(self, **kwargs):
await self.async_open_cover()
self._update_scheduled_stop_travel_callback(self._tilt_time_blind)
if self._tilt_intermediate:
self._travel_up_intermediate()
self._travel_to_position(self._intermediate_pos_up)
await self._becker.move_up_intermediate(self._channel)

async def async_close_cover(self, **kwargs):
Expand All @@ -365,7 +365,7 @@ async def async_close_cover_tilt(self, **kwargs):
await self.async_close_cover()
self._update_scheduled_stop_travel_callback(self._tilt_time_blind)
if self._tilt_intermediate:
self._travel_down_intermediate()
self._travel_to_position(self._intermediate_pos_down)
await self._becker.move_down_intermediate(self._channel)

async def async_stop_cover(self, **kwargs):
Expand All @@ -383,14 +383,14 @@ async def async_set_cover_position(self, **kwargs):
await self._becker.move_down(self._channel)
elif self._tc.is_opening():
await self._becker.move_up(self._channel)
if 0 < self._tc.travel_to_position < 100:
if 0 < pos < 100:
self._update_scheduled_stop_travel_callback(travel_time)

def _travel_to_position(self, position):
"""Start TravelCalculator and update ha-state."""
# In TravelCalculator 0 is open, 100 is closed.
travel_time = self._tc._calculate_travel_time(
self.current_cover_position - position
travel_time = self._tc.calculate_travel_time(
self.current_cover_position, position
)
if self._template is None:
_LOGGER.debug(
Expand All @@ -409,20 +409,6 @@ def _travel_stop(self):
_LOGGER.debug("%s stopped at position %s", self.name, self.current_cover_position)
self._update_scheduled_ha_state_callback(0)

def _travel_up_intermediate(self):
pos = self.current_cover_position
if pos >= self._intermediate_pos_up or not self._intermediate_position:
self._travel_to_position(OPEN_POSITION)
else:
self._travel_to_position(self._intermediate_pos_up)

def _travel_down_intermediate(self):
pos = self.current_cover_position
if pos <= self._intermediate_pos_down or not self._intermediate_position:
self._travel_to_position(CLOSED_POSITION)
else:
self._travel_to_position(self._intermediate_pos_down)

def _update_scheduled_ha_state_callback(self, delay=None):
"""
Update ha-state callback
Expand Down Expand Up @@ -481,12 +467,14 @@ async def _async_message_received(self, packet):
if self._tilt_blind and (self.is_opening or self.is_closing):
self._travel_stop()
elif cmd_arg == COMMANDS['up_intermediate'] and self._intermediate_position:
self._travel_up_intermediate()
self._travel_to_position(self._intermediate_pos_up)
self._tilt_timeout = time.time() # reset timeout
elif command == COMMANDS['up']:
self._travel_to_position(OPEN_POSITION)
self._tilt_timeout = time.time() + TILT_RECEIVE_TIMEOUT
elif cmd_arg == COMMANDS['down_intermediate'] and self._intermediate_position:
self._travel_down_intermediate()
self._travel_to_position(self._intermediate_pos_down)
self._tilt_timeout = time.time() # reset timeout
elif command == COMMANDS['down']:
self._travel_to_position(CLOSED_POSITION)
self._tilt_timeout = time.time() + TILT_RECEIVE_TIMEOUT
Expand Down
6 changes: 2 additions & 4 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
"domain": "becker",
"name": "Becker",
"documentation": "",
"version": "0.3.1",
"requirements": [
"xknx"
],
"version": "0.3.2",
"requirements": [],
"dependencies": [],
"codeowners": ["@nicolasberthel", "@RainerStaude"]
}
6 changes: 3 additions & 3 deletions pybecker/becker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# pylint: disable=missing-class-docstring
import logging
import re
import time
import asyncio
from random import randrange

from .becker_helper import finalize_code
Expand Down Expand Up @@ -147,7 +147,7 @@ async def run_codes(self, channel, unit, cmd, test):
unit[1] += 1
await self.write([code])

time.sleep(int(mt.group(2)))
await asyncio.sleep(int(mt.group(2)))

# stop moving
code = generate_code(channel, unit, COMMAND_HALT)
Expand Down Expand Up @@ -276,4 +276,4 @@ async def init_unconfigured_unit(self, channel, name=None):
"Init call to %s:%s #%d", un, 1, init_call_count)
await self.stop(':'.join((str(un), '1')))
# 0.5 to 0.9 seconds (works with my Roto cover)
time.sleep(randrange(5, 10, 1) / 10)
await asyncio.sleep(randrange(5, 10, 1) / 10)
2 changes: 2 additions & 0 deletions pybecker/database.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# pylint: disable=missing-function-docstring
# pylint: disable=missing-class-docstring
import logging
import os
import time
Expand Down
Loading

0 comments on commit 97c41d8

Please sign in to comment.