Skip to content

Commit

Permalink
Add Xiaomi Mi WiFi Router PRO R3P
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmitry Mamontov committed Mar 12, 2021
1 parent 4074347 commit 2b50dbe
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 23 deletions.
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ A. This is a legacy device tracking option. But the integration allows importing

A. Yes, the integration supports devices connected in `repeater mode`. But to get the number of devices and their tracking, you will also need to connect and configure the parent router.

**Q. Can I use the router in repeater mode without a parent MiWiFi device?**

A. It is possible with the `force_load_repeater_devices` option enabled. But there is a limitation. You will not see IP, uptime, and connection type, but the name will be the mac-address.

## Install
Installed through the custom repository [HACS](https://hacs.xyz/) - `dmamontov/hass-miwifi`

Expand All @@ -52,6 +56,7 @@ For authorization, use the ip of your router and its password
miwifi:
ip_address: router_ip
password: router_pass
force_load_repeater_devices: False # PRO
```
## Advanced config
Expand All @@ -66,7 +71,7 @@ The component supports automatic deletion of monitored devices after a specified
```yaml
miwifi:
...
last_activity_days: 30
last_activity_days: 30 # PRO
```

## Services
Expand Down Expand Up @@ -96,9 +101,13 @@ target:
4. Add new Lovelace card: [example](https://gist.github.com/dmamontov/e6fa1842c486388387aaf061d3a82818)

## Routers tested
| Router | Firmware version | Region | Status |
| --------------------------------------------------------------------------------- | ---------------- | ------ | --------- |
| [Xiaomi AC2100](https://xiaomiplanets.com/review-xiaomi-ac2100-router/) | 2.0.743 | CN | Supported |
| [Xiaomi AX3600](https://xiaomiplanets.com/xiaomi-aiot-router-ax3600-performance/) | 1.0.79 | CN | Supported |

Many more Xiaomi and Redmi routers supported by MiWiFi (OpenWRT - Luci API)

| Router | Firmware version | Status |
| ----------------------------------------------------------------------------------- | -------------------------- | ----------------------------- |
| [Xiaomi AC2100](https://xiaomiplanets.com/review-xiaomi-ac2100-router/) | 2.0.743(CN) | Supported |
| [Xiaomi AX3600](https://xiaomiplanets.com/xiaomi-aiot-router-ax3600-performance/) | 1.0.79(CN), 3.0.22(Global) | Supported |
| [Xiaomi AX1800](https://xiaomiplanets.com/xiaomi-my-router-ax1800-performance-11/) | 3.0.34(Global) | Supported |
| [Xiaomi PRO R3P](https://xiaomiplanets.com/xiaomi-mi-router-for-hd-action-1/) | 2.16.29(CN) | With restrictions<sup>*</sup> |

<sup>*</sup> Not all integration options may be supported.
11 changes: 9 additions & 2 deletions custom_components/miwifi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
from homeassistant.helpers.json import JSONEncoder
from homeassistant.helpers.storage import Store

from .core.const import DOMAIN, CONF_LAST_ACTIVITY_DAYS, DEFAULT_LAST_ACTIVITY_DAYS, STORAGE_VERSION
from .core.const import (
DOMAIN,
CONF_FORCE_LOAD_REPEATER_DEVICES,
CONF_LAST_ACTIVITY_DAYS,
DEFAULT_LAST_ACTIVITY_DAYS,
STORAGE_VERSION
)
from .core.luci_data import LuciData

_LOGGER = logging.getLogger(__name__)
Expand All @@ -22,7 +28,8 @@
vol.Schema({
vol.Required(CONF_IP_ADDRESS): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_LAST_ACTIVITY_DAYS, default = DEFAULT_LAST_ACTIVITY_DAYS): cv.positive_int
vol.Optional(CONF_FORCE_LOAD_REPEATER_DEVICES, default = False): cv.boolean,
vol.Optional(CONF_LAST_ACTIVITY_DAYS, default = DEFAULT_LAST_ACTIVITY_DAYS): cv.positive_int,
})
]
)
Expand Down
8 changes: 6 additions & 2 deletions custom_components/miwifi/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .core.const import DOMAIN, CONF_LAST_ACTIVITY_DAYS, DEFAULT_LAST_ACTIVITY_DAYS
from .core.const import DOMAIN, CONF_LAST_ACTIVITY_DAYS, CONF_FORCE_LOAD_REPEATER_DEVICES, DEFAULT_LAST_ACTIVITY_DAYS
from .core import exceptions
from .core.luci import Luci

_LOGGER = logging.getLogger(__name__)

AUTH_SCHEMA = vol.Schema({
vol.Required(CONF_IP_ADDRESS): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_PASSWORD): str
})

class MiWifiFlowHandler(ConfigFlow, domain = DOMAIN):
Expand Down Expand Up @@ -78,6 +78,10 @@ async def async_step_settings(self, user_input = None):
options_schema = vol.Schema({
vol.Required(CONF_IP_ADDRESS, default = self.config_entry.options.get(CONF_IP_ADDRESS, "")): str,
vol.Required(CONF_PASSWORD, default = self.config_entry.options.get(CONF_PASSWORD, "")): str,
vol.Optional(
CONF_FORCE_LOAD_REPEATER_DEVICES,
default = self.config_entry.options.get(CONF_FORCE_LOAD_REPEATER_DEVICES, False)
): cv.boolean,
vol.Optional(
CONF_LAST_ACTIVITY_DAYS,
default = self.config_entry.options.get(CONF_LAST_ACTIVITY_DAYS, DEFAULT_LAST_ACTIVITY_DAYS)
Expand Down
1 change: 1 addition & 0 deletions custom_components/miwifi/core/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
DOMAINS = ["binary_sensor", "sensor", "light", "switch", "device_tracker"]
SCAN_INTERVAL = 10

CONF_FORCE_LOAD_REPEATER_DEVICES = "force_load_repeater_devices"
CONF_LAST_ACTIVITY_DAYS = "last_activity_days"
DEFAULT_LAST_ACTIVITY_DAYS = 30

Expand Down
31 changes: 26 additions & 5 deletions custom_components/miwifi/core/luci.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@
_LOGGER = logging.getLogger(__name__)

class Luci(object):
def __init__(self, hass: HomeAssistant, session, ip: str, password: str):
def __init__(
self,
hass: HomeAssistant,
session,
ip: str,
password: str,
options: dict = {}
) -> None:
if ip.endswith("/"):
ip = ip[:-1]

Expand All @@ -46,6 +53,7 @@ def __init__(self, hass: HomeAssistant, session, ip: str, password: str):

self.base_url = BASE_RESOURCE.format(ip = ip)
self.password = password
self.is_force_load = options["is_force_load"] if "is_force_load" in options else False

self.is_repeater_mode = False

Expand Down Expand Up @@ -123,6 +131,10 @@ async def set_device_data(self) -> None:
init_info = await self.init_info()
status = await self.status()

model = init_info["model"] if "model" in init_info else None
if not model and "hardware" in init_info:
model = init_info["hardware"]

self._device_data = {
"mac": status["hardware"]["mac"],
"name": init_info["routername"] if "routername" in init_info else None,
Expand All @@ -146,10 +158,12 @@ async def set_entity_data(self) -> None:
if state["up"] != 1:
self._state = False

self._data["light"]["led"] = led["status"] == 1
uptime = wan_info["info"]["uptime"] if isinstance(wan_info["info"], dict) and "uptime" in wan_info["info"] else 0

self._data["light"]["led"] = led["status"] == 1 if "status" in led else False
self._data["binary_sensor"]["repeater_mode"] = self.is_repeater_mode
self._data["binary_sensor"]["wifi_state"] = wifi_state
self._data["binary_sensor"]["wan_state"] = wan_info["info"]["uptime"] > 0
self._data["binary_sensor"]["wan_state"] = uptime

async def set_devices_list(self) -> None:
wifi_connect_devices = await self.wifi_connect_devices()
Expand All @@ -159,6 +173,9 @@ async def set_devices_list(self) -> None:
for index, device in enumerate(wifi_connect_devices["list"]):
self._signals[device["mac"]] = device["signal"]

if self.is_repeater_mode and self.is_force_load:
self.add_devices(self._signals)

if self.is_repeater_mode or DOMAIN not in self.hass.data:
return

Expand Down Expand Up @@ -191,7 +208,7 @@ async def set_devices_list(self) -> None:

if ip == self._ip:
self.add_devices(devices_to_ip[ip])
else:
elif not self.hass.data[DOMAIN][entities_map[ip]].api.is_force_load:
self.hass.data[DOMAIN][entities_map[ip]].api.add_devices(devices_to_ip[ip])
await asyncio.sleep(1)
self.hass.data[DOMAIN][entities_map[ip]].update_devices()
Expand All @@ -214,13 +231,17 @@ def add_devices(self, devices: dict) -> None:
devices[mac]["router_mac"] = self._device_data["mac"]
devices[mac]["signal"] = self._signals[mac] if mac in self._signals else 0

if "name" not in devices[mac]:
devices[mac]["name"] = mac

if mac not in self._devices_list:
self._new_devices.append(mac)

self._current_devices.append(mac)
self._devices_list[mac] = devices[mac]

self._data["sensor"][CONNECTION_TO_SENSOR[devices[mac]["type"]]] += 1
if "type" in devices[mac]:
self._data["sensor"][CONNECTION_TO_SENSOR[devices[mac]["type"]]] += 1

async def get_entities_map(self) -> dict:
entries_map = {}
Expand Down
21 changes: 19 additions & 2 deletions custom_components/miwifi/core/luci_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@
from homeassistant.helpers.storage import Store

from . import exceptions
from .const import DOMAIN, DOMAINS, DATA_UPDATED, DEVICES_UPDATED, SCAN_INTERVAL, CONF_LAST_ACTIVITY_DAYS, DEFAULT_LAST_ACTIVITY_DAYS
from .const import (
DOMAIN,
DOMAINS,
DATA_UPDATED,
DEVICES_UPDATED,
SCAN_INTERVAL,
CONF_FORCE_LOAD_REPEATER_DEVICES,
CONF_LAST_ACTIVITY_DAYS,
DEFAULT_LAST_ACTIVITY_DAYS
)
from .luci import Luci

_LOGGER = logging.getLogger(__name__)
Expand All @@ -28,7 +37,15 @@ def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry, store: Store,

session = async_get_clientsession(hass, False)

self.api = Luci(hass, session, self.ip, self.password)
self.api = Luci(
hass,
session,
self.ip,
self.password,
{
"is_force_load": config_entry.options.get(CONF_FORCE_LOAD_REPEATER_DEVICES, False)
}
)

self.unsub_timer = None
self.available = False
Expand Down
8 changes: 4 additions & 4 deletions custom_components/miwifi/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ def update_devices() -> None:

def _get_new_device(hass: HomeAssistant, device: dict, legacy_device: dict) -> dict:
return {
"ip": device["ip"][0]["ip"],
"connection": device["connection"],
"ip": device["ip"][0]["ip"] if "ip" in device else "0:0:0:0",
"connection": device["connection"] if "connection" in device else "Not detected",
"router_mac": device["router_mac"],
"name": legacy_device["name"] if "name" in legacy_device else device["name"],
"icon": legacy_device["icon"] if "icon" in legacy_device else None,
"signal": device["signal"],
"online": device["ip"][0]["online"],
"online": device["ip"][0]["online"] if "ip" in device else "0",
"unique_id": async_generate_entity_id(
DEVICE_TRACKER_ENTITY_ID_FORMAT,
legacy_device["dev_id"] if "dev_id" in legacy_device else device["name"],
Expand Down Expand Up @@ -269,7 +269,7 @@ async def async_update(self) -> None:
if old_router_mac != self._device["router_mac"]:
self.luci.remove_device(self._mac)
remove_enries.append(self._config_entry)

self.luci = self.hass.data[DOMAIN][entry_id]
self._config_entry = self.luci.config_entry

Expand Down
3 changes: 2 additions & 1 deletion custom_components/miwifi/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"data": {
"ip_address": "IP address",
"password": "Password",
"last_activity_days": "Number of days to wait after the last activity"
"force_load_repeater_devices": "Forced booting of devices in repeater mode [PRO]",
"last_activity_days": "Allowed number of days to wait after the last activity [PRO]"
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion custom_components/miwifi/translations/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"data": {
"ip_address": "IP адрес",
"password": "Пароль",
"last_activity_days": "Допустимое количество дней ожидания после последней активности"
"force_load_repeater_devices": "Принудительная загрузка устройств в режиме репитера [PRO]",
"last_activity_days": "Допустимое количество дней ожидания после последней активности [PRO]"
}
}
}
Expand Down

0 comments on commit 2b50dbe

Please sign in to comment.