Skip to content

Commit 83fbfdb

Browse files
authored
added weather_status API (#11)
* added `weather_status` API
1 parent 2c88185 commit 83fbfdb

File tree

11 files changed

+199
-36
lines changed

11 files changed

+199
-36
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Project cloud-py-api was **abandoned** and divided into two parts:
2020
* ~~Operations with Trash bin and File versions~~
2121
* Operations with Users and User Groups
2222
* User status manipulation
23+
* Weather status
2324
* ~~Nextcloud notifications support~~
2425
* ~~Shares operations support~~
2526
* ~~Talk support~~

nc_py_api/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class ApiScope(IntEnum):
2121
USER_INFO = 10
2222
USER_STATUS = 11
2323
NOTIFICATIONS = 12
24+
WEATHER_STATUS = 13
2425

2526

2627
class ApiScopesStruct(TypedDict):

nc_py_api/nextcloud.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,30 @@
1515
from .preferences import PreferencesAPI
1616
from .theming import ThemingInfo, get_parsed_theme
1717
from .ui_files_actions_menu import UiFilesActionsAPI
18-
from .users import UserAPI
19-
from .users_groups import UsersGroupsAPI
20-
from .users_statuses import UsersStatusesAPI
18+
from .users import UsersAPI
19+
from .users_groups import UserGroupsAPI
20+
from .users_status import UserStatusAPI
21+
from .weather_status import WeatherStatusAPI
2122

2223

2324
class NextcloudBasic(ABC):
2425
apps: AppAPI
2526
files: FilesAPI
2627
preferences_api: PreferencesAPI
27-
users: UserAPI
28-
users_groups: UsersGroupsAPI
29-
users_statuses: UsersStatusesAPI
28+
users: UsersAPI
29+
users_groups: UserGroupsAPI
30+
users_status: UserStatusAPI
31+
weather_status: WeatherStatusAPI
3032
_session: NcSessionBasic
3133

3234
def _init_api(self, session: NcSessionBasic):
3335
self.apps = AppAPI(session)
3436
self.files = FilesAPI(session)
3537
self.preferences_api = PreferencesAPI(session)
36-
self.users = UserAPI(session)
37-
self.users_groups = UsersGroupsAPI(session)
38-
self.users_statuses = UsersStatusesAPI(session)
38+
self.users = UsersAPI(session)
39+
self.users_groups = UserGroupsAPI(session)
40+
self.users_status = UserStatusAPI(session)
41+
self.weather_status = WeatherStatusAPI(session)
3942

4043
@property
4144
def capabilities(self) -> dict:

nc_py_api/users.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
ENDPOINT = f"{ENDPOINT_BASE}/users"
1212

1313

14-
class UserAPI:
14+
class UsersAPI:
1515
def __init__(self, session: NcSessionBasic):
1616
self._session = session
1717

nc_py_api/users_groups.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class GroupDetails(TypedDict):
1919
can_remove: bool
2020

2121

22-
class UsersGroupsAPI:
22+
class UserGroupsAPI:
2323
def __init__(self, session: NcSessionBasic):
2424
self._session = session
2525

nc_py_api/users_statuses.py renamed to nc_py_api/users_status.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class CurrentUserStatus(UserStatus):
3232
statusIsUserDefined: bool
3333

3434

35-
class UsersStatusesAPI:
35+
class UserStatusAPI:
3636
def __init__(self, session: NcSessionBasic):
3737
self._session = session
3838

nc_py_api/weather_status.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
Nextcloud API for working with weather statuses.
3+
"""
4+
5+
from enum import IntEnum
6+
from typing import Optional, TypedDict, Union
7+
8+
from ._session import NcSessionBasic
9+
from .misc import check_capabilities, require_capabilities
10+
11+
ENDPOINT = "/ocs/v2.php/apps/weather_status/api/v1"
12+
13+
14+
class WeatherLocationMode(IntEnum):
15+
UNKNOWN = 0
16+
MODE_BROWSER_LOCATION = 1
17+
MODE_MANUAL_LOCATION = 2
18+
19+
20+
class WeatherLocation(TypedDict):
21+
latitude: float
22+
longitude: float
23+
address: str
24+
mode: WeatherLocationMode
25+
26+
27+
class WeatherStatusAPI:
28+
def __init__(self, session: NcSessionBasic):
29+
self._session = session
30+
31+
@property
32+
def available(self) -> bool:
33+
return not check_capabilities("weather_status", self._session.capabilities)
34+
35+
def get_location(self) -> WeatherLocation:
36+
require_capabilities("weather_status", self._session.capabilities)
37+
result = self._session.ocs(method="GET", path=f"{ENDPOINT}/location")
38+
lat = result.get("lat", "")
39+
lon = result.get("lon", "")
40+
return {
41+
"latitude": float(lat if lat else "0"),
42+
"longitude": float(lon if lon else "0"),
43+
"address": result.get("address", ""),
44+
"mode": WeatherLocationMode(int(result.get("mode", 0))),
45+
}
46+
47+
def set_location(
48+
self, latitude: Optional[float] = None, longitude: Optional[float] = None, address: Optional[str] = None
49+
) -> bool:
50+
require_capabilities("weather_status", self._session.capabilities)
51+
params: dict[str, Union[str, float]] = {}
52+
if latitude is not None and longitude is not None:
53+
params.update({"lat": latitude, "lon": longitude})
54+
elif address:
55+
params["address"] = address
56+
else:
57+
raise ValueError("latitude & longitude or address should be present")
58+
result = self._session.ocs(method="PUT", path=f"{ENDPOINT}/location", params=params)
59+
return result.get("success", False)
60+
61+
def get_forecast(self) -> list[dict]:
62+
require_capabilities("weather_status", self._session.capabilities)
63+
return self._session.ocs(method="GET", path=f"{ENDPOINT}/forecast")
64+
65+
def get_favorites(self) -> list[str]:
66+
require_capabilities("weather_status", self._session.capabilities)
67+
return self._session.ocs(method="GET", path=f"{ENDPOINT}/favorites")
68+
69+
def set_favorites(self, favorites: list[str]) -> bool:
70+
require_capabilities("weather_status", self._session.capabilities)
71+
result = self._session.ocs(method="PUT", path=f"{ENDPOINT}/favorites", json={"favorites": favorites})
72+
return result.get("success", False)
73+
74+
def set_mode(self, mode: WeatherLocationMode) -> bool:
75+
if int(mode) == WeatherLocationMode.UNKNOWN.value:
76+
raise ValueError("This mode can not be set")
77+
require_capabilities("weather_status", self._session.capabilities)
78+
result = self._session.ocs(method="PUT", path=f"{ENDPOINT}/mode", params={"mode": int(mode)})
79+
return result.get("success", False)

tests/_install.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ def enabled_handler(enabled: bool, _nc: NextcloudApp) -> str:
1616
def initialization():
1717
set_enabled_handler(APP, enabled_handler)
1818
set_scopes(APP, {
19-
"required": [ApiScope.SYSTEM, ApiScope.DAV, ApiScope.USER_INFO, ApiScope.USER_STATUS, ApiScope.NOTIFICATIONS],
19+
"required": [ApiScope.SYSTEM, ApiScope.DAV, ApiScope.USER_INFO, ApiScope.USER_STATUS,
20+
ApiScope.NOTIFICATIONS, ApiScope.WEATHER_STATUS],
2021
"optional": []
2122
})
2223

File renamed without changes.

tests/users_statuses_test.py renamed to tests/users_status_test.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@pytest.mark.parametrize("nc", NC_TO_TEST)
99
def test_available(nc):
10-
assert nc.users_statuses.available
10+
assert nc.users_status.available
1111

1212

1313
def compare_user_statuses(p1, p2):
@@ -21,9 +21,9 @@ def compare_user_statuses(p1, p2):
2121
@pytest.mark.parametrize("nc", NC_TO_TEST)
2222
@pytest.mark.parametrize("message", ("1 2 3", None, ""))
2323
def test_get_status(nc, message):
24-
nc.users_statuses.set(message)
25-
r1 = nc.users_statuses.get_current()
26-
r2 = nc.users_statuses.get(nc.user)
24+
nc.users_status.set(message)
25+
r1 = nc.users_status.get_current()
26+
r2 = nc.users_status.get(nc.user)
2727
compare_user_statuses(r1, r2)
2828
assert r1["userId"] == "admin"
2929
assert r1["icon"] is None
@@ -37,12 +37,12 @@ def test_get_status(nc, message):
3737

3838
@pytest.mark.parametrize("nc", NC_TO_TEST)
3939
def test_get_status_non_existent_user(nc):
40-
assert nc.users_statuses.get("no such user") is None
40+
assert nc.users_status.get("no such user") is None
4141

4242

4343
@pytest.mark.parametrize("nc", NC_TO_TEST)
4444
def test_get_predefined(nc):
45-
r = nc.users_statuses.get_predefined()
45+
r = nc.users_status.get_predefined()
4646
if nc.srv_version["major"] < 27:
4747
assert r == []
4848
else:
@@ -57,10 +57,10 @@ def test_get_predefined(nc):
5757

5858
@pytest.mark.parametrize("nc", NC_TO_TEST)
5959
def test_get_list(nc):
60-
r_all = nc.users_statuses.get_list()
60+
r_all = nc.users_status.get_list()
6161
assert r_all
6262
assert isinstance(r_all, list)
63-
r_current = nc.users_statuses.get_current()
63+
r_current = nc.users_status.get_current()
6464
for i in r_all:
6565
if i["userId"] == nc.user:
6666
compare_user_statuses(i, r_current)
@@ -69,18 +69,18 @@ def test_get_list(nc):
6969
@pytest.mark.parametrize("nc", NC_TO_TEST)
7070
def test_set_status(nc):
7171
time_clear = int(time()) + 60
72-
nc.users_statuses.set("cool status", time_clear)
73-
r = nc.users_statuses.get_current()
72+
nc.users_status.set("cool status", time_clear)
73+
r = nc.users_status.get_current()
7474
assert r["message"] == "cool status"
7575
assert r["clearAt"] == time_clear
7676
assert r["icon"] is None
77-
nc.users_statuses.set("Sick!", status_icon='🤒')
78-
r = nc.users_statuses.get_current()
77+
nc.users_status.set("Sick!", status_icon='🤒')
78+
r = nc.users_status.get_current()
7979
assert r["message"] == "Sick!"
8080
assert r["clearAt"] is None
8181
assert r["icon"] == '🤒'
82-
nc.users_statuses.set(None)
83-
r = nc.users_statuses.get_current()
82+
nc.users_status.set(None)
83+
r = nc.users_status.get_current()
8484
assert r["message"] is None
8585
assert r["clearAt"] is None
8686
assert r["icon"] is None
@@ -89,8 +89,8 @@ def test_set_status(nc):
8989
@pytest.mark.parametrize("nc", NC_TO_TEST)
9090
@pytest.mark.parametrize("value", ("online", "away", "dnd", "invisible", "offline"))
9191
def test_set_status_type(nc, value):
92-
nc.users_statuses.set_status_type(value)
93-
r = nc.users_statuses.get_current()
92+
nc.users_status.set_status_type(value)
93+
r = nc.users_status.get_current()
9494
assert r["status"] == value
9595
assert r["statusIsUserDefined"]
9696

@@ -99,12 +99,12 @@ def test_set_status_type(nc, value):
9999
@pytest.mark.parametrize("clear_at", (None, int(time()) + 360))
100100
def test_set_predefined(nc, clear_at):
101101
if nc.srv_version["major"] < 27:
102-
nc.users_statuses.set_predefined("meeting")
102+
nc.users_status.set_predefined("meeting")
103103
else:
104-
predefined_statuses = nc.users_statuses.get_predefined()
104+
predefined_statuses = nc.users_status.get_predefined()
105105
for i in predefined_statuses:
106-
nc.users_statuses.set_predefined(i["id"], clear_at)
107-
r = nc.users_statuses.get_current()
106+
nc.users_status.set_predefined(i["id"], clear_at)
107+
r = nc.users_status.get_current()
108108
assert r["message"] == i["message"]
109109
assert r["messageId"] == i["id"]
110110
assert r["messageIsPredefined"]
@@ -118,18 +118,18 @@ def test_get_back_status_from_from_empty_user(nc):
118118
nc._session.user = ""
119119
try:
120120
with pytest.raises(ValueError):
121-
nc.users_statuses.get_backup_status("")
121+
nc.users_status.get_backup_status("")
122122
finally:
123123
nc._session.user = orig_user
124124

125125

126126
@pytest.mark.parametrize("nc", NC_TO_TEST)
127127
@pytest.mark.skipif(NC_VERSION["major"] < 27, reason="Run only on NC27+")
128128
def test_get_back_status_from_from_non_exist_user(nc):
129-
assert nc.users_statuses.get_backup_status("mёm_m-m.l") is None
129+
assert nc.users_status.get_backup_status("mёm_m-m.l") is None
130130

131131

132132
@pytest.mark.parametrize("nc", NC_TO_TEST)
133133
@pytest.mark.skipif(NC_VERSION["major"] < 27, reason="Run only on NC27+")
134134
def test_restore_from_non_existing_back_status(nc):
135-
assert nc.users_statuses.restore_backup_status("no such backup status") is None
135+
assert nc.users_status.restore_backup_status("no such backup status") is None

0 commit comments

Comments
 (0)