Skip to content

Commit e371945

Browse files
committed
Added time control switches
1 parent 7e75390 commit e371945

File tree

16 files changed

+491
-34
lines changed

16 files changed

+491
-34
lines changed

custom_components/huawei_mesh_router/classes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class EmulatedSwitch(StrEnum):
3535
DEVICE_ACCESS = "wlan_device_access_switch"
3636
URL_FILTER = "url_filter_switch"
3737
PORT_MAPPING = "port_mapping"
38+
TIME_CONTROL = "time_control"
3839

3940

4041
# ---------------------------

custom_components/huawei_mesh_router/client/classes.py

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
from datetime import datetime, time
56
from dataclasses import dataclass
67
from enum import Enum, IntEnum, StrEnum
78
from typing import Any, Dict, Final, Iterable, TypeAlias
@@ -20,21 +21,22 @@
2021
# Feature
2122
# ---------------------------
2223
class Feature(StrEnum):
23-
NFC: Final = "feature_nfc"
24+
NFC = "feature_nfc"
2425
URL_FILTER = "feature_url_filter"
2526
WIFI_80211R = "feature_wifi_80211r"
2627
WIFI_TWT = "feature_wifi_twt"
2728
WLAN_FILTER = "feature_wlan_filter"
2829
DEVICE_TOPOLOGY = "feature_device_topology"
2930
GUEST_NETWORK = "feature_guest_network"
3031
PORT_MAPPING = "feature_port_mapping"
32+
TIME_CONTROL = "feature_time_control"
3133

3234

3335
# ---------------------------
3436
# Switch
3537
# ---------------------------
3638
class Switch(StrEnum):
37-
NFC: Final = "nfc_switch"
39+
NFC = "nfc_switch"
3840
WIFI_80211R = "wifi_80211r_switch"
3941
WIFI_TWT = "wifi_twt_switch"
4042
WLAN_FILTER = "wlan_filter_switch"
@@ -129,6 +131,19 @@ class HuaweiUrlFilterInfo:
129131
devices: list[HuaweiFilterItem]
130132

131133

134+
# ---------------------------
135+
# DayOfWeek
136+
# ---------------------------
137+
class DayOfWeek(StrEnum):
138+
MONDAY = "Monday"
139+
TUESDAY = "Tuesday"
140+
WEDNESDAY = "Wednesday"
141+
THURSDAY = "Thursday"
142+
FRIDAY = "Friday"
143+
SATURDAY = "Saturday"
144+
SUNDAY = "Sunday"
145+
146+
132147
# ---------------------------
133148
# HuaweiPortMappingItem
134149
# ---------------------------
@@ -387,3 +402,94 @@ def connected_devices(self) -> Iterable[HuaweiDeviceNode]:
387402
def add_device(self, device: HuaweiDeviceNode) -> None:
388403
"""Add connected node to the device."""
389404
self._connected_devices.append(device)
405+
406+
407+
# ---------------------------
408+
# HuaweiTimeAccessItem
409+
# ---------------------------
410+
class HuaweiTimeControlItemDay:
411+
def __init__(
412+
self, day_of_week: DayOfWeek, is_enabled: bool, start: time, end: time
413+
):
414+
"""Initialize."""
415+
self._day_of_week = day_of_week
416+
self._is_enabled = is_enabled
417+
self._start = start
418+
self._end = end
419+
420+
@property
421+
def day_of_week(self) -> DayOfWeek:
422+
"""Return the day of week."""
423+
return self._day_of_week
424+
425+
@property
426+
def is_enabled(self) -> bool:
427+
"""Return the state of the day."""
428+
return self._is_enabled
429+
430+
@property
431+
def start(self) -> time:
432+
"""Return the start time at this day."""
433+
return self._start
434+
435+
@property
436+
def end(self) -> time:
437+
"""Return the end time at this day."""
438+
return self._end
439+
440+
441+
# ---------------------------
442+
# HuaweiTimeAccessItem
443+
# ---------------------------
444+
class HuaweiTimeControlItem:
445+
def __init__(self, data: Dict):
446+
"""Initialize."""
447+
self._data = data
448+
self._days: dict[DayOfWeek, HuaweiTimeControlItemDay] = {}
449+
450+
for day_of_week in DayOfWeek:
451+
452+
enabled_value = self._data.get(f"{day_of_week.value}enable")
453+
is_enabled: bool = isinstance(enabled_value, bool) and enabled_value
454+
455+
start_value = self._data.get(f"{day_of_week.value}From", "00:00")
456+
start: time = datetime.strptime(start_value, "%H:%M").time()
457+
458+
end_value = self._data.get(f"{day_of_week.value}To", "00:00")
459+
end: time = datetime.strptime(end_value, "%H:%M").time()
460+
461+
day: HuaweiTimeControlItemDay = HuaweiTimeControlItemDay(
462+
day_of_week, is_enabled, start, end
463+
)
464+
465+
self._days[day_of_week] = day
466+
467+
@property
468+
def id(self) -> str:
469+
"""Return the state of the item."""
470+
return self._data.get("ID")
471+
472+
@property
473+
def name(self) -> str:
474+
"""Return the name of the item."""
475+
parts = [part for part in self.id.split(".") if part]
476+
last_non_empty = parts[-1] if parts else "?"
477+
return f"Time limit rule {last_non_empty}"
478+
479+
@property
480+
def enabled(self) -> bool:
481+
"""Return the state of the item."""
482+
value = self._data.get("Enable")
483+
return isinstance(value, bool) and value
484+
485+
@property
486+
def days(self) -> dict[DayOfWeek, HuaweiTimeControlItemDay]:
487+
"""Return the schedule for each day."""
488+
return self._days
489+
490+
def update(self, source: HuaweiTimeControlItem) -> None:
491+
self._data = source._data
492+
self._days = source._days
493+
494+
def set_enabled(self, enabled: bool) -> None:
495+
self._data["Enable"] = enabled

custom_components/huawei_mesh_router/client/const.py

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

88
URL_DEVICE_INFO: Final = "api/system/deviceinfo"
99
URL_DEVICE_TOPOLOGY: Final = "api/device/topology"
10+
URL_GUEST_NETWORK: Final = "api/ntwk/guest_network?type=notshowpassall"
1011
URL_HOST_INFO: Final = "api/system/HostInfo"
12+
URL_PORT_MAPPING: Final = "api/ntwk/portmapping"
13+
URL_REBOOT: Final = "api/service/reboot.cgi"
14+
URL_REPEATER_INFO: Final = "api/ntwk/repeaterinfo"
1115
URL_SWITCH_NFC: Final = "api/bsp/nfc_switch"
1216
URL_SWITCH_WIFI_80211R: Final = "api/ntwk/WlanGuideBasic?type=notshowpassall"
1317
URL_SWITCH_WIFI_TWT: Final = "api/ntwk/WlanGuideBasic?type=notshowpassall"
14-
URL_REBOOT: Final = "api/service/reboot.cgi"
15-
URL_REPEATER_INFO: Final = "api/ntwk/repeaterinfo"
16-
URL_WANDETECT: Final = "api/ntwk/wandetect"
17-
URL_WLAN_FILTER: Final = "api/ntwk/wlanfilterenhance"
18+
URL_TIME_CONTROL: Final = "api/ntwk/timecontrol"
1819
URL_URL_FILTER: Final = "api/ntwk/urlfilter"
19-
URL_GUEST_NETWORK: Final = "api/ntwk/guest_network?type=notshowpassall"
2020
URL_WAN_INFO: Final = "api/ntwk/wan?type=active"
21-
URL_PORT_MAPPING: Final = "api/ntwk/portmapping"
21+
URL_WANDETECT: Final = "api/ntwk/wandetect"
22+
URL_WLAN_FILTER: Final = "api/ntwk/wlanfilterenhance"

custom_components/huawei_mesh_router/client/huaweiapi.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
HuaweiPortMappingItem,
2323
HuaweiRouterInfo,
2424
HuaweiRsaPublicKey,
25+
HuaweiTimeControlItem,
2526
HuaweiUrlFilterInfo,
2627
Switch,
2728
)
@@ -36,6 +37,7 @@
3637
URL_SWITCH_NFC,
3738
URL_SWITCH_WIFI_80211R,
3839
URL_SWITCH_WIFI_TWT,
40+
URL_TIME_CONTROL,
3941
URL_URL_FILTER,
4042
URL_WANDETECT,
4143
URL_WLAN_FILTER,
@@ -719,7 +721,7 @@ async def set_port_mapping_state(self, port_mapping_id: str, enabled: bool) -> N
719721
)
720722

721723
if not target:
722-
raise InvalidActionError(f"Unknown filter: {port_mapping_id}")
724+
raise InvalidActionError(f"Unknown port mapping: {port_mapping_id}")
723725

724726
target["Enable"] = enabled
725727

@@ -754,3 +756,30 @@ async def _set_guest_network_enabled(self, enabled: bool) -> None:
754756
secure=primary_item.sec_opt != WIFI_SECURITY_OPEN,
755757
password=primary_item.key,
756758
)
759+
760+
async def get_time_control_items(self) -> Iterable[HuaweiTimeControlItem]:
761+
data = await self._core_api.get(URL_TIME_CONTROL)
762+
return [HuaweiTimeControlItem(item) for item in data]
763+
764+
async def set_time_control_item_state(
765+
self, time_control_item_id: str, enabled: bool
766+
) -> None:
767+
time_control_items = await self._core_api.get(URL_TIME_CONTROL)
768+
target = next(
769+
(
770+
item
771+
for item in time_control_items
772+
if item.get("ID") == time_control_item_id
773+
)
774+
)
775+
776+
if not target:
777+
raise InvalidActionError(
778+
f"Unknown time control item: {time_control_item_id}"
779+
)
780+
781+
target["Enable"] = enabled
782+
783+
await self._core_api.post(
784+
URL_TIME_CONTROL, target, extra_data={"action": "update"}
785+
)

custom_components/huawei_mesh_router/client/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
URL_SWITCH_NFC,
1010
URL_SWITCH_WIFI_80211R,
1111
URL_SWITCH_WIFI_TWT,
12+
URL_TIME_CONTROL,
1213
URL_URL_FILTER,
1314
URL_WLAN_FILTER,
1415
)
@@ -110,6 +111,12 @@ async def _is_port_mapping_available(self) -> bool:
110111
data = await self._core_api.get(URL_PORT_MAPPING)
111112
return data is not None
112113

114+
@log_feature(Feature.TIME_CONTROL)
115+
@unauthorized_as_false
116+
async def _is_time_control_available(self) -> bool:
117+
data = await self._core_api.get(URL_TIME_CONTROL)
118+
return data is not None
119+
113120
async def update(self) -> None:
114121
"""Update the available features list."""
115122
if await self._is_nfc_available():
@@ -136,6 +143,9 @@ async def update(self) -> None:
136143
if await self._is_port_mapping_available():
137144
self._available_features.add(Feature.PORT_MAPPING)
138145

146+
if await self._is_time_control_available():
147+
self._available_features.add(Feature.TIME_CONTROL)
148+
139149
def is_available(self, feature: Feature) -> bool:
140150
"""Return true if feature is available."""
141151
return feature in self._available_features

custom_components/huawei_mesh_router/config_flow.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
DEFAULT_ROUTER_CLIENTS_SENSORS,
3333
DEFAULT_SCAN_INTERVAL,
3434
DEFAULT_SSL,
35+
DEFAULT_TIME_CONTROL_SWITCHES,
3536
DEFAULT_URL_FILTER_SWITCHES,
3637
DEFAULT_USER,
3738
DEFAULT_VERIFY_SSL,
@@ -43,6 +44,7 @@
4344
OPT_EVENT_ENTITIES,
4445
OPT_PORT_MAPPING_SWITCHES,
4546
OPT_ROUTER_CLIENTS_SENSORS,
47+
OPT_TIME_CONTROL_SWITCHES,
4648
OPT_URL_FILTER_SWITCHES,
4749
OPT_WIFI_ACCESS_SWITCHES,
4850
)
@@ -251,6 +253,12 @@ async def async_step_features_select(self, user_input=None) -> FlowResult:
251253
OPT_PORT_MAPPING_SWITCHES, DEFAULT_PORT_MAPPING_SWITCHES
252254
),
253255
): bool,
256+
vol.Required(
257+
OPT_TIME_CONTROL_SWITCHES,
258+
default=self.options.get(
259+
OPT_TIME_CONTROL_SWITCHES, DEFAULT_TIME_CONTROL_SWITCHES
260+
),
261+
): bool,
254262
},
255263
),
256264
)

custom_components/huawei_mesh_router/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
OPT_DEVICE_TRACKER = "device_tracker"
2121
OPT_DEVICE_TRACKER_ZONES = "device_tracker_zones"
2222
OPT_EVENT_ENTITIES = "event_entities"
23+
OPT_TIME_CONTROL_SWITCHES = "time_control_switches"
2324

2425
DEFAULT_HOST: Final = "192.168.3.1"
2526
DEFAULT_USER: Final = "admin"
@@ -37,6 +38,7 @@
3738
DEFAULT_URL_FILTER_SWITCHES: Final = False
3839
DEFAULT_PORT_MAPPING_SWITCHES: Final = False
3940
DEFAULT_EVENT_ENTITIES: Final = False
41+
DEFAULT_TIME_CONTROL_SWITCHES: Final = False
4042

4143
ATTR_MANUFACTURER: Final = "Huawei"
4244
PLATFORMS: Final = [

custom_components/huawei_mesh_router/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
"iot_class": "local_polling",
1414
"issue_tracker": "https://github.com/vmakeev/huawei_mesh_router/issues",
1515
"requirements": ["pycryptodome>=3.12.0"],
16-
"version": "0.9.2.1"
16+
"version": "0.9.3"
1717
}

custom_components/huawei_mesh_router/options.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
DEFAULT_ROUTER_CLIENTS_SENSORS,
1212
DEFAULT_EVENT_ENTITIES,
1313
DEFAULT_SCAN_INTERVAL,
14+
DEFAULT_TIME_CONTROL_SWITCHES,
1415
DEFAULT_URL_FILTER_SWITCHES,
1516
DEFAULT_WIFI_ACCESS_SWITCHES,
1617
OPT_DEVICE_TRACKER,
@@ -19,6 +20,7 @@
1920
OPT_EVENT_ENTITIES,
2021
OPT_PORT_MAPPING_SWITCHES,
2122
OPT_ROUTER_CLIENTS_SENSORS,
23+
OPT_TIME_CONTROL_SWITCHES,
2224
OPT_URL_FILTER_SWITCHES,
2325
OPT_WIFI_ACCESS_SWITCHES,
2426
)
@@ -103,3 +105,12 @@ def event_entities(self) -> bool:
103105
OPT_EVENT_ENTITIES,
104106
DEFAULT_EVENT_ENTITIES,
105107
)
108+
109+
@property
110+
def time_control_switches(self) -> bool:
111+
"""Return option 'time control switches' value"""
112+
return get_option(
113+
self._config_entry,
114+
OPT_TIME_CONTROL_SWITCHES,
115+
DEFAULT_TIME_CONTROL_SWITCHES,
116+
)

0 commit comments

Comments
 (0)