Skip to content
This repository was archived by the owner on Mar 9, 2023. It is now read-only.

Commit cd08511

Browse files
committed
Remove old xknx dependency
Still relying on the old TravelCalculator method from 0.9.4 - which works well here. To avoid xknx version conflicts, as per @farmio suggestion dependency is removed by copying these functions to the component.
1 parent 1889aa7 commit cd08511

File tree

3 files changed

+164
-7
lines changed

3 files changed

+164
-7
lines changed

custom_components/cover_rf_time_based/cover.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import homeassistant.helpers.config_validation as cv
3030
from homeassistant.helpers.restore_state import RestoreEntity
3131

32+
from .travelcalculator import TravelCalculator
33+
from .travelcalculator import TravelStatus
34+
3235
_LOGGER = logging.getLogger(__name__)
3336

3437
CONF_DEVICES = 'devices'
@@ -175,7 +178,6 @@ def __init__(self,
175178
device_class,
176179
availability_template):
177180
"""Initialize the cover."""
178-
from xknx.devices import TravelCalculator
179181
self._travel_time_down = travel_time_down
180182
self._travel_time_up = travel_time_up
181183
self._open_script_entity_id = open_script_entity_id
@@ -269,14 +271,12 @@ def current_cover_position(self):
269271
@property
270272
def is_opening(self):
271273
"""Return if the cover is opening or not."""
272-
from xknx.devices import TravelStatus
273274
return self.tc.is_traveling() and \
274275
self.tc.travel_direction == TravelStatus.DIRECTION_UP
275276

276277
@property
277278
def is_closing(self):
278279
"""Return if the cover is closing or not."""
279-
from xknx.devices import TravelStatus
280280
return self.tc.is_traveling() and \
281281
self.tc.travel_direction == TravelStatus.DIRECTION_DOWN
282282

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
{
22
"domain": "cover_rf_time_based",
33
"name": "Cover Time Based RF (trigger script)",
4-
"version": "1.1.2",
4+
"version": "1.1.3",
55
"documentation": "https://github.com/nagyrobi/home-assistant-custom-components-cover-rf-time-based",
66
"iot_class": "assumed_state",
7-
"requirements": [
8-
"xknx==0.9.4"
9-
],
107
"codeowners": ["@davidramosweb", "@nagyrobi", "@Alfiegerner"]
118
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
Module TravelCalculator provides functionality for predicting the current position of a Cover.
3+
4+
E.g.:
5+
6+
* Given a Cover that takes 100 seconds to travel from top to bottom.
7+
* Starting from position 90, directed to position 60 at time 0.
8+
* At time 10 TravelCalculator will return position 80 (final position not reached).
9+
* At time 20 TravelCalculator will return position 70 (final position not reached).
10+
* At time 30 TravelCalculator will return position 60 (final position reached).
11+
12+
Borrowed from XKNX implementation: https://github.com/XKNX/xknx/blob/main/xknx/devices/travelcalculator.py release 0.9.4.
13+
Thanks @farmio https://github.com/nagyrobi/home-assistant-custom-components-cover-rf-time-based/issues/50#issuecomment-1212172751
14+
"""
15+
import time
16+
from enum import Enum
17+
18+
19+
class PositionType(Enum):
20+
"""Enum class for different type of calculated positions."""
21+
22+
UNKNOWN = 1
23+
CALCULATED = 2
24+
CONFIRMED = 3
25+
26+
27+
class TravelStatus(Enum):
28+
"""Enum class for travel status."""
29+
30+
DIRECTION_UP = 1
31+
DIRECTION_DOWN = 2
32+
STOPPED = 3
33+
34+
35+
class TravelCalculator:
36+
"""Class for calculating the current position of a cover."""
37+
38+
# pylint: disable=too-many-instance-attributes
39+
40+
def __init__(self, travel_time_down, travel_time_up):
41+
"""Initialize TravelCalculator class."""
42+
self.position_type = PositionType.UNKNOWN
43+
self.last_known_position = 0
44+
45+
self.travel_time_down = travel_time_down
46+
self.travel_time_up = travel_time_up
47+
48+
self.travel_to_position = 0
49+
self.travel_started_time = 0
50+
self.travel_direction = TravelStatus.STOPPED
51+
52+
# 0 is closed, 100 is fully open
53+
self.position_closed = 0
54+
self.position_open = 100
55+
56+
self.time_set_from_outside = None
57+
58+
def set_position(self, position):
59+
"""Set known position of cover."""
60+
self.last_known_position = position
61+
self.travel_to_position = position
62+
self.position_type = PositionType.CONFIRMED
63+
64+
def stop(self):
65+
"""Stop traveling."""
66+
self.last_known_position = self.current_position()
67+
self.travel_to_position = self.last_known_position
68+
self.position_type = PositionType.CALCULATED
69+
self.travel_direction = TravelStatus.STOPPED
70+
71+
def start_travel(self, travel_to_position):
72+
"""Start traveling to position."""
73+
self.stop()
74+
self.travel_started_time = self.current_time()
75+
self.travel_to_position = travel_to_position
76+
self.position_type = PositionType.CALCULATED
77+
78+
self.travel_direction = \
79+
TravelStatus.DIRECTION_UP \
80+
if travel_to_position > self.last_known_position else \
81+
TravelStatus.DIRECTION_DOWN
82+
83+
def start_travel_up(self):
84+
"""Start traveling up."""
85+
self.start_travel(self.position_open)
86+
87+
def start_travel_down(self):
88+
"""Start traveling down."""
89+
self.start_travel(self.position_closed)
90+
91+
def current_position(self):
92+
"""Return current (calculated or known) position."""
93+
if self.position_type == PositionType.CALCULATED:
94+
return self._calculate_position()
95+
return self.last_known_position
96+
97+
def is_traveling(self):
98+
"""Return if cover is traveling."""
99+
return self.current_position() != self.travel_to_position
100+
101+
def position_reached(self):
102+
"""Return if cover has reached designated position."""
103+
return self.current_position() == self.travel_to_position
104+
105+
def is_open(self):
106+
"""Return if cover is (fully) open."""
107+
return self.current_position() == self.position_open
108+
109+
def is_closed(self):
110+
"""Return if cover is (fully) closed."""
111+
return self.current_position() == self.position_closed
112+
113+
def _calculate_position(self):
114+
"""Return calculated position."""
115+
relative_position = self.travel_to_position - self.last_known_position
116+
117+
def position_reached_or_exceeded(relative_position):
118+
"""Return if designated position was reached."""
119+
if relative_position >= 0 \
120+
and self.travel_direction == TravelStatus.DIRECTION_DOWN:
121+
return True
122+
if relative_position <= 0 \
123+
and self.travel_direction == TravelStatus.DIRECTION_UP:
124+
return True
125+
return False
126+
127+
if position_reached_or_exceeded(relative_position):
128+
return self.travel_to_position
129+
130+
travel_time = self._calculate_travel_time(relative_position)
131+
if self.current_time() > self.travel_started_time + travel_time:
132+
return self.travel_to_position
133+
progress = (self.current_time()-self.travel_started_time)/travel_time
134+
position = self.last_known_position + relative_position * progress
135+
return int(position)
136+
137+
def _calculate_travel_time(self, relative_position):
138+
"""Calculate time to travel to relative position."""
139+
travel_direction = \
140+
TravelStatus.DIRECTION_UP \
141+
if relative_position > 0 else \
142+
TravelStatus.DIRECTION_DOWN
143+
travel_time_full = \
144+
self.travel_time_up \
145+
if travel_direction == TravelStatus.DIRECTION_UP else \
146+
self.travel_time_down
147+
travel_range = self.position_open - self.position_closed
148+
149+
return travel_time_full * abs(relative_position) / travel_range
150+
151+
def current_time(self):
152+
"""Get current time. May be modified from outside (for unit tests)."""
153+
# time_set_from_outside is used within unit tests
154+
if self.time_set_from_outside is not None:
155+
return self.time_set_from_outside
156+
return time.time()
157+
158+
def __eq__(self, other):
159+
"""Equal operator."""
160+
return self.__dict__ == other.__dict__

0 commit comments

Comments
 (0)