Skip to content

Commit 0011404

Browse files
committed
Implemented long_poll back into the mix
1 parent 2d224db commit 0011404

File tree

6 files changed

+77
-46
lines changed

6 files changed

+77
-46
lines changed

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "intellifire4py"
3-
version = "4.1.7"
3+
version = "4.1.8"
44
description = "Intellifire4Py"
55
authors = ["Jeff Stein <[email protected]>"]
66
license = "MIT"

src/intellifire4py/cloud_api.py

+60-40
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@
66

77
from asyncio import Task
88
from typing import Any
9-
9+
import json
1010
import aiohttp
1111
from aiohttp import CookieJar, ClientSession, ClientTimeout
1212

1313
from .exceptions import CloudError
1414
from .model import (
15-
IntelliFireFireplaceCloud,
1615
IntelliFireUserData,
1716
)
1817
from .model import IntelliFirePollData
1918

20-
from .const import IntelliFireCommand, IntelliFireApiMode
19+
from .const import IntelliFireCommand, IntelliFireApiMode, IntelliFireCloudPollType
2120

2221
from .control import IntelliFireController
2322
from .read import IntelliFireDataProvider
@@ -30,6 +29,7 @@ class IntelliFireAPICloud(IntelliFireController, IntelliFireDataProvider):
3029
"""Api for cloud access."""
3130

3231
_control_mode = IntelliFireApiMode.CLOUD
32+
_poll_mode = IntelliFireCloudPollType.LONG
3333

3434
def __init__(
3535
self,
@@ -87,8 +87,6 @@ def __init__(
8787
# Full data set on the user
8888
self._user_data: IntelliFireUserData = IntelliFireUserData()
8989

90-
self.last_send = None
91-
9290
def _get_session(self, timeout_seconds: float = 10.0) -> ClientSession:
9391
"""Ensure that the aiohttp ClientSession is created and open."""
9492
timeout = ClientTimeout(timeout_seconds)
@@ -150,7 +148,7 @@ async def _send_cloud_command(
150148
422 Invalid Parameter (invalid command id or command value)
151149
"""
152150
if response.status == 204:
153-
self.last_send = datetime.now()
151+
self._last_send = datetime.now()
154152
return
155153
# elif (
156154
# response.status == 403
@@ -165,7 +163,7 @@ async def _send_cloud_command(
165163
else:
166164
raise Exception(f"Unexpected return code {response.status}")
167165

168-
async def long_poll(self, fireplace: IntelliFireFireplaceCloud) -> bool:
166+
async def long_poll(self) -> bool:
169167
"""Perform a LongPoll to wait for a Status update.
170168
171169
Only returns a status update when the fireplace’s status actually changes (excluding normal periodic
@@ -192,30 +190,43 @@ async def long_poll(self, fireplace: IntelliFireFireplaceCloud) -> bool:
192190
bool: `True` if status changed, `False` if it did not
193191
"""
194192

195-
self._log.debug("Long Poll: Start")
196-
197-
async with self._get_session().get(
198-
f"{self.prefix}://iftapi.net/a/{self._serial}/applongpoll", timeout=61
199-
) as response:
200-
self._log.debug("Long Poll Status Code %d", response.status)
201-
if response.status == 200:
202-
self._log.debug("Long poll: 200 - Received data")
203-
self.last_long_poll = datetime.now()
204-
return True
205-
elif response.status == 408:
206-
self._log.debug("Long poll: 408 - No Data changed")
207-
self.last_long_poll = datetime.now()
208-
return False
209-
elif response.status == 403:
210-
raise CloudError("Not authorized")
211-
elif response.status == 404:
212-
raise CloudError("Fireplace not found (bad serial number)")
213-
else:
214-
response_text = await response.text()
215-
self._log.error(
216-
f"Unexpected status code: {response.status}, Response: {response_text}"
217-
)
218-
raise CloudError(f"Unexpected status code: {response.status}")
193+
long_poll_url = f"{self.prefix}://iftapi.net/a/{self._serial}/applongpoll"
194+
self._log.debug(f"long_poll() {long_poll_url}")
195+
196+
async with self._get_session() as session:
197+
try:
198+
response = await session.get(long_poll_url, timeout=61)
199+
200+
self._log.debug("Long Poll Status Code %d", response.status)
201+
if response.status == 200:
202+
self._log.debug("Long poll: 200 - Received data")
203+
204+
# Data has text/html header type so we need to manually convert it to json
205+
json_data = json.loads(await response.text())
206+
207+
self._data = IntelliFirePollData(**json_data)
208+
self._log.debug(f"poll() complete: {self._data}")
209+
210+
self._last_poll = datetime.now()
211+
return True
212+
elif response.status == 408:
213+
self._log.debug("Long poll: 408 - No Data changed")
214+
self._last_poll = datetime.now()
215+
return False
216+
except aiohttp.ClientResponseError as e:
217+
if e.status == 403:
218+
raise CloudError("Not authorized") from e
219+
if e.status == 404:
220+
raise CloudError("Fireplace not found (bad serial number)") from e
221+
else:
222+
response_text = await response.text()
223+
self._log.error(
224+
f"Unexpected status code: {response.status}, Response: {response_text}"
225+
)
226+
raise CloudError(
227+
f"Unexpected status code: {response.status}"
228+
) from e
229+
return False
219230

220231
async def poll(self, timeout_seconds: float = 10.0) -> None:
221232
"""Return a fireplace’s status in JSON.
@@ -266,8 +277,7 @@ async def poll(self, timeout_seconds: float = 10.0) -> None:
266277
267278
"""
268279

269-
serial = self._serial
270-
poll_url = f"{self.prefix}://iftapi.net/a/{serial}//apppoll"
280+
poll_url = f"{self.prefix}://iftapi.net/a/{self._serial}//apppoll"
271281

272282
self._log.debug(f"poll() {poll_url}")
273283
async with self._get_session() as session:
@@ -278,7 +288,7 @@ async def poll(self, timeout_seconds: float = 10.0) -> None:
278288
self._data = IntelliFirePollData(**json_data)
279289
self._log.debug(f"poll() complete: {self._data}")
280290

281-
self.last_poll = datetime.now()
291+
self._last_poll = datetime.now()
282292

283293
except aiohttp.ClientResponseError as e:
284294
if e.status == 403:
@@ -324,14 +334,20 @@ async def __background_poll(self, minimum_wait_in_seconds: int = 15) -> None:
324334
self._log.debug("__background_poll:: Loop start time %f", start)
325335

326336
try:
327-
# new_data = await self.long_poll()
337+
if self._poll_mode == IntelliFireCloudPollType.LONG:
338+
await self.long_poll()
339+
else:
340+
await self.poll()
341+
328342
#
329-
# if new_data:
330-
# self._log.debug(self.data)
343+
# # has_new_data = await self.long_poll()
344+
# #
345+
# # if new_data:
346+
# # self._log.debug(self.data)
347+
# #
348+
# # Long poll didn't seem to be working so switched to normal polling again
331349
#
332-
# Long poll didn't seem to be working so switched to normal polling again
333-
334-
await self.poll()
350+
# await self.poll()
335351

336352
end = time.time()
337353
duration: float = end - start
@@ -361,3 +377,7 @@ def data(self) -> IntelliFirePollData:
361377
): # pragma: no cover - the tests SHOULD be hitting this but dont appear to be
362378
self._log.warning("Returning uninitialized poll data") # pragma: no cover
363379
return self._data
380+
381+
def set_poll_mode(self, mode: IntelliFireCloudPollType):
382+
"""Set the poll mode."""
383+
self._poll_mode = mode

src/intellifire4py/const.py

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ class IntelliFireApiMode(Enum):
1818
NONE = "none"
1919

2020

21+
class IntelliFireCloudPollType(Enum):
22+
"""Polling type."""
23+
24+
LONG = "long"
25+
SHORT = "short"
26+
27+
2128
class IntelliFireErrorCode(MultiValueEnum): # type: ignore
2229
"""The following is a description of various error codes. These were obtained by decompiling the Android APK.
2330

src/intellifire4py/control.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def __init__(self, control_mode: IntelliFireApiMode): # pragma: no cover
1616
"""Initialize the controller knowing whether its local or cloud based."""
1717
self._control_mode = control_mode
1818
self._data = IntelliFirePollData()
19-
self.last_send: datetime | None = None
19+
self._last_send: datetime | None = None
2020

2121
async def flame_on(self) -> None:
2222
"""Turn on the flame."""

src/intellifire4py/local_api.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ async def poll(
205205
json_data = await response.json(content_type=None)
206206
self._data = IntelliFirePollData(**json_data)
207207
self._log.debug(f"poll() complete: {self._data}")
208-
self.last_poll = datetime.now()
208+
self._last_poll = datetime.now()
209209
except JSONDecodeError as error:
210210
if not suppress_warnings:
211211
self._log.warning("Error decoding JSON: [%s]", response.text)
@@ -339,7 +339,7 @@ async def _send_local_command(
339339
self._log.debug(
340340
"_send_local_command:: Response Code [%d]", resp.status
341341
)
342-
self.last_send = datetime.now()
342+
self._last_send = datetime.now()
343343
except asyncio.TimeoutError as error:
344344
self._log.warning("Control Endpoint Timeout Error %s", error)
345345
continue

src/intellifire4py/read.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ class IntelliFireDataProvider(ABC):
1313
def __init__(self) -> None:
1414
"""Define simple initializer."""
1515
self._data = IntelliFirePollData()
16-
self.last_poll: datetime | None = None
17-
self.last_long_poll: datetime | None = None
16+
self._last_poll: datetime | None = None
1817
pass
1918

19+
@property
20+
def last_poll(self) -> datetime | None:
21+
"""Return the last poll time."""
22+
return self._last_poll
23+
2024
@property
2125
@abstractmethod
2226
def data(self) -> IntelliFirePollData:

0 commit comments

Comments
 (0)