File: voucher.py

package info (click to toggle)
python-aiounifi 79-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 660 kB
  • sloc: python: 11,124; sh: 5; makefile: 5
file content (230 lines) | stat: -rw-r--r-- 6,279 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
"""Hotspot vouchers as part of a UniFi network."""

from dataclasses import dataclass
from datetime import datetime, timedelta
from enum import StrEnum
from typing import NotRequired, Self, TypedDict

from .api import ApiItem, ApiRequest


class VoucherStatus(StrEnum):
    """Voucher status."""

    VALID_ONE = "VALID_ONE"
    VALID_MULTI = "VALID_MULTI"
    USED_MULTIPLE = "USED_MULTIPLE"


class TypedVoucher(TypedDict):
    """Voucher type definition."""

    _id: str
    site_id: str
    note: NotRequired[str]
    code: str
    quota: int
    duration: float
    qos_overwrite: NotRequired[bool]
    qos_usage_quota: NotRequired[str]
    qos_rate_max_up: NotRequired[int]
    qos_rate_max_down: NotRequired[int]
    used: int
    create_time: float
    start_time: NotRequired[float]
    end_time: NotRequired[float]
    for_hotspot: NotRequired[bool]
    admin_name: str
    status: VoucherStatus
    status_expires: float


@dataclass
class VoucherListRequest(ApiRequest):
    """Request object for voucher list."""

    @classmethod
    def create(cls) -> Self:
        """Create voucher list request."""
        return cls(
            method="get",
            path="/stat/voucher",
        )


@dataclass
class VoucherCreateRequest(ApiRequest):
    """Request object for voucher create."""

    @classmethod
    def create(
        cls,
        expire_number: int,
        expire_unit: int = 1,
        number: int = 1,
        quota: int = 0,
        usage_quota: int | None = None,
        rate_max_up: int | None = None,
        rate_max_down: int | None = None,
        note: str | None = None,
    ) -> Self:
        """Create voucher create request.

        :param expire_number: expiration of voucher per expire_unit
        :param expire_unit: scale of expire_number, 1 = minute, 60 = hour, 3600 = day
        :param number: number of vouchers
        :param quota: number of using; 0 = unlimited
        :param usage_quota: quantity of bytes allowed in MB
        :param rate_max_up: up speed allowed in kbps
        :param rate_max_down: down speed allowed in kbps
        :param note: description
        """
        data = {
            "cmd": "create-voucher",
            "n": number,
            "quota": quota,
            "expire_number": expire_number,
            "expire_unit": expire_unit,
        }
        if usage_quota:
            data["bytes"] = usage_quota
        if rate_max_up:
            data["up"] = rate_max_up
        if rate_max_down:
            data["down"] = rate_max_down
        if note:
            data["note"] = note

        return cls(
            method="post",
            path="/cmd/hotspot",
            data=data,
        )


@dataclass
class VoucherDeleteRequest(ApiRequest):
    """Request object for voucher delete."""

    @classmethod
    def create(
        cls,
        obj_id: str,
    ) -> Self:
        """Create voucher delete request."""
        data = {
            "cmd": "delete-voucher",
            "_id": obj_id,
        }
        return cls(
            method="post",
            path="/cmd/hotspot",
            data=data,
        )


class Voucher(ApiItem):
    """Represents a voucher."""

    raw: TypedVoucher

    @property
    def id(self) -> str:
        """ID of voucher."""
        return self.raw["_id"]

    @property
    def site_id(self) -> str:
        """Site ID."""
        return self.raw["site_id"]

    @property
    def note(self) -> str:
        """Note given by creator to voucher."""
        return self.raw.get("note") or ""

    @property
    def code(self) -> str:
        """Code of voucher in known format 00000-00000.

        To enter the code on the captive portal, the hyphen must be placed after the fifth digit.
        """
        c = self.raw.get("code", "")
        # API returns the code without a hyphen. But this is necessary. Separate the API string after the fifth digit.
        return f"{c[:5]}-{c[5:]}"

    @property
    def quota(self) -> int:
        """Allowed uses (0 = unlimited) of voucher."""
        return self.raw["quota"]

    @property
    def duration(self) -> timedelta:
        """Expiration of voucher."""
        return timedelta(minutes=self.raw["duration"])

    @property
    def qos_overwrite(self) -> bool:
        """Defaults for QoS overwritten by the use of this voucher."""
        return self.raw.get("qos_overwrite", False)

    @property
    def qos_usage_quota(self) -> int:
        """Quantity of bytes (in MB) allowed when using this voucher."""
        return int(self.raw.get("qos_usage_quota", 0))

    @property
    def qos_rate_max_up(self) -> int:
        """Up speed (in kbps) allowed when using this voucher."""
        return self.raw.get("qos_rate_max_up", 0)

    @property
    def qos_rate_max_down(self) -> int:
        """Down speed (in kbps) allowed when using this voucher."""
        return self.raw.get("qos_rate_max_down", 0)

    @property
    def used(self) -> int:
        """Number of uses of this voucher."""
        return self.raw["used"]

    @property
    def create_time(self) -> datetime:
        """Create datetime of voucher."""
        return datetime.fromtimestamp(self.raw["create_time"])

    @property
    def start_time(self) -> datetime | None:
        """Start datetime of first usage of voucher."""
        if "start_time" in self.raw:
            return datetime.fromtimestamp(self.raw["start_time"])
        return None

    @property
    def end_time(self) -> datetime | None:
        """End datetime of latest usage of voucher."""
        if "end_time" in self.raw:
            return datetime.fromtimestamp(self.raw["end_time"])
        return None

    @property
    def for_hotspot(self) -> bool:
        """For hotspot use."""
        return self.raw.get("for_hotspot", False)

    @property
    def admin_name(self) -> str:
        """Creator name of voucher."""
        return self.raw["admin_name"]

    @property
    def status(self) -> VoucherStatus:
        """Status of voucher."""
        return self.raw["status"]

    @property
    def status_expires(self) -> timedelta | None:
        """Status expires in seconds."""
        if (status_expiry := self.raw["status_expires"]) > 0:
            return timedelta(seconds=status_expiry)
        return None