PyHolfuy 0.0__tar.gz → 0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
- Name: PyHolfuy
3
- Version: 0.0
2
+ Name: pyholfuy
3
+ Version: 0.2
4
4
  Summary: A Python library for interacting with Holfuy weather stations.
5
5
  Author-email: Håvard Dagenborg <haavardj@dagenborg.net>
6
6
  License-Expression: MIT
@@ -16,15 +16,7 @@ Provides-Extra: test
16
16
  Requires-Dist: aioresponses==0.7.8; extra == "test"
17
17
  Requires-Dist: coverage==7.8.0; extra == "test"
18
18
  Requires-Dist: mypy==1.15.0; extra == "test"
19
- Requires-Dist: pytest-asyncio==0.26.0; extra == "test"
20
- Requires-Dist: pytest-cov==6.1.1; extra == "test"
21
- Requires-Dist: pytest-error-for-skips==2.0.2; extra == "test"
22
- Requires-Dist: pytest-timeout==2.3.1; extra == "test"
23
- Requires-Dist: pytest==8.3.5; extra == "test"
24
- Requires-Dist: ruff==0.11.7; extra == "test"
25
- Requires-Dist: syrupy==4.9.1; extra == "test"
26
19
  Provides-Extra: dev
27
- Requires-Dist: pre-commit==4.2.0; extra == "dev"
28
20
  Dynamic: license-file
29
21
 
30
22
  ![Holfy Logo](https://holfuy.com/image/logo/holfuy-logo.png)
@@ -6,9 +6,10 @@ import logging
6
6
  from typing import Any
7
7
 
8
8
  import aiohttp
9
+ from aiohttp import ClientSession
9
10
 
10
11
  from .const import (
11
- CONF_TEMPERATURE_UNIT_CELSUIS,
12
+ CONF_TEMPERATURE_UNIT_CELSIUS,
12
13
  CONF_WIND_SPEED_UNIT_MS,
13
14
  CONF_AVERAGING_NEWEST,
14
15
  CONF_MODE_JSON,
@@ -45,10 +46,10 @@ class HolfuyService:
45
46
  def __init__(
46
47
  self,
47
48
  api_key: str,
48
- websession=None,
49
- temperature_unit=CONF_TEMPERATURE_UNIT_CELSUIS,
50
- wind_speed_unit=CONF_WIND_SPEED_UNIT_MS,
51
- timestamps_in_utc=False,
49
+ session: ClientSession,
50
+ temperature_unit:str=CONF_TEMPERATURE_UNIT_CELSIUS,
51
+ wind_speed_unit:str=CONF_WIND_SPEED_UNIT_MS,
52
+ timestamps_in_utc:bool=False,
52
53
  ) -> None:
53
54
  """Initialize the Weather object."""
54
55
 
@@ -57,16 +58,8 @@ class HolfuyService:
57
58
  self._wind_speed_unit = wind_speed_unit
58
59
  self._temperature_unit = temperature_unit
59
60
  self._timestamps_in_utc = timestamps_in_utc
61
+ self._session = session
60
62
 
61
- if websession is None:
62
-
63
- async def _create_session():
64
- return aiohttp.ClientSession()
65
-
66
- loop = asyncio.get_event_loop()
67
- self._websession = loop.run_until_complete(_create_session())
68
- else:
69
- self._websession = websession
70
63
 
71
64
  async def fetch_data(
72
65
  self,
@@ -104,8 +97,9 @@ class HolfuyService:
104
97
  params["s"] = STATIONS_ALL
105
98
 
106
99
  try:
100
+
107
101
  async with asyncio.timeout(TIMEOUT):
108
- resp = await self._websession.get(self._api_url, params=params)
102
+ resp = await self._session.get(self._api_url, params=params)
109
103
 
110
104
  if resp.status >= 400:
111
105
  _LOGGER.error("%s returned %s", self._api_url, resp.status)
@@ -117,7 +111,7 @@ class HolfuyService:
117
111
  # measurements objects. Make data consistent for all request types before it
118
112
  # is returned.
119
113
  if stations and len(stations) == 1:
120
- result = {"measurements": result}
114
+ result = {"measurements": [result]}
121
115
 
122
116
  return result
123
117
 
@@ -11,7 +11,7 @@ CONF_WIND_SPEED_UNIT_KMH = "km/h"
11
11
  CONF_WIND_SPEED_UNIT_MS = "m/s"
12
12
  CONF_WIND_SPEED_UNIT_MPH = "mph"
13
13
 
14
- CONF_TEMPERATURE_UNIT_CELSUIS = "Celsius"
14
+ CONF_TEMPERATURE_UNIT_CELSIUS = "Celsius"
15
15
  CONF_TEMPERATURE_UNIT_FAHRENHEIT = "Fahrenheit"
16
16
 
17
17
  CONF_OPTION_BATT = "batt"
@@ -0,0 +1,156 @@
1
+ import asyncio
2
+ from unittest import IsolatedAsyncioTestCase, mock
3
+ from unittest.mock import AsyncMock, patch
4
+ import json
5
+
6
+ import aiohttp
7
+
8
+ RESP_142 = json.loads("""{"stationId":142,"stationName":"THPK Ersfjord","location":{"latitude":"69.70017",
9
+ "longitude":"18.63842","altitude":130},"dateTime":"2025-05-19 15:45:00","wind":{"speed":9.4,"gust":13.3,"min":0,
10
+ "unit":"m/s","direction":245},"battery":4.15,"daily":{"max_temp":0,"min_temp":0},"temperature":0}""")
11
+
12
+ RESP_ALL = json.loads("""{"measurements":[{"stationId":142,"stationName":"THPK Ersfjord","location":{"latitude":"69.70017",
13
+ "longitude":"18.63842","altitude":130},"dateTime":"2025-05-19 15:45:00","wind":{"speed":9.4,"gust":13.3,"min":0,
14
+ "unit":"m/s","direction":245},"battery":4.15,"daily":{"max_temp":0,"min_temp":0},"temperature":0},
15
+ {"stationId":207,"stationName":"THPK Finnvikdalen","location":{"latitude":"69.76117",
16
+ "longitude":"18.85657","altitude":270},"dateTime":"2025-05-19 15:45:00",
17
+ "wind":{"speed":10,"gust":13.6,"min":0,"unit":"m/s","direction":339},"battery":4.14,
18
+ "daily":{"max_temp":7.5,"min_temp":4.5},"temperature":4.7},{"stationId":299,"stationName":"THPK Fjellheisen",
19
+ "location":{"latitude":"69.63116","longitude":"18.99377","altitude":420},"dateTime":"2025-05-19 15:45:00",
20
+ "wind":{"speed":7.8,"gust":10.3,"min":0,"unit":"m/s","direction":256},"battery":4.14,
21
+ "daily":{"max_temp":5.7,"min_temp":0},"temperature":3.6}]}""")
22
+
23
+ RESP_AUTHERROR=json.loads("""
24
+ {"measurements":[{"stationId":105,"error":"Sorry, you don't have access for this station.","errorCode":"no_access"},
25
+ {"stationId":106,"error":"Sorry, you don't have access for this station.","errorCode":"no_access"}]}""")
26
+
27
+ from holfuy import HolfuyService
28
+
29
+ from holfuy.const import (
30
+ CONF_TEMPERATURE_UNIT_CELSIUS,
31
+ CONF_WIND_SPEED_UNIT_MS,
32
+ CONF_AVERAGING_NEWEST,
33
+ )
34
+
35
+
36
+ class TestHolfuyService(IsolatedAsyncioTestCase):
37
+ def setUp(self):
38
+ """Set up test cases."""
39
+ self.session = AsyncMock(spec=aiohttp.ClientSession)
40
+ self.api = HolfuyService(
41
+ "test_api_key",
42
+ self.session,
43
+ CONF_TEMPERATURE_UNIT_CELSIUS,
44
+ CONF_WIND_SPEED_UNIT_MS,
45
+ False,
46
+ )
47
+
48
+ async def test_fetch_data_single_station(self):
49
+ """Test fetching data for a single station."""
50
+ mock_response = RESP_142
51
+
52
+ mock_resp = AsyncMock()
53
+ mock_resp.status = 200
54
+ mock_resp.json = AsyncMock(return_value=mock_response)
55
+
56
+ self.session.get = AsyncMock(return_value=mock_resp)
57
+
58
+ result = await self.api.fetch_data(["142"])
59
+
60
+ expected_params = {
61
+ "pw": "test_api_key",
62
+ "m": "JSON",
63
+ "tu": CONF_TEMPERATURE_UNIT_CELSIUS,
64
+ "su": CONF_WIND_SPEED_UNIT_MS,
65
+ "avg": CONF_AVERAGING_NEWEST,
66
+ "s": "142",
67
+ }
68
+
69
+ self.session.get.assert_called_once_with(
70
+ "http://api.holfuy.com/live/",
71
+ params=expected_params
72
+ )
73
+
74
+ self.assertEqual(result["measurements"][0]["stationId"], 142)
75
+
76
+ async def test_fetch_data_all_stations(self):
77
+ """Test fetching data for multiple stations."""
78
+ mock_response = RESP_ALL
79
+
80
+ mock_resp = AsyncMock()
81
+ mock_resp.status = 200
82
+ mock_resp.json = AsyncMock(return_value=mock_response)
83
+ self.session.get = AsyncMock(return_value= mock_resp)
84
+
85
+ result = await self.api.fetch_data(None)
86
+
87
+ expected_params = {
88
+ "pw": "test_api_key",
89
+ "m": "JSON",
90
+ "tu": CONF_TEMPERATURE_UNIT_CELSIUS,
91
+ "su": CONF_WIND_SPEED_UNIT_MS,
92
+ "avg": CONF_AVERAGING_NEWEST,
93
+ "s": "all",
94
+ }
95
+
96
+ self.session.get.assert_called_once_with(
97
+ "http://api.holfuy.com/live/",
98
+ params=expected_params
99
+ )
100
+
101
+ self.assertEqual(result, mock_response)
102
+
103
+ async def test_fetch_data_error_response(self):
104
+ """Test handling of error responses."""
105
+ mock_resp = AsyncMock()
106
+ mock_resp.status = 404
107
+
108
+ self.session.get = AsyncMock(return_value=mock_resp)
109
+
110
+ result = await self.api.fetch_data(None)
111
+
112
+ self.assertIsNone(result)
113
+
114
+ async def test_fetch_data_invalid_access_token(self):
115
+ """Test handling of error responses."""
116
+ mock_resp = AsyncMock()
117
+ mock_resp.status = 200
118
+ mock_response = RESP_AUTHERROR
119
+
120
+ mock_resp.json = AsyncMock(return_value=mock_response)
121
+
122
+ self.session.get = AsyncMock(return_value=mock_resp)
123
+
124
+ result = await self.api.fetch_data(["105","106"])
125
+
126
+ expected_params = {
127
+ "pw": "test_api_key",
128
+ "m": "JSON",
129
+ "tu": CONF_TEMPERATURE_UNIT_CELSIUS,
130
+ "su": CONF_WIND_SPEED_UNIT_MS,
131
+ "avg": CONF_AVERAGING_NEWEST,
132
+ "s": "105,106",
133
+ }
134
+
135
+ self.session.get.assert_called_once_with(
136
+ "http://api.holfuy.com/live/",
137
+ params=expected_params
138
+ )
139
+
140
+ self.assertEqual(result["measurements"][0]["stationId"], 105)
141
+
142
+ async def test_fetch_data_timeout(self):
143
+ """Test handling of timeout errors."""
144
+ self.session.get = AsyncMock(side_effect=asyncio.TimeoutError)
145
+
146
+ result = await self.api.fetch_data(None)
147
+
148
+ self.assertIsNone(result)
149
+
150
+ async def test_fetch_data_client_error(self):
151
+ """Test handling of client errors."""
152
+ self.session.get = AsyncMock(side_effect=aiohttp.ClientError)
153
+
154
+ result = await self.api.fetch_data(None)
155
+
156
+ self.assertIsNone(result)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
- Name: PyHolfuy
3
- Version: 0.0
2
+ Name: pyholfuy
3
+ Version: 0.2
4
4
  Summary: A Python library for interacting with Holfuy weather stations.
5
5
  Author-email: Håvard Dagenborg <haavardj@dagenborg.net>
6
6
  License-Expression: MIT
@@ -16,15 +16,7 @@ Provides-Extra: test
16
16
  Requires-Dist: aioresponses==0.7.8; extra == "test"
17
17
  Requires-Dist: coverage==7.8.0; extra == "test"
18
18
  Requires-Dist: mypy==1.15.0; extra == "test"
19
- Requires-Dist: pytest-asyncio==0.26.0; extra == "test"
20
- Requires-Dist: pytest-cov==6.1.1; extra == "test"
21
- Requires-Dist: pytest-error-for-skips==2.0.2; extra == "test"
22
- Requires-Dist: pytest-timeout==2.3.1; extra == "test"
23
- Requires-Dist: pytest==8.3.5; extra == "test"
24
- Requires-Dist: ruff==0.11.7; extra == "test"
25
- Requires-Dist: syrupy==4.9.1; extra == "test"
26
19
  Provides-Extra: dev
27
- Requires-Dist: pre-commit==4.2.0; extra == "dev"
28
20
  Dynamic: license-file
29
21
 
30
22
  ![Holfy Logo](https://holfuy.com/image/logo/holfuy-logo.png)
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ holfuy/__init__.py
6
+ holfuy/const.py
7
+ holfuy/test_holfuy.py
8
+ pyholfuy.egg-info/PKG-INFO
9
+ pyholfuy.egg-info/SOURCES.txt
10
+ pyholfuy.egg-info/dependency_links.txt
11
+ pyholfuy.egg-info/requires.txt
12
+ pyholfuy.egg-info/top_level.txt
@@ -0,0 +1,8 @@
1
+ aiohttp>=3.9.4
2
+
3
+ [dev]
4
+
5
+ [test]
6
+ aioresponses==0.7.8
7
+ coverage==7.8.0
8
+ mypy==1.15.0
@@ -5,8 +5,8 @@ requires = ["setuptools >= 77.0.3"]
5
5
  build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
- name = "PyHolfuy"
9
- version = "0.0"
8
+ name = "pyholfuy"
9
+ version = "0.2"
10
10
  description = "A Python library for interacting with Holfuy weather stations."
11
11
  authors = [{name="Håvard Dagenborg", email="haavardj@dagenborg.net"}]
12
12
  license = "MIT"
@@ -29,24 +29,11 @@ test = [
29
29
  "aioresponses==0.7.8",
30
30
  "coverage==7.8.0",
31
31
  "mypy==1.15.0",
32
- "pytest-asyncio==0.26.0",
33
- "pytest-cov==6.1.1",
34
- "pytest-error-for-skips==2.0.2",
35
- "pytest-timeout==2.3.1",
36
- "pytest==8.3.5",
37
- "ruff==0.11.7",
38
- "syrupy==4.9.1",
39
32
  ]
40
33
  dev = [
41
- "pre-commit==4.2.0",
42
34
  ]
43
35
 
44
- [tool.ruff]
45
- line-length = 88
46
- indent-width = 4
47
- select = ["E", "F", "W", "C", "N", "Q"]
48
36
 
49
37
  [tool.pylint]
50
38
  max-line-length = 88
51
39
  disable = ["C0114", "C0115", "C0116"]
52
-
@@ -1,11 +0,0 @@
1
- LICENSE
2
- MANIFEST.in
3
- README.md
4
- pyproject.toml
5
- PyHolfuy.egg-info/PKG-INFO
6
- PyHolfuy.egg-info/SOURCES.txt
7
- PyHolfuy.egg-info/dependency_links.txt
8
- PyHolfuy.egg-info/requires.txt
9
- PyHolfuy.egg-info/top_level.txt
10
- holfuy/__init__.py
11
- holfuy/const.py
@@ -1,16 +0,0 @@
1
- aiohttp>=3.9.4
2
-
3
- [dev]
4
- pre-commit==4.2.0
5
-
6
- [test]
7
- aioresponses==0.7.8
8
- coverage==7.8.0
9
- mypy==1.15.0
10
- pytest-asyncio==0.26.0
11
- pytest-cov==6.1.1
12
- pytest-error-for-skips==2.0.2
13
- pytest-timeout==2.3.1
14
- pytest==8.3.5
15
- ruff==0.11.7
16
- syrupy==4.9.1
File without changes
File without changes
File without changes
File without changes