diff --git a/README.md b/README.md index a8ca8c3..7f6ec8e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![HACS Default](https://img.shields.io/badge/HACS-default-blue.svg?style=for-the-badge)](https://hacs.xyz) [![Community forum discussion](https://img.shields.io/badge/COMMUNITY-FORUM-success?style=for-the-badge&color=yellow)](https://community.home-assistant.io/t/custom-component-asusrouter-integration/416111)Buy Me A Coffee
[![Installations](https://img.shields.io/endpoint?url=https://ha-analytics.vaskivskyi.com/badges/asusrouter/total.json&style=for-the-badge&color=blue)](https://github.com/Vaskivskyi/ha-custom-analytics) +
+ ## Monitor and control your AsusWRT-powered router from Home Assistant `AsusRouter` is a custom integration for Home Assistant to monitor and control your AsusWRT (and [AsusWRT-Merlin](https://www.asuswrt-merlin.net/))-powered router using the [AsusRouter](https://github.com/Vaskivskyi/asusrouter) python library. @@ -28,7 +30,7 @@ The integration might show a different number of connected devices compared to t ## Firmware limitations -Firmware versions `3.0.0.4.x` and `3.0.0.6.x` are fully supported (older versions might have a limited amount of sensors available). When talking about the FW, `3.0.0.4` might be missed since it is the same all the time. Important is only the last part, e.g. `386.48631` or `106.xxxxx` for the stock or `386.7` for Merlin FW. +Firmware versions `3.0.0.4.x` and `3.0.0.6.x` are fully supported (older versions might have a limited amount of sensors available). When talking about the FW, `3.0.0.4` might be missed since it is the same all the time. Important is only the last part, e.g. `386.48631` or `102.xxxxx` for the stock or `386.7` for Merlin FW. Firmware `5.x.x` (some DSL models) is **NOT supported** (not AsusWRT). @@ -52,6 +54,7 @@ After AsusRouter is installed, you can add your device from Home Assistant UI. [![Open your Home Assistant instance and start setting up a new integration.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=asusrouter) To connect to the device you need to provide the following data: + - IP address or hostname - Username (the one you use to log into the WebUI) - Password @@ -64,12 +67,15 @@ Almost all the integration settings can be reconfigured later via the `Configure ## Features AsusRouter supports 14+ groups of features, including monitoring of: -- connected device, CPU, guest WLAN, LED, load average, network, OpenVPN, parental control, ports, RAM, temperature, WAN, WLAN. + +- connected device, CPU, guest WLAN, LED, Aura RGB, load average, network, OpenVPN, parental control, ports, RAM, temperature, WAN, WLAN. and control of: -- guest WLAN, LED, OpenVPN, parental control, WLAN. - as well as the following HA platforms: +- guest WLAN, LED, Aura RGB, OpenVPN, parental control, WLAN. + +as well as the following HA platforms: + - `binary_sensor`, `button`, `device_tracker`, `light`, `sensor`, `switch`, `update` and HA events and services. @@ -81,95 +87,106 @@ and HA events and services. AsusRouter supports virtually every AsusWRT-powered device. This list is purely based on the reports from the users. Other devices with the compatible firmware should work as well. ### WiFi 7 | 802.11be -|Model|Status|Tested firmware|Find it on Amazon[^amazon]| -|---|---|---|---| -|[GT-BE98](https://asusrouter.vaskivskyi.com/devices/GT-BE98.html)|💛 Expected to work||find it| -|[GT-BE98 Pro](https://asusrouter.vaskivskyi.com/devices/GT-BE98Pro.html)|💛 Expected to work||find it| -|[RT-BE88U](https://asusrouter.vaskivskyi.com/devices/RT-BE88U.html)|💛 Expected to work||find it| -|[RT-BE96U](https://asusrouter.vaskivskyi.com/devices/RT-BE96U.html)|💛 Expected to work||find it| -|[TUF-BE3600](https://asusrouter.vaskivskyi.com/devices/TUF-BE3600.html)|💛 Expected to work||find it| -|[TUF-BE6500](https://asusrouter.vaskivskyi.com/devices/TUF-BE6500.html)|💛 Expected to work||find it| -|[ZenWiFi BD4](https://asusrouter.vaskivskyi.com/devices/ZenWiFiBD4.html)|💛 Expected to work||find it| -|[ZenWiFi BQ16](https://asusrouter.vaskivskyi.com/devices/ZenWiFiBQ16.html)|💛 Expected to work||find it| -|[ZenWiFi BQ16 Pro](https://asusrouter.vaskivskyi.com/devices/ZenWiFiBQ16Pro.html)|💛 Expected to work||find it| -|[ZenWiFi BT10](https://asusrouter.vaskivskyi.com/devices/ZenWiFiBT10.html)|💛 Expected to work||find it| + +| Model | Status | Tested firmware | Find it on Amazon[^amazon] | +| ------------------------------------------------------------------------------- | ------------------- | -------------------------- | -------------------------------------------------------------------------------------- | +| [GT-BE19000](https://asusrouter.vaskivskyi.com/devices/GT-BE19000.md) | 💛 Expected to work | | find it | +| [GT-BE98](https://asusrouter.vaskivskyi.com/devices/GT-BE98.md) | 💚 Confirmed | Stock:
  • `102_34372`
  • | find it | +| [GT-BE98 Pro](https://asusrouter.vaskivskyi.com/devices/GT-BE98Pro.md) | 💛 Expected to work | | find it | +| [RT-BE58U](https://asusrouter.vaskivskyi.com/devices/RT-BE58U.md) | 💛 Expected to work | | find it | +| [RT-BE88U](https://asusrouter.vaskivskyi.com/devices/RT-BE88U.md) | 💛 Expected to work | | find it | +| [RT-BE92U](https://asusrouter.vaskivskyi.com/devices/RT-BE92U.md) | 💛 Expected to work | | find it | +| [RT-BE96U](https://asusrouter.vaskivskyi.com/devices/RT-BE96U.md) | 💛 Expected to work | | find it | +| [TUF-BE3600](https://asusrouter.vaskivskyi.com/devices/TUF-BE3600.md) | 💛 Expected to work | | find it | +| [TUF-BE6500](https://asusrouter.vaskivskyi.com/devices/TUF-BE6500.md) | 💛 Expected to work | | find it | +| [ZenWiFi BD4](https://asusrouter.vaskivskyi.com/devices/ZenWiFiBD4.md) | 💛 Expected to work | | find it | +| [ZenWiFi BQ16](https://asusrouter.vaskivskyi.com/devices/ZenWiFiBQ16.md) | 💛 Expected to work | | find it | +| [ZenWiFi BQ16 Pro](https://asusrouter.vaskivskyi.com/devices/ZenWiFiBQ16Pro.md) | 💛 Expected to work | | find it | +| [ZenWiFi BT10](https://asusrouter.vaskivskyi.com/devices/ZenWiFiBT10.md) | 💛 Expected to work | | find it | ### WiFi 6e | 802.11axe -|Model|Status|Tested firmware|Find it on Amazon[^amazon]| -|---|---|---|---| -|[GT-AXE11000](https://asusrouter.vaskivskyi.com/devices/GT-AXE11000.html)|💛 Expected to work||find it| -|[GT-AXE16000](https://asusrouter.vaskivskyi.com/devices/GT-AXE16000.html)|💚 Confirmed|Stock:
  • `388.21617`
  • |find it| -|[RT-AXE7800](https://asusrouter.vaskivskyi.com/devices/RT-AXE7800.html)|💚 Confirmed|Stock:
  • `388_22068`
  • |find it| -|[ZenWiFi ET8](https://asusrouter.vaskivskyi.com/devices/ZenWiFiET8.html)|💚 Confirmed|Stock:
  • `388.23759`
  • |find it| -|[ZenWiFi Pro ET12](https://asusrouter.vaskivskyi.com/devices/ZenWiFiProET12.html)|💚 Confirmed|Stock:
  • `388.23013`
  • |find it| + +| Model | Status | Tested firmware | Find it on Amazon[^amazon] | +| ------------------------------------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| [GT-AXE11000](https://asusrouter.vaskivskyi.com/devices/GT-AXE11000.md) | 💛 Expected to work | | find it | +| [GT-AXE16000](https://asusrouter.vaskivskyi.com/devices/GT-AXE16000.md) | 💚 Confirmed | Stock:
  • `388.21617`
  • Merlin:
  • `388.7_beta1_rog`
  • `388.7_0_rog`
  • | find it | +| [RT-AXE7800](https://asusrouter.vaskivskyi.com/devices/RT-AXE7800.md) | 💚 Confirmed | Stock:
  • `388_22068`
  • | find it | +| [ZenWiFi ET8](https://asusrouter.vaskivskyi.com/devices/ZenWiFiET8.md) | 💚 Confirmed | Stock:
  • `388.23759`
  • | find it | +| [ZenWiFi ET9](https://asusrouter.vaskivskyi.com/devices/ZenWiFiET9.md) | 💛 Expected to work | | find it | +| [ZenWiFi Pro ET12](https://asusrouter.vaskivskyi.com/devices/ZenWiFiProET12.md) | 💚 Confirmed | Stock:
  • `388.23013`
  • | find it | ### WiFi 6 | 802.11ax -|Model|Status|Tested firmware|Find it on Amazon[^amazon]| -|---|---|---|---| -|[DSL-AX82U](https://asusrouter.vaskivskyi.com/devices/DSL-AX82U.html)|💚 Confirmed|Merlin:
  • `386.07_0-gnuton0_beta2`
  • |find it| -|[GT-AX11000](https://asusrouter.vaskivskyi.com/devices/GT-AX11000.html)|💚 Confirmed|Merlin:
  • `386.7_2`
  • `388.1_0`
  • `388.4_0`
  • |find it| -|[GT-AX11000 Pro](https://asusrouter.vaskivskyi.com/devices/GT-AX11000Pro.html)|💚 Confirmed|Stock:
  • `388.24198`
  • |find it| -|[GT-AX6000](https://asusrouter.vaskivskyi.com/devices/GT-AX6000.html)|💛 Expected to work||find it| -|[GT6](https://asusrouter.vaskivskyi.com/devices/GT6.html)|💛 Expected to work||find it| -|[RT-AX3000P](https://asusrouter.vaskivskyi.com/devices/RT-AX3000P.html)|💛 Expected to work||find it| -|[RT-AX52](https://asusrouter.vaskivskyi.com/devices/RT-AX52.html)|💛 Expected to work||find it| -|[RT-AX53U](https://asusrouter.vaskivskyi.com/devices/RT-AX53U.html)|💚 Confirmed|Stock:
  • `386.69061`
  • |find it| -|[RT-AX5400](https://asusrouter.vaskivskyi.com/devices/RT-AX5400.html)|💛 Expected to work||find it| -|[RT-AX55](https://asusrouter.vaskivskyi.com/devices/RT-AX55.html)|💚 Confirmed|Stock:
  • `386.50410`
  • `386.52041`
  • |find it| -|[RT-AX56U](https://asusrouter.vaskivskyi.com/devices/RT-AX56U.html)|💚 Confirmed|Merlin:
  • `386.7_2`
  • `388.1_0`
  • `388.2_2`
  • |find it| -|[RT-AX57](https://asusrouter.vaskivskyi.com/devices/RT-AX57.html)|💛 Expected to work||find it| -|[RT-AX57 Go](https://asusrouter.vaskivskyi.com/devices/RT-AX57Go.html)|💛 Expected to work||find it| -|[RT-AX57M](https://asusrouter.vaskivskyi.com/devices/RT-AX57M.html)|💛 Expected to work||find it| -|[RT-AX58U](https://asusrouter.vaskivskyi.com/devices/RT-AX58U.html)|💚 Confirmed|Stock:
  • `386.49674`
  • `388.22237`
  • Merlin:
  • `386.7_2`
  • `388.1_0`
  • `388.4_0`
  • `388.7.0`
  • |find it| -|[RT-AX59U](https://asusrouter.vaskivskyi.com/devices/RT-AX59U.html)|💛 Expected to work||find it| -|[RT-AX68U](https://asusrouter.vaskivskyi.com/devices/RT-AX68U.html)|💚 Confirmed|Stock:
  • `388.21732`
  • |find it| -|[RT-AX82U](https://asusrouter.vaskivskyi.com/devices/RT-AX82U.html)|💚 Confirmed|Stock:
  • `386.48664`
  • `386.49674`
  • |find it| -|[RT-AX86S](https://asusrouter.vaskivskyi.com/devices/RT-AX86S.html)|💚 Confirmed|Stock:
  • `386.46061`
  • `386.48260`
  • `386.49447`
  • `388.22525`
  • Merlin:
  • `386.7_2`
  • |find it| -|[RT-AX86U](https://asusrouter.vaskivskyi.com/devices/RT-AX86U.html)|💚 Confirmed|Stock:
  • `386.46061`
  • `386.48260`
  • `386.49447`
  • `388.22525`
  • Merlin:
  • `386.7_2`
  • `388.4_0`
  • |find it| -|[RT-AX86U Pro](https://asusrouter.vaskivskyi.com/devices/RT-AX86UPro.html)|💚 Confirmed|Stock:
  • `388.23565`
  • |find it| -|[RT-AX88U](https://asusrouter.vaskivskyi.com/devices/RT-AX88U.html)|💚 Confirmed|Stock:
  • `386.45934`
  • `386.48631`
  • `388.24198`
  • Merlin:
  • `386.5_2`
  • `386.8_0`
  • `388.1_0`
  • `388.2_0`
  • `388.4_0`
  • `388.7_0`
  • |find it| -|[RT-AX88U Pro](https://asusrouter.vaskivskyi.com/devices/RT-AX88UPro.html)|💚 Confirmed|Merlin:
  • `388.4_0`
  • |find it| -|[RT-AX89X](https://asusrouter.vaskivskyi.com/devices/RT-AX89X.html)|💚 Confirmed||find it| -|[RT-AX92U](https://asusrouter.vaskivskyi.com/devices/RT-AX92U.html)|💚 Confirmed|Stock:
  • `386.46061`
  • |find it| -|[TUF-AX3000 V2](https://asusrouter.vaskivskyi.com/devices/TUF-AX3000V2.html)|💚 Confirmed|Stock:
  • `388.23785`
  • |find it| -|[TUF-AX4200](https://asusrouter.vaskivskyi.com/devices/TUF-AX4200.html)|💛 Expected to work||find it| -|[TUF-AX5400](https://asusrouter.vaskivskyi.com/devices/TUF-AX5400.html)|💚 Confirmed|Stock:
  • `386.50224`
  • `388.21224`
  • `388.22525`
  • `388.23285`
  • Merlin:
  • `388.4_0`
  • |find it| -|[TUF-AX6000](https://asusrouter.vaskivskyi.com/devices/TUF-AX6000.html)|💚 Confirmed|Stock:
  • `388.32432`
  • |find it| -|[ZenWiFi AX (XT8)](https://asusrouter.vaskivskyi.com/devices/ZenWiFiAX(XT8).html)|💚 Confirmed|Stock:
  • `386.48706`
  • `388.23285`
  • Merlin:
  • `386.7_2-gnuton1`
  • |find it| -|[ZenWiFi AX Hybrid (XP4)](https://asusrouter.vaskivskyi.com/devices/ZenWiFiAXHybrid(XP4).html)|💛 Expected to work||find it| -|[ZenWiFi AX Mini (XD4)](https://asusrouter.vaskivskyi.com/devices/ZenWiFiAXMini(XD4).html)|💚 Confirmed|Stock:
  • `386.48790`
  • `386.49599`
  • |find it| -|[ZenWiFi Pro XT12](https://asusrouter.vaskivskyi.com/devices/ZenWiFiProXT12.html)|💚 Confirmed|Stock:
  • `388.22127`
  • |find it| -|[ZenWiFi XD4 Plus](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD4Plus.html)|💛 Expected to work||find it| -|[ZenWiFi XD4S](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD4S.html)|💛 Expected to work||find it| -|[ZenWiFi XD5](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD5.html)|💛 Expected to work||find it| -|[ZenWiFi XD6](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD6.html)|💚 Confirmed|Stock:
  • `388.21380`
  • |find it| -|[ZenWiFi XD6S](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD6S.html)|💚 Confirmed|Stock:
  • `388.21380`
  • |find it| -|[ZenWiFi XT9](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXT9.html)|💚 Confirmed|Stock:
  • `388_23285`
  • |find it| + +| Model | Status | Tested firmware | Find it on Amazon[^amazon] | +| ---------------------------------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| [DSL-AX82U](https://asusrouter.vaskivskyi.com/devices/DSL-AX82U.md) | 💚 Confirmed | Merlin:
  • `386.07_0-gnuton0_beta2`
  • | find it | +| [GT-AX11000](https://asusrouter.vaskivskyi.com/devices/GT-AX11000.md) | 💚 Confirmed | Merlin:
  • `386.7_2`
  • `388.1_0`
  • `388.4_0`
  • `388.7_0_rog`
  • | find it | +| [GT-AX11000 Pro](https://asusrouter.vaskivskyi.com/devices/GT-AX11000Pro.md) | 💚 Confirmed | Stock:
  • `388.24198`
  • Merlin:
  • `388.7_0_rog`
  • | find it | +| [GT-AX6000](https://asusrouter.vaskivskyi.com/devices/GT-AX6000.md) | 💛 Expected to work | Merlin:
  • `388.7_beta1`
  • | find it | +| [GT6](https://asusrouter.vaskivskyi.com/devices/GT6.md) | 💛 Expected to work | | find it | +| [RP-AX56](https://asusrouter.vaskivskyi.com/devices/RP-AX56.md) | 💚 Confirmed | | find it | +| [RT-AX3000P](https://asusrouter.vaskivskyi.com/devices/RT-AX3000P.md) | 💛 Expected to work | | find it | +| [RT-AX52](https://asusrouter.vaskivskyi.com/devices/RT-AX52.md) | 💛 Expected to work | | find it | +| [RT-AX53U](https://asusrouter.vaskivskyi.com/devices/RT-AX53U.md) | 💚 Confirmed | Stock:
  • `386.69061`
  • | find it | +| [RT-AX5400](https://asusrouter.vaskivskyi.com/devices/RT-AX5400.md) | 💛 Expected to work | | find it | +| [RT-AX55](https://asusrouter.vaskivskyi.com/devices/RT-AX55.md) | 💚 Confirmed | Stock:
  • `386.50410`
  • `386.52041`
  • | find it | +| [RT-AX56U](https://asusrouter.vaskivskyi.com/devices/RT-AX56U.md) | 💚 Confirmed | Merlin:
  • `386.7_2`
  • `388.1_0`
  • `388.2_2`
  • | find it | +| [RT-AX57](https://asusrouter.vaskivskyi.com/devices/RT-AX57.md) | 💛 Expected to work | | find it | +| [RT-AX57 Go](https://asusrouter.vaskivskyi.com/devices/RT-AX57Go.md) | 💛 Expected to work | | find it | +| [RT-AX57M](https://asusrouter.vaskivskyi.com/devices/RT-AX57M.md) | 💛 Expected to work | | find it | +| [RT-AX58U](https://asusrouter.vaskivskyi.com/devices/RT-AX58U.md) | 💚 Confirmed | Stock:
  • `386.49674`
  • `388.22237`
  • Merlin:
  • `386.7_2`
  • `388.1_0`
  • `388.4_0`
  • `388.7.0`
  • | find it | +| [RT-AX59U](https://asusrouter.vaskivskyi.com/devices/RT-AX59U.md) | 💛 Expected to work | | find it | +| [RT-AX68U](https://asusrouter.vaskivskyi.com/devices/RT-AX68U.md) | 💚 Confirmed | Stock:
  • `388.21732`
  • | find it | +| [RT-AX82U](https://asusrouter.vaskivskyi.com/devices/RT-AX82U.md) | 💚 Confirmed | Stock:
  • `386.48664`
  • `386.49674`
  • | find it | +| [RT-AX86S](https://asusrouter.vaskivskyi.com/devices/RT-AX86S.md) | 💚 Confirmed | Stock:
  • `386.46061`
  • `386.48260`
  • `386.49447`
  • `388.22525`
  • Merlin:
  • `386.7_2`
  • | find it | +| [RT-AX86U](https://asusrouter.vaskivskyi.com/devices/RT-AX86U.md) | 💚 Confirmed | Stock:
  • `386.46061`
  • `386.48260`
  • `386.49447`
  • `388.22525`
  • Merlin:
  • `386.7_2`
  • `388.4_0`
  • `388.7_beta1`
  • | find it | +| [RT-AX86U Pro](https://asusrouter.vaskivskyi.com/devices/RT-AX86UPro.md) | 💚 Confirmed | Stock:
  • `388.23565`
  • | find it | +| [RT-AX88U](https://asusrouter.vaskivskyi.com/devices/RT-AX88U.md) | 💚 Confirmed | Stock:
  • `386.45934`
  • `386.48631`
  • `388.24198`
  • Merlin:
  • `386.5_2`
  • `386.8_0`
  • `388.1_0`
  • `388.2_0`
  • `388.4_0`
  • `388.7_0`
  • | find it | +| [RT-AX88U Pro](https://asusrouter.vaskivskyi.com/devices/RT-AX88UPro.md) | 💚 Confirmed | Merlin:
  • `388.4_0`
  • | find it | +| [RT-AX89X](https://asusrouter.vaskivskyi.com/devices/RT-AX89X.md) | 💚 Confirmed | | find it | +| [RT-AX92U](https://asusrouter.vaskivskyi.com/devices/RT-AX92U.md) | 💚 Confirmed | Stock:
  • `386.46061`
  • | find it | +| [TUF-AX3000 V2](https://asusrouter.vaskivskyi.com/devices/TUF-AX3000V2.md) | 💚 Confirmed | Stock:
  • `388.23785`
  • | find it | +| [TUF-AX4200](https://asusrouter.vaskivskyi.com/devices/TUF-AX4200.md) | 💛 Expected to work | | find it | +| [TUF-AX5400](https://asusrouter.vaskivskyi.com/devices/TUF-AX5400.md) | 💚 Confirmed | Stock:
  • `386.50224`
  • `388.21224`
  • `388.22525`
  • `388.23285`
  • `388.24121`
  • Merlin:
  • `388.4_0`
  • | find it | +| [TUF-AX6000](https://asusrouter.vaskivskyi.com/devices/TUF-AX6000.md) | 💚 Confirmed | Stock:
  • `388.32432`
  • | find it | +| [ZenWiFi AX (XT8)]() | 💚 Confirmed | Stock:
  • `386.48706`
  • `388.23285`
  • Merlin:
  • `386.7_2-gnuton1`
  • | find it | +| [ZenWiFi AX Hybrid (XP4)]() | 💛 Expected to work | | find it | +| [ZenWiFi AX Mini (XD4)]() | 💚 Confirmed | Stock:
  • `386.48790`
  • `386.49599`
  • | find it | +| [ZenWiFi Pro XT12](https://asusrouter.vaskivskyi.com/devices/ZenWiFiProXT12.md) | 💚 Confirmed | Stock:
  • `388.22127`
  • | find it | +| [ZenWiFi XD4 Plus](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD4Plus.md) | 💛 Expected to work | | find it | +| [ZenWiFi XD4S](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD4S.md) | 💛 Expected to work | | find it | +| [ZenWiFi XD5](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD5.md) | 💚 Confirmed | Stock:
  • `388.23949`
  • | find it | +| [ZenWiFi XD6](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD6.md) | 💚 Confirmed | Stock:
  • `388.21380`
  • | find it | +| [ZenWiFi XD6S](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXD6S.md) | 💚 Confirmed | Stock:
  • `388.21380`
  • | find it | +| [ZenWiFi XT9](https://asusrouter.vaskivskyi.com/devices/ZenWiFiXT9.md) | 💚 Confirmed | Stock:
  • `388_23285`
  • | find it | ### WiFi 5 | 802.11ac -|Model|Status|Tested firmware|Find it on Amazon[^amazon]| -|---|---|---|---| -|[4G-AC55U](https://asusrouter.vaskivskyi.com/devices/4G-AC55U.html)|💚 Confirmed|Stock:
  • `380.8102`
  • |find it| -|[DSL-AC68U](https://asusrouter.vaskivskyi.com/devices/DSL-AC68U.html)|💚 Confirmed|Stock:
  • `386.47534`
  • `386.50117`
  • Merlin:
  • `386.4-gnuton2`
  • `386.7_2-gnuton1`
  • |find it| -|[RT-AC51U](https://asusrouter.vaskivskyi.com/devices/RT-AC51U.html)|💚 Confirmed|Stock:
  • `380.8591`
  • |find it| -|[RT-AC52U B1](https://asusrouter.vaskivskyi.com/devices/RT-AC52UB1.html)|💚 Confirmed||find it| -|[RT-AC5300](https://asusrouter.vaskivskyi.com/devices/RT-AC5300.html)|💚 Confirmed|Merlin:
  • `386.7_2`
  • |find it| -|[RT-AC57U V3](https://asusrouter.vaskivskyi.com/devices/RT-AC57UV3.html)|💚 Confirmed|Stock:
  • `386.21649`
  • |find it| -|[RT-AC58U](https://asusrouter.vaskivskyi.com/devices/RT-AC58U.html)|💚 Confirmed||find it| -|[RT-AC66U](https://asusrouter.vaskivskyi.com/devices/RT-AC66U.html)|💚 Confirmed|Merlin:
  • `380.70_0`
  • |find it| -|[RT-AC66U B1](https://asusrouter.vaskivskyi.com/devices/RT-AC66UB1.html)|💚 Confirmed|Stock:
  • `386.51255`
  • |find it| -|[RT-AC68U](https://asusrouter.vaskivskyi.com/devices/RT-AC68U.html)|💚 Confirmed|Stock:
  • `386.49703`
  • Merlin:
  • `386.5_2`
  • `386.7_0`
  • |find it| -|[RT-AC85P](https://asusrouter.vaskivskyi.com/devices/RT-AC85P.html)|💚 Confirmed|Stock:
  • `382.52516`
  • |find it| -|[RT-AC86U](https://asusrouter.vaskivskyi.com/devices/RT-AC86U.html)|💚 Confirmed|Stock:
  • `386.48260`
  • `386.49709`
  • Merlin:
  • `386.7_0`
  • `386.7_2`
  • `386.9_0`
  • |find it| -|[RT-AC87U](https://asusrouter.vaskivskyi.com/devices/RT-AC87U.html)|💚 Confirmed|Merlin:
  • `384.13_10`
  • |find it| -|[RT-AC88U](https://asusrouter.vaskivskyi.com/devices/RT-AC88U.html)|💚 Confirmed|Stock:
  • `386.48260`
  • Merlin:
  • `386.5_0`
  • `386.7_beta1`
  • `386.12_2`
  • |find it| -|[RT-ACRH17](https://asusrouter.vaskivskyi.com/devices/RT-ACRH17.html)|💚 Confirmed|Stock:
  • `382.52517`
  • |find it| -|[ZenWiFi AC Mini(CD6)](https://asusrouter.vaskivskyi.com/devices/ZenWiFiACMini(CD6).html)|💛 Expected to work||find it| + +| Model | Status | Tested firmware | Find it on Amazon[^amazon] | +| ----------------------------------------------------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| [4G-AC55U](https://asusrouter.vaskivskyi.com/devices/4G-AC55U.md) | 💚 Confirmed | Stock:
  • `380.8102`
  • | find it | +| [BRT-AC828](https://asusrouter.vaskivskyi.com/devices/BRT-AC828.md) | 💚 Confirmed | Stock:
  • `382_70348`
  • | find it | +| [DSL-AC68U](https://asusrouter.vaskivskyi.com/devices/DSL-AC68U.md) | 💚 Confirmed | Stock:
  • `386.47534`
  • `386.50117`
  • Merlin:
  • `386.4-gnuton2`
  • `386.7_2-gnuton1`
  • | find it | +| [RT-AC51U](https://asusrouter.vaskivskyi.com/devices/RT-AC51U.md) | 💚 Confirmed | Stock:
  • `380.8591`
  • | find it | +| [RT-AC52U B1](https://asusrouter.vaskivskyi.com/devices/RT-AC52UB1.md) | 💚 Confirmed | | find it | +| [RT-AC5300](https://asusrouter.vaskivskyi.com/devices/RT-AC5300.md) | 💚 Confirmed | Merlin:
  • `386.7_2`
  • | find it | +| [RT-AC57U V3](https://asusrouter.vaskivskyi.com/devices/RT-AC57UV3.md) | 💚 Confirmed | Stock:
  • `386.21649`
  • | find it | +| [RT-AC58U](https://asusrouter.vaskivskyi.com/devices/RT-AC58U.md) | 💚 Confirmed | | find it | +| [RT-AC66U](https://asusrouter.vaskivskyi.com/devices/RT-AC66U.md) | 💚 Confirmed | Merlin:
  • `380.70_0`
  • | find it | +| [RT-AC66U B1](https://asusrouter.vaskivskyi.com/devices/RT-AC66UB1.md) | 💚 Confirmed | Stock:
  • `386.51255`
  • | find it | +| [RT-AC68U](https://asusrouter.vaskivskyi.com/devices/RT-AC68U.md) | 💚 Confirmed | Stock:
  • `386.49703`
  • Merlin:
  • `386.5_2`
  • `386.7_0`
  • | find it | +| [RT-AC85P](https://asusrouter.vaskivskyi.com/devices/RT-AC85P.md) | 💚 Confirmed | Stock:
  • `382.52516`
  • | find it | +| [RT-AC86U](https://asusrouter.vaskivskyi.com/devices/RT-AC86U.md) | 💚 Confirmed | Stock:
  • `386.48260`
  • `386.49709`
  • Merlin:
  • `386.7_0`
  • `386.7_2`
  • `386.9_0`
  • | find it | +| [RT-AC87U](https://asusrouter.vaskivskyi.com/devices/RT-AC87U.md) | 💚 Confirmed | Merlin:
  • `384.13_10`
  • | find it | +| [RT-AC88U](https://asusrouter.vaskivskyi.com/devices/RT-AC88U.md) | 💚 Confirmed | Stock:
  • `386.48260`
  • Merlin:
  • `386.5_0`
  • `386.7_beta1`
  • `386.12_2`
  • | find it | +| [RT-ACRH17](https://asusrouter.vaskivskyi.com/devices/RT-ACRH17.md) | 💚 Confirmed | Stock:
  • `382.52517`
  • | find it | +| [ZenWiFi AC Mini(CD6)]() | 💛 Expected to work | | find it | ### WiFi 4 | 802.11n -|Model|Status|Tested firmware|Find it on Amazon[^amazon]| -|---|---|---|---| -|[RT-N66U](https://asusrouter.vaskivskyi.com/devices/RT-N66U.html)|💚 Confirmed||find it| + +| Model | Status | Tested firmware | Find it on Amazon[^amazon] | +| --------------------------------------------------------------- | ------------ | --------------- | -------------------------------------------------------------------------------------- | +| [RT-N66U](https://asusrouter.vaskivskyi.com/devices/RT-N66U.md) | 💚 Confirmed | | find it | ## New features development @@ -179,12 +196,6 @@ Here is the list of features being in process of development or considered for t GroupFeatureStatus -Aura RGB
      -
    1. Full support (#82)
    2. -
    -on hold
    (a device with Aura RGB support is required for development and testing) - - Connected device
    1. Per-device traffic monitoring (#220)
    2. Possibility to use DHCP `hostname` value for device tracking (#119)
    3. @@ -194,7 +205,6 @@ Here is the list of features being in process of development or considered for t - ## Support the integration ### Issues and Pull requests @@ -213,6 +223,6 @@ Moreover, you can support the integration by using the Amazon links provided in ## Thanks to -The initial codebase for this integration is highly based on Home Assistant core integration [AsusWRT](https://www.home-assistant.io/integrations/asuswrt/) and [ollo69/ha_asuswrt_custom](https://github.com/ollo69/ha_asuswrt_custom). +The initial codebase (from April 2022) for this integration is highly based on Home Assistant core integration [AsusWRT](https://www.home-assistant.io/integrations/asuswrt/) and [ollo69/ha_asuswrt_custom](https://github.com/ollo69/ha_asuswrt_custom). [^amazon]: As an Amazon Associate I earn from qualifying purchases. Not like I ever got anything yet (: diff --git a/custom_components/asusrouter/bridge.py b/custom_components/asusrouter/bridge.py index 0b95522..4eda00c 100644 --- a/custom_components/asusrouter/bridge.py +++ b/custom_components/asusrouter/bridge.py @@ -7,6 +7,18 @@ from typing import Any, Callable, Optional import aiohttp +from homeassistant.const import ( + CONF_HOST, + CONF_PASSWORD, + CONF_PORT, + CONF_SSL, + CONF_USERNAME, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.aiohttp_client import async_create_clientsession +from homeassistant.helpers.update_coordinator import UpdateFailed + from asusrouter import AsusRouter from asusrouter.error import AsusRouterError from asusrouter.modules.aimesh import AiMeshDevice @@ -20,20 +32,10 @@ ) from asusrouter.modules.identity import AsusDevice from asusrouter.modules.parental_control import ParentalControlRule, PCRuleType -from homeassistant.const import ( - CONF_HOST, - CONF_PASSWORD, - CONF_PORT, - CONF_SSL, - CONF_USERNAME, -) -from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.aiohttp_client import async_create_clientsession -from homeassistant.helpers.update_coordinator import UpdateFailed from . import helpers from .const import ( + AURA, BOOTTIME, CONF_CACHE_TIME, CONF_DEFAULT_CACHE_TIME, @@ -66,6 +68,8 @@ TEMPERATURE, WLAN, ) +from .modules.aura import aura_to_ha +from .modules.firmware import to_ha as firmware_to_ha _LOGGER = logging.getLogger(__name__) @@ -100,7 +104,9 @@ def __init__( self._active: bool = False @staticmethod - def _get_api(configs: dict[str, Any], session: aiohttp.ClientSession) -> AsusRouter: + def _get_api( + configs: dict[str, Any], session: aiohttp.ClientSession + ) -> AsusRouter: """Get AsusRouter API.""" return AsusRouter( @@ -169,14 +175,18 @@ async def async_clean(self) -> None: # <-- Connection # -------------------- - async def async_cleanup_sensors(self, sensors: dict[str, Any]) -> dict[str, Any]: + async def async_cleanup_sensors( + self, sensors: dict[str, Any] + ) -> dict[str, Any]: """Cleanup sensors depending on the device mode.""" mode = self._configs.get(CONF_MODE, CONF_DEFAULT_MODE) available = MODE_SENSORS[mode] _LOGGER.debug("Available sensors for mode=`%s`: %s", mode, available) sensors = { - group: details for group, details in sensors.items() if group in available + group: details + for group, details in sensors.items() + if group in available } return sensors @@ -185,7 +195,14 @@ async def async_get_available_sensors(self) -> dict[str, dict[str, Any]]: """Get available sensors.""" sensors = { - BOOTTIME: {SENSORS: SENSORS_BOOTTIME, METHOD: self._get_data_boottime}, + AURA: { + SENSORS: await self._get_sensors_modern(AsusData.AURA), + METHOD: self._get_data_aura, + }, + BOOTTIME: { + SENSORS: SENSORS_BOOTTIME, + METHOD: self._get_data_boottime, + }, CPU: { SENSORS: await self._get_sensors_modern(AsusData.CPU), METHOD: self._get_data_cpu, @@ -207,7 +224,9 @@ async def async_get_available_sensors(self) -> dict[str, dict[str, Any]]: METHOD: self._get_data_network, }, "ovpn_client": { - SENSORS: await self._get_sensors_modern(AsusData.OPENVPN_CLIENT), + SENSORS: await self._get_sensors_modern( + AsusData.OPENVPN_CLIENT + ), METHOD: self._get_data_ovpn_client, }, "ovpn_server": { @@ -240,11 +259,15 @@ async def async_get_available_sensors(self) -> dict[str, dict[str, Any]]: METHOD: self._get_data_wan, }, "wireguard_client": { - SENSORS: await self._get_sensors_modern(AsusData.WIREGUARD_CLIENT), + SENSORS: await self._get_sensors_modern( + AsusData.WIREGUARD_CLIENT + ), METHOD: self._get_data_wireguard_client, }, "wireguard_server": { - SENSORS: await self._get_sensors_modern(AsusData.WIREGUARD_SERVER), + SENSORS: await self._get_sensors_modern( + AsusData.WIREGUARD_SERVER + ), METHOD: self._get_data_wireguard_server, }, WLAN: { @@ -304,6 +327,13 @@ async def async_get_clients(self) -> dict[str, AsusClient]: return await self._get_data(AsusData.CLIENTS, force=True) # Sensor-specific methods + async def _get_data_aura(self) -> dict[str, Any]: + """Get Aura data from the device.""" + + data = await self._get_data_modern(AsusData.AURA) + + return aura_to_ha(data) + async def _get_data_boottime(self) -> dict[str, Any]: """Get `boottime` data from the device.""" @@ -317,7 +347,9 @@ async def _get_data_cpu(self) -> dict[str, Any]: async def _get_data_firmware(self) -> dict[str, Any]: """Get firmware data from the device.""" - return await self._get_data(AsusData.FIRMWARE) + data = await self._get_data_modern(AsusData.FIRMWARE) + + return firmware_to_ha(data) async def _get_data_gwlan(self) -> dict[str, Any]: """Get GWLAN data from the device.""" @@ -492,7 +524,9 @@ async def _get_sensors( "Raw `%s` sensors of type (%s): %s", datatype, type(data), data ) sensors = ( - process(data) if process is not None else self._process_sensors(data) + process(data) + if process is not None + else self._process_sensors(data) ) _LOGGER.debug("Available `%s` sensors: %s", sensor_type, sensors) except AsusRouterError as ex: @@ -516,7 +550,9 @@ async def _get_sensors_modern(self, datatype: AsusData) -> list[str]: "Raw `%s` sensors of type (%s): %s", datatype, type(data), data ) sensors = convert_to_ha_sensors_list(data) - _LOGGER.debug("Available `%s` sensors: %s", datatype.value, sensors) + _LOGGER.debug( + "Available `%s` sensors: %s", datatype.value, sensors + ) except AsusRouterError as ex: if datatype.value in DEFAULT_SENSORS: sensors = DEFAULT_SENSORS[datatype.value] @@ -637,7 +673,9 @@ async def async_pc_rule(self, **kwargs: Any) -> bool: reg_value = entity_reg.async_get(entity) if not isinstance(reg_value, er.RegistryEntry): continue - capabilities: dict[str, Any] = helpers.as_dict(reg_value.capabilities) + capabilities: dict[str, Any] = helpers.as_dict( + reg_value.capabilities + ) devices.append(capabilities) # Convert devices to rules diff --git a/custom_components/asusrouter/client.py b/custom_components/asusrouter/client.py index 3342b53..a00d146 100644 --- a/custom_components/asusrouter/client.py +++ b/custom_components/asusrouter/client.py @@ -5,19 +5,17 @@ from datetime import datetime, timezone from typing import Any, Callable, Optional +from homeassistant.core import callback +from homeassistant.helpers.device_registry import format_mac + from asusrouter.modules.client import ( AsusClient, AsusClientConnection, AsusClientConnectionWlan, AsusClientDescription, ) -from asusrouter.modules.connection import ConnectionState -from asusrouter.modules.homeassistant import ( - convert_to_ha_state_bool, - convert_to_ha_string, -) -from homeassistant.core import callback -from homeassistant.helpers.device_registry import format_mac +from asusrouter.modules.connection import ConnectionState, ConnectionType +from asusrouter.modules.homeassistant import convert_to_ha_state_bool from .helpers import clean_dict @@ -49,6 +47,9 @@ def __init__( # Connection state self._state: ConnectionState = ConnectionState.UNKNOWN + self._connection_type: ConnectionType = ConnectionType.DISCONNECTED + self._guest: bool = False + self._guest_id: int = 0 # Device last active self._last_activity: Optional[datetime] = None @@ -58,7 +59,9 @@ def update( self, client_info: Optional[AsusClient] = None, consider_home: int = 0, - event_call: Optional[Callable[[str, Optional[dict[str, Any]]], None]] = None, + event_call: Optional[ + Callable[[str, Optional[dict[str, Any]]], None] + ] = None, ): """Update client information.""" @@ -77,7 +80,7 @@ def update( # Connected state state = client_info.state - self._identity = self.generate_identity() + self._identity = self.generate_identity(state) self._extra_state_attributes = self.generate_extra_state_attributes() # If is connected @@ -111,7 +114,9 @@ def update( self.identity, ) - def generate_identity(self) -> dict[str, Any]: + def generate_identity( + self, state: Optional[ConnectionState] + ) -> dict[str, Any]: """Generate client identity.""" identity: dict[str, Any] = { @@ -121,14 +126,22 @@ def generate_identity(self) -> dict[str, Any]: } if isinstance(self.connection, AsusClientConnection): - identity["connection_type"] = convert_to_ha_string(self.connection.type) + # Rewrite guest from last known state if needed + if state == ConnectionState.DISCONNECTED: + identity["guest"] = self._guest + identity["guest_id"] = self._guest_id + if self.connection.type != ConnectionType.DISCONNECTED: + self._connection_type = self.connection.type + identity["connection_type"] = self._connection_type identity["node"] = ( - format_mac(self.connection.node) if self.connection.node else None + format_mac(self.connection.node) + if self.connection.node + else None ) if isinstance(self.connection, AsusClientConnectionWlan): - identity["guest"] = self.connection.guest - identity["guest_id"] = self.connection.guest_id + identity["guest"] = self._guest = self.connection.guest + identity["guest_id"] = self._guest_id = self.connection.guest_id identity["connected"] = self.connection.since return clean_dict(identity) @@ -136,7 +149,9 @@ def generate_identity(self) -> dict[str, Any]: def generate_extra_state_attributes(self) -> dict[str, Any]: """Generate extra state attributes.""" - attributes: dict[str, Any] = self._identity.copy() if self._identity else {} + attributes: dict[str, Any] = ( + self._identity.copy() if self._identity else {} + ) attributes["last_activity"] = self._last_activity @@ -165,7 +180,9 @@ def state(self) -> Optional[bool]: def ip_address(self) -> Optional[str]: """Return IP address.""" - return self.connection.ip_address if self.connection is not None else None + return ( + self.connection.ip_address if self.connection is not None else None + ) @property def mac_address(self) -> str: @@ -177,7 +194,11 @@ def mac_address(self) -> str: def name(self) -> Optional[str]: """Return name.""" - return self.description.name if self.description is not None else self._name + return ( + self.description.name + if self.description is not None + else self._name + ) @property def extra_state_attributes(self) -> dict[str, Any]: diff --git a/custom_components/asusrouter/const.py b/custom_components/asusrouter/const.py index a4f4da9..402cf19 100644 --- a/custom_components/asusrouter/const.py +++ b/custom_components/asusrouter/const.py @@ -4,13 +4,6 @@ from typing import Any, Callable -from asusrouter.modules.openvpn import AsusOVPNClient, AsusOVPNServer -from asusrouter.modules.parental_control import AsusBlockAll, AsusParentalControl -from asusrouter.modules.port_forwarding import AsusPortForwarding -from asusrouter.modules.system import AsusSystem -from asusrouter.modules.wireguard import AsusWireGuardClient, AsusWireGuardServer -from asusrouter.modules.wlan import AsusWLAN, Wlan -from asusrouter.tools import converters from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.button import ButtonDeviceClass from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass @@ -32,6 +25,20 @@ UnitOfTemperature, ) +from asusrouter.modules.openvpn import AsusOVPNClient, AsusOVPNServer +from asusrouter.modules.parental_control import ( + AsusBlockAll, + AsusParentalControl, +) +from asusrouter.modules.port_forwarding import AsusPortForwarding +from asusrouter.modules.system import AsusSystem +from asusrouter.modules.wireguard import ( + AsusWireGuardClient, + AsusWireGuardServer, +) +from asusrouter.modules.wlan import AsusWLAN, Wlan +from asusrouter.tools import converters + from .dataclass import ( ARBinarySensorDescription, ARButtonDescription, @@ -81,6 +88,7 @@ API_ID = "api_id" API_TYPE = "api_type" APPLY = "apply" +AURA = "aura" BITS_PER_SECOND = "bits/s" BOOTTIME = "boottime" BRIDGE = "bridge" @@ -255,7 +263,7 @@ # Access Point MODE_ACCESS_POINT = MODE_NODE.copy() -MODE_ACCESS_POINT.extend([WLAN]) +MODE_ACCESS_POINT.extend([WLAN, AURA]) # Media Bridge MODE_MEDIA_BRIDGE = MODE_ACCESS_POINT.copy() @@ -290,9 +298,15 @@ SENSORS_AIMESH = [NUMBER, LIST] SENSORS_BOOTTIME = ["datetime"] SENSORS_CHANGE = ["change"] -SENSORS_CONNECTED_DEVICES = [NUMBER, DEVICES, "latest", "latest_time"] +SENSORS_CONNECTED_DEVICES = [ + NUMBER, + DEVICES, + "latest", + "latest_time", + "gn_number", +] SENSORS_CPU = [TOTAL, USED, USAGE] -SENSORS_FIRMWARE = [STATE] +SENSORS_FIRMWARE = [STATE, "state_beta"] SENSORS_GWLAN = { "sync_node": "aimesh_sync", "auth_mode_x": "auth_method", @@ -506,7 +520,7 @@ } CONF_DEFAULT_HIDE_PASSWORDS = False CONF_DEFAULT_INTERFACES = [WAN.upper()] -CONF_DEFAULT_INTERVALS = {CONF_INTERVAL + FIRMWARE: 21600} +CONF_DEFAULT_INTERVALS = {CONF_INTERVAL + FIRMWARE: 600} CONF_DEFAULT_LATEST_CONNECTED = 5 CONF_DEFAULT_MODE = ROUTER CONF_DEFAULT_PORT = 0 @@ -1029,6 +1043,22 @@ entity_registry_enabled_default=True, ), ] +STATIC_AURA: list[AREntityDescription] = [ + ARLightDescription( + key="state", + key_group="aura", + name="AURA", + icon_on=ICON_LIGHT_ON, + icon_off=ICON_LIGHT_OFF, + entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=True, + extra_state_attributes={ + "brightness": "brightness", + "rgb_color": "rgb_color", + "zones": "zones", + }, + ), +] STATIC_SENSORS: list[AREntityDescription] = [ # AiMesh ARSensorDescription( @@ -1066,6 +1096,17 @@ "devices": "devices", }, ), + # Connected GuestNetwork devices + ARSensorDescription( + key="gn_number", + key_group="devices", + name="Connected GuestNetwork Devices", + icon=ICON_ROUTER, + state_class=SensorStateClass.MEASUREMENT, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=True, + extra_state_attributes={}, + ), # CPU ARSensorDescription( key="total_usage", @@ -1076,7 +1117,9 @@ native_unit_of_measurement=PERCENTAGE, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, - extra_state_attributes={f"{num}_usage": f"core_{num}" for num in NUMERIC_CORES}, + extra_state_attributes={ + f"{num}_usage": f"core_{num}" for num in NUMERIC_CORES + }, ), # Latest connected ARSensorDescription( @@ -1225,7 +1268,8 @@ entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, extra_state_attributes={ - f"{num}_{key}": value for key, value in SENSORS_OVPN_CLIENT.items() + f"{num}_{key}": value + for key, value in SENSORS_OVPN_CLIENT.items() }, ) for num in range(1, 6) @@ -1247,7 +1291,8 @@ entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, extra_state_attributes={ - f"{num}_{key}": value for key, value in SENSORS_OVPN_SERVER.items() + f"{num}_{key}": value + for key, value in SENSORS_OVPN_SERVER.items() }, ) for num in NUMERIC_OVPN_SERVER @@ -1269,7 +1314,8 @@ entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, extra_state_attributes={ - f"{num}_{key}": value for key, value in SENSORS_WIREGUARD_CLIENT.items() + f"{num}_{key}": value + for key, value in SENSORS_WIREGUARD_CLIENT.items() }, ) for num in range(1, 6) @@ -1291,7 +1337,8 @@ entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, extra_state_attributes={ - f"{num}_{key}": value for key, value in SENSORS_WIREGUARD_SERVER.items() + f"{num}_{key}": value + for key, value in SENSORS_WIREGUARD_SERVER.items() }, ) for num in range(1, 2) @@ -1359,7 +1406,8 @@ entity_category=EntityCategory.CONFIG, entity_registry_enabled_default=False, extra_state_attributes={ - f"{wlan}_{gwlan}_{key}": value for key, value in SENSORS_GWLAN.items() + f"{wlan}_{gwlan}_{key}": value + for key, value in SENSORS_GWLAN.items() }, ) for gwlan in NUMERIC_GWLAN @@ -1392,12 +1440,26 @@ name="Firmware update", icon=ICON_UPDATE, device_class=UpdateDeviceClass.FIRMWARE, + entity_registry_enabled_default=True, + extra_state_attributes={ + "current": "current", + "latest": "latest", + "release_note": "release_note", + }, + ), + ARUpdateDescription( + key="state_beta", + key_group="firmware", + name="Firmware update (Beta)", + icon=ICON_UPDATE, + device_class=UpdateDeviceClass.FIRMWARE, + entity_registry_enabled_default=False, extra_state_attributes={ "current": "current", - "available": "available", + "latest_beta": "latest", "release_note": "release_note", }, - ) + ), ] # <-- SENSORS diff --git a/custom_components/asusrouter/device_tracker.py b/custom_components/asusrouter/device_tracker.py index b9c257f..45e375f 100644 --- a/custom_components/asusrouter/device_tracker.py +++ b/custom_components/asusrouter/device_tracker.py @@ -113,7 +113,7 @@ def _compile_device_info(self, mac_address: str, name: Optional[str]) -> DeviceI identifiers={ (DOMAIN, mac_address), }, - name=name, + default_name=name, via_device=(DOMAIN, self._router.mac), ) diff --git a/custom_components/asusrouter/light.py b/custom_components/asusrouter/light.py index 99a0e62..9372a51 100644 --- a/custom_components/asusrouter/light.py +++ b/custom_components/asusrouter/light.py @@ -2,20 +2,37 @@ from __future__ import annotations +import logging from typing import Any -from asusrouter.modules.led import AsusLED -from homeassistant.components.light import ColorMode, LightEntity +from homeassistant.components.light import ( + ColorMode, + LightEntity, + LightEntityFeature, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from .const import ASUSROUTER, DOMAIN, STATIC_LIGHTS +from asusrouter.modules.aura import AsusAura +from asusrouter.modules.color import ColorRGB, scale_value_int +from asusrouter.modules.led import AsusLED + +from .const import ASUSROUTER, DOMAIN, STATIC_AURA, STATIC_LIGHTS from .dataclass import ARLightDescription from .entity import ARBinaryEntity, async_setup_ar_entry +from .modules.aura import AURA_EFFECTS, AURA_NO_EFFECT, per_zone_light from .router import ARDevice +_LOGGER = logging.getLogger(__name__) + +EFFECT = "effect" +RGB_COLOR = "rgb_color" +BRIGHTNESS = "brightness" +COLOR_MODE = "color_mode" +ZONE_ID = "zone_id" + async def async_setup_entry( hass: HomeAssistant, @@ -24,14 +41,36 @@ async def async_setup_entry( ) -> None: """Set up AsusRouter lights.""" - lights = STATIC_LIGHTS.copy() - - if not hass.data[DOMAIN][config_entry.entry_id][ASUSROUTER].bridge.identity.led: - return - - await async_setup_ar_entry( - hass, config_entry, async_add_entities, lights, ARLightLED - ) + leds = STATIC_LIGHTS.copy() + if ( + hass.data[DOMAIN][config_entry.entry_id][ + ASUSROUTER + ].bridge.identity.led + is True + ): + await async_setup_ar_entry( + hass, config_entry, async_add_entities, leds, ARLightLED + ) + + auras = STATIC_AURA.copy() + if ( + hass.data[DOMAIN][config_entry.entry_id][ + ASUSROUTER + ].bridge.identity.aura + is True + ): + # Create per-zone lights + auras.extend( + per_zone_light( + hass.data[DOMAIN][config_entry.entry_id][ + ASUSROUTER + ].bridge.identity.aura_zone + ) + ) + + await async_setup_ar_entry( + hass, config_entry, async_add_entities, auras, ARLightAura + ) class ARLightLED(ARBinaryEntity, LightEntity): @@ -66,3 +105,87 @@ async def async_turn_off( """Turn off LED.""" await self._set_state(AsusLED.OFF) + + +class ARLightAura(ARBinaryEntity, LightEntity): + """AsusRouter Aura light.""" + + def __init__( + self, + coordinator: DataUpdateCoordinator, + router: ARDevice, + description: ARLightDescription, + ) -> None: + """Initialize AsusRouter Aura light.""" + + self._attr_supported_features = LightEntityFeature.EFFECT + self._attr_effect_list = AURA_EFFECTS + + super().__init__(coordinator, router, description) + self.entity_description: ARLightDescription = description + + async def async_turn_on( + self, + **kwargs: Any, + ) -> None: + """Turn on Aura.""" + + effect = AsusAura.ON + if "effect" in kwargs: + effect = AsusAura.__members__.get( + kwargs["effect"].upper(), AsusAura.ON + ) + + color = None + if "rgb_color" in kwargs: + rgb = kwargs["rgb_color"] + color = ColorRGB(rgb[0], rgb[1], rgb[2], scale=255) + + brightness = None + if "brightness" in kwargs: + brightness = scale_value_int(kwargs["brightness"], 128, 255) + + zone_id = ( + self.entity_description.capabilities.get("zone_id", None) + if self.entity_description.capabilities + else None + ) + + await self._set_state( + effect, + color=color, + brightness=brightness, + zone=zone_id, + ) + + async def async_turn_off( + self, + **kwargs: Any, + ) -> None: + """Turn off Aura.""" + + await self._set_state(AsusAura.OFF) + + @property + def effect(self) -> str | None: + """Return the current effect.""" + + return self.coordinator.data.get("effect", AURA_NO_EFFECT) + + @property + def supported_color_modes(self) -> set[str] | None: + """Return the supported color modes.""" + + # This is a workaround to avoid a bug in HA + # which does not hide the color picker and brightness slider + # even when the current color mode is set to ONOFF only + _supported_color_modes = self.coordinator.data.get( + "color_mode", ColorMode.RGB + ) + return {_supported_color_modes} + + @property + def color_mode(self) -> ColorMode | str | None: + """Return the color mode.""" + + return self.coordinator.data.get("color_mode", ColorMode.RGB) diff --git a/custom_components/asusrouter/manifest.json b/custom_components/asusrouter/manifest.json index e5e98df..783f9d4 100644 --- a/custom_components/asusrouter/manifest.json +++ b/custom_components/asusrouter/manifest.json @@ -8,6 +8,6 @@ "iot_class": "local_polling", "issue_tracker": "https://github.com/Vaskivskyi/ha-asusrouter/issues", "loggers": ["asusrouter"], - "requirements": ["asusrouter==1.11.0"], - "version": "0.32.1" + "requirements": ["asusrouter==1.12.0"], + "version": "0.33.0" } diff --git a/custom_components/asusrouter/modules/__init__.py b/custom_components/asusrouter/modules/__init__.py new file mode 100644 index 0000000..9dc1640 --- /dev/null +++ b/custom_components/asusrouter/modules/__init__.py @@ -0,0 +1 @@ +"""Complex modules for AsusRouter integration.""" diff --git a/custom_components/asusrouter/modules/aura.py b/custom_components/asusrouter/modules/aura.py new file mode 100644 index 0000000..398686f --- /dev/null +++ b/custom_components/asusrouter/modules/aura.py @@ -0,0 +1,132 @@ +"""Aura RGB module for AsusRouter integration. + +This module allows converting AsusRouter Aura data to the +Home Assistant format. This is required due to the complex +data structure and effect handling by Home Assistant. +""" + +from __future__ import annotations + +from typing import Any + +from homeassistant.components.light import ColorMode +from homeassistant.const import EntityCategory + +from asusrouter.modules.color import ColorRGBB +from asusrouter.tools.converters import scale_value_int + +from ..const import ICON_LIGHT_OFF, ICON_LIGHT_ON +from ..dataclass import AREntityDescription, ARLightDescription + +AURA_NO_EFFECT = "EFFECT_OFF" +AURA_FALLBACK_BRIGHTNESS = 128 +AURA_FALLBACK_COLOR = ColorRGBB( + (128, 128, 128), + AURA_FALLBACK_BRIGHTNESS, + scale=128, +) + +AURA_EFFECTS = [ + "Gradient", + "Static", + "Breathing", + "Evolution", + "Rainbow", + "Wave", + "Marquee", +] + +AURA_EFFECTS_MAP = { + 0: AURA_NO_EFFECT, + 1: "Gradient", + 2: "Static", + 3: "Breathing", + 4: "Evolution", + 5: "Rainbow", + 6: "Wave", + 7: "Marquee", +} + +SUPPORTED_COLOR_MODES = { + 0: ColorMode.ONOFF, + 1: ColorMode.RGB, + 2: ColorMode.RGB, + 3: ColorMode.RGB, + 4: ColorMode.ONOFF, + 5: ColorMode.ONOFF, + 6: ColorMode.ONOFF, + 7: ColorMode.RGB, +} + +ACTIVE_BRIGHTNESS = "active_brightness" +ACTIVE_COLOR = "active_color" +BRIGHTNESS = "brightness" +RGB_COLOR = "rgb_color" +SCHEME = "scheme" +STATE = "state" +ZONES = "zones" + + +def aura_to_ha(data: dict[str, Any]) -> dict[str, Any]: + """Convert AsusRouter Aura data to Home Assistant format.""" + + result = { + STATE: data.get(STATE, False), + ZONES: data.get(ZONES, 0), + } + + # Current active effect + _effect = data.get(SCHEME, 0) + result["effect"] = AURA_EFFECTS_MAP.get(_effect, AURA_NO_EFFECT) + + # Colors + if ACTIVE_COLOR in data: + result[RGB_COLOR] = ColorRGBB(data[ACTIVE_COLOR], scale=255).as_tuple() + if ACTIVE_BRIGHTNESS in data: + result[BRIGHTNESS] = scale_value_int( + data.get(ACTIVE_BRIGHTNESS, AURA_FALLBACK_BRIGHTNESS), + 255, + 128, + ) + + for i in range(result["zones"]): + color_key = f"active_{i}_color" + brightness_key = f"active_{i}_brightness" + if color_key in data: + result[f"{RGB_COLOR}_{i}"] = ColorRGBB( + data.get(color_key, AURA_FALLBACK_COLOR), + scale=255, + ).as_tuple() + if brightness_key in data: + result[f"{BRIGHTNESS}_{i}"] = scale_value_int( + data.get(brightness_key, AURA_FALLBACK_BRIGHTNESS), + 255, + 128, + ) + + # Color mode support + result["color_mode"] = SUPPORTED_COLOR_MODES.get(_effect, ColorMode.ONOFF) + + return result + + +def per_zone_light(zones: int = 3) -> list[AREntityDescription]: + """Create a per-zone light entity descriptions.""" + + return [ + ARLightDescription( + key=STATE, + key_group="aura", + name=f"AURA Zone {i+1}", + icon_on=ICON_LIGHT_ON, + icon_off=ICON_LIGHT_OFF, + capabilities={"zone_id": i}, + entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=False, + extra_state_attributes={ + f"{BRIGHTNESS}_{i}": BRIGHTNESS, + f"{RGB_COLOR}_{i}": RGB_COLOR, + }, + ) + for i in range(zones) + ] diff --git a/custom_components/asusrouter/modules/firmware.py b/custom_components/asusrouter/modules/firmware.py new file mode 100644 index 0000000..bf54484 --- /dev/null +++ b/custom_components/asusrouter/modules/firmware.py @@ -0,0 +1,31 @@ +"""Firmware module for AsusRouter integration.""" + +from __future__ import annotations + +from typing import Any, Optional + + +def to_ha(data: Optional[dict[str, Any]]) -> dict[str, Any]: + """Convert AsusRouter firmware data to Home Assistant format.""" + + if not data: + return {} + + # Current firmware + _current = data.get("current") + _current = str(_current) if _current else None + + # Available stable firmware + _latest = data.get("available") + _latest = str(_latest) if _latest else _current + + # Available beta firmware + _latest_beta = data.get("available_beta") + _latest_beta = str(_latest_beta) if _latest_beta else _current + + return { + "current": _current, + "latest": _latest, + "latest_beta": _latest_beta, + "release_note": data.get("release_note"), + } diff --git a/custom_components/asusrouter/router.py b/custom_components/asusrouter/router.py index 7bb6af5..9947549 100644 --- a/custom_components/asusrouter/router.py +++ b/custom_components/asusrouter/router.py @@ -8,6 +8,7 @@ from typing import Any, Optional from asusrouter.error import AsusRouterError +from asusrouter.modules.client import AsusClientConnectionWlan from asusrouter.modules.connection import ConnectionState, ConnectionType from asusrouter.modules.identity import AsusDevice from asusrouter.modules.parental_control import ParentalControlRule @@ -115,6 +116,7 @@ def __init__( self._latest_connected_list: list[dict[str, Any]] = [] self._aimesh_number: int = 0 self._aimesh_list: list[dict[str, Any]] = [] + self._gn_clients_number: int = 0 async def _get_clients(self) -> dict[str, Any]: """Return clients sensors.""" @@ -124,6 +126,7 @@ async def _get_clients(self) -> dict[str, Any]: SENSORS_CONNECTED_DEVICES[1]: self._clients_list, SENSORS_CONNECTED_DEVICES[2]: self._latest_connected_list, SENSORS_CONNECTED_DEVICES[3]: self._latest_connected, + SENSORS_CONNECTED_DEVICES[4]: self._gn_clients_number, } async def _get_aimesh(self) -> dict[str, Any]: @@ -143,6 +146,7 @@ def update_clients( clients_list: Optional[list[Any]], latest_connected: Optional[datetime], latest_connected_list: list[Any], + gn_clients_number: int, ) -> bool: """Update connected devices attribute.""" @@ -151,12 +155,14 @@ def update_clients( and self._clients_list == clients_list and self._latest_connected == latest_connected and self._latest_connected_list == latest_connected_list + and self._gn_clients_number == gn_clients_number ): return False self._clients_number = clients_number self._clients_list = clients_list self._latest_connected = latest_connected self._latest_connected_list = latest_connected_list + self._gn_clients_number = gn_clients_number return True def update_aimesh( @@ -286,6 +292,7 @@ def __init__( self._latest_connected: Optional[datetime] = None self._latest_connected_list: list[dict[str, Any]] = [] self._connect_error: bool = False + self._gn_clients_number: int = 0 # Sensor filters self.sensor_filters: dict[tuple[str, str], list[str]] = {} @@ -563,11 +570,18 @@ async def update_clients(self) -> None: self._clients_number = 0 self._clients_list = [] + # Connected GuestNetwork clients sensor + self._gn_clients_number = 0 + for client_mac, client in self._clients.items(): if client.state: self._clients_number += 1 self._clients_list.append(client.identity) + if isinstance(client.connection, AsusClientConnectionWlan) and client.connection.guest: + self._gn_clients_number += 1 + + # Filter clients # Only include the listed clients if self._client_filter == "include": @@ -802,6 +816,7 @@ async def _init_sensor_coordinators(self) -> None: self._clients_list, self._latest_connected, self._latest_connected_list, + self._gn_clients_number, ) self._sensor_handler.update_aimesh( self._aimesh_number, @@ -861,6 +876,7 @@ async def _update_unpolled_sensors(self) -> None: clients_list, self._latest_connected, self._latest_connected_list, + self._gn_clients_number, ): await coordinator.async_refresh() diff --git a/custom_components/asusrouter/update.py b/custom_components/asusrouter/update.py index 70c750c..2f94705 100644 --- a/custom_components/asusrouter/update.py +++ b/custom_components/asusrouter/update.py @@ -6,13 +6,14 @@ import logging from typing import Any -from asusrouter.modules.system import AsusSystem from homeassistant.components.update import UpdateEntity, UpdateEntityFeature from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from asusrouter.modules.system import AsusSystem + from .const import STATIC_UPDATES from .dataclass import ARUpdateDescription from .entity import ARBinaryEntity, async_setup_ar_entry @@ -49,9 +50,13 @@ def __init__( super().__init__(coordinator, router, description) self.entity_description: ARUpdateDescription = description - self._attr_installed_version = self.extra_state_attributes.get("current") - self._attr_latest_version = self.extra_state_attributes.get("available") - self._attr_release_summary = self.extra_state_attributes.get("release_note") + self._attr_installed_version = self.extra_state_attributes.get( + "current" + ) + self._attr_latest_version = self.extra_state_attributes.get("latest") + self._attr_release_summary = self.extra_state_attributes.get( + "release_note" + ) self._attr_in_progress = False self._attr_supported_features = ( @@ -87,5 +92,6 @@ async def async_install( except Exception as ex: # pylint: disable=broad-except _LOGGER.error( - "An exception occurred while trying to install the update: %s", ex + "An exception occurred while trying to install the update: %s", + ex, )