|
3 | 3 | import binascii |
4 | 4 | import logging |
5 | 5 | import socket |
| 6 | +import time |
6 | 7 |
|
7 | 8 | _LOGGER = logging.getLogger(__name__) |
8 | 9 |
|
|
27 | 28 | ON = b'\x01' |
28 | 29 | OFF = b'\x00' |
29 | 30 |
|
| 31 | +# Timeout after which to renew device subscriptions |
| 32 | +SUBSCRIPTION_TIMEOUT = 60 |
| 33 | + |
30 | 34 |
|
31 | 35 | def _is_discovery_response(data): |
32 | 36 | """ Is this a discovery response? |
@@ -77,6 +81,8 @@ def __init__(self, host): |
77 | 81 | self._socket.bind(('', PORT)) |
78 | 82 | (self._mac, self._mac_reversed) = self._discover_mac() |
79 | 83 |
|
| 84 | + self._subscribe() |
| 85 | + |
80 | 86 | @property |
81 | 87 | def on(self): |
82 | 88 | """ State property. |
@@ -131,18 +137,27 @@ def _subscribe(self): |
131 | 137 | + PADDING_1 + self._mac_reversed + PADDING_1 |
132 | 138 | status = self._udp_transact(cmd, self._subscribe_resp) |
133 | 139 | if status is not None: |
| 140 | + self.last_subscribed = time.time() |
134 | 141 | return status == ON |
135 | 142 | else: |
136 | 143 | raise S20Exception( |
137 | 144 | "No status could be found for {}".format(self.host)) |
138 | 145 |
|
| 146 | + def _subscription_is_recent(self): |
| 147 | + return self.last_subscribed > time.time() - SUBSCRIPTION_TIMEOUT |
| 148 | + |
139 | 149 | def _control(self, state): |
140 | 150 | """ Control device state. |
141 | 151 |
|
142 | 152 | Possible states are ON or OFF. |
143 | 153 |
|
144 | 154 | :param state: Switch to this state. |
145 | 155 | """ |
| 156 | + |
| 157 | + # Renew subscription if necessary |
| 158 | + if not self._subscription_is_recent(): |
| 159 | + self._subscribe() |
| 160 | + |
146 | 161 | cmd = MAGIC + CONTROL + self._mac + PADDING_1 + PADDING_2 + state |
147 | 162 | _LOGGER.debug("Sending new state to %s: %s", self.host, ord(state)) |
148 | 163 | ack_state = self._udp_transact(cmd, self._control_resp, state) |
@@ -217,18 +232,16 @@ def _udp_transact(self, payload, handler, *args, |
217 | 232 | # From the right device? |
218 | 233 | if addr[0] == self.host: |
219 | 234 | retval = handler(data, *args) |
| 235 | + # Return as soon as a response is received |
| 236 | + if retval: |
| 237 | + return retval |
220 | 238 | except socket.timeout: |
221 | 239 | break |
222 | | - if retval: |
223 | | - break |
224 | | - return retval |
225 | 240 |
|
226 | 241 | def _turn_on(self): |
227 | 242 | """ Turn on the device. """ |
228 | | - if not self._subscribe(): |
229 | | - self._control(ON) |
| 243 | + self._control(ON) |
230 | 244 |
|
231 | 245 | def _turn_off(self): |
232 | 246 | """ Turn off the device. """ |
233 | | - if self._subscribe(): |
234 | | - self._control(OFF) |
| 247 | + self._control(OFF) |
0 commit comments