|
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