Skip to content

Commit

Permalink
Merge pull request #63 from dmamontov/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
dmamontov committed May 16, 2022
2 parents 57fbc1d + 1227433 commit 0d14ed6
Show file tree
Hide file tree
Showing 15 changed files with 152 additions and 31 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ jobs:
language: [ 'python' ]
steps:
- uses: actions/checkout@v2
- uses: github/codeql-action/init@v1
- uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- uses: github/codeql-action/autobuild@v1
- uses: github/codeql-action/analyze@v1
- uses: github/codeql-action/autobuild@v2
- uses: github/codeql-action/analyze@v2

hassfest:
runs-on: "ubuntu-latest"
Expand Down
3 changes: 3 additions & 0 deletions custom_components/miwifi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from homeassistant.exceptions import PlatformNotReady

from .const import (
CONF_ENCRYPTION_ALGORITHM,
CONF_ACTIVITY_DAYS,
CONF_IS_FORCE_LOAD,
DEFAULT_ACTIVITY_DAYS,
Expand All @@ -31,6 +32,7 @@
UPDATER,
)
from .discovery import async_start_discovery
from .enum import EncryptionAlgorithm
from .helper import get_config_value, get_store
from .services import SERVICES
from .updater import LuciUpdater
Expand Down Expand Up @@ -59,6 +61,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass,
_ip,
get_config_value(entry, CONF_PASSWORD),
get_config_value(entry, CONF_ENCRYPTION_ALGORITHM, EncryptionAlgorithm.SHA1),
get_config_value(entry, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL),
get_config_value(entry, CONF_TIMEOUT, DEFAULT_TIMEOUT),
get_config_value(entry, CONF_IS_FORCE_LOAD, False),
Expand Down
35 changes: 35 additions & 0 deletions custom_components/miwifi/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
CONF_ACTIVITY_DAYS,
CONF_IS_FORCE_LOAD,
CONF_STAY_ONLINE,
CONF_ENCRYPTION_ALGORITHM,
DEFAULT_ACTIVITY_DAYS,
DEFAULT_SCAN_INTERVAL,
DEFAULT_STAY_ONLINE,
Expand All @@ -32,6 +33,7 @@
UPDATER,
)
from .discovery import async_start_discovery
from .enum import EncryptionAlgorithm
from .helper import async_user_documentation_url, async_verify_access, get_config_value

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -125,6 +127,15 @@ async def async_step_user(
{
vol.Required(CONF_IP_ADDRESS): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(
CONF_ENCRYPTION_ALGORITHM,
default=EncryptionAlgorithm.SHA1,
): vol.In(
[
EncryptionAlgorithm.SHA1,
EncryptionAlgorithm.SHA256,
]
),
vol.Required(
CONF_STAY_ONLINE, default=DEFAULT_STAY_ONLINE
): cv.positive_int,
Expand Down Expand Up @@ -160,6 +171,7 @@ async def async_step_discovery_confirm(
self.hass,
user_input[CONF_IP_ADDRESS],
user_input[CONF_PASSWORD],
user_input[CONF_ENCRYPTION_ALGORITHM],
user_input[CONF_TIMEOUT],
)

Expand Down Expand Up @@ -202,6 +214,15 @@ async def async_step_discovery_confirm(
{
vol.Required(CONF_IP_ADDRESS, default=_ip): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(
CONF_ENCRYPTION_ALGORITHM,
default=EncryptionAlgorithm.SHA1,
): vol.In(
[
EncryptionAlgorithm.SHA1,
EncryptionAlgorithm.SHA256,
]
),
vol.Required(
CONF_STAY_ONLINE, default=DEFAULT_STAY_ONLINE
): cv.positive_int,
Expand Down Expand Up @@ -243,6 +264,7 @@ async def async_step_init(self, user_input: ConfigType | None = None) -> FlowRes
self.hass,
user_input[CONF_IP_ADDRESS],
user_input[CONF_PASSWORD],
user_input[CONF_ENCRYPTION_ALGORITHM],
user_input[CONF_TIMEOUT],
)

Expand Down Expand Up @@ -279,6 +301,19 @@ def _get_options_schema(self) -> vol.Schema:
CONF_PASSWORD,
default=get_config_value(self._config_entry, CONF_PASSWORD, ""),
): str,
vol.Required(
CONF_ENCRYPTION_ALGORITHM,
default=get_config_value(
self._config_entry,
CONF_ENCRYPTION_ALGORITHM,
EncryptionAlgorithm.SHA1,
),
): vol.In(
[
EncryptionAlgorithm.SHA1,
EncryptionAlgorithm.SHA256,
]
),
vol.Required(
CONF_STAY_ONLINE,
default=get_config_value(
Expand Down
1 change: 1 addition & 0 deletions custom_components/miwifi/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
CONF_STAY_ONLINE: Final = "stay_online"
CONF_IS_FORCE_LOAD: Final = "is_force_load"
CONF_ACTIVITY_DAYS: Final = "activity_days"
CONF_ENCRYPTION_ALGORITHM: Final = "encryption_algorithm"
CONF_REQUEST: Final = "request"
CONF_RESPONSE: Final = "response"
CONF_URI: Final = "uri"
Expand Down
8 changes: 8 additions & 0 deletions custom_components/miwifi/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

from enum import Enum, IntEnum
from homeassistant.backports.enum import StrEnum

from .const import (
ATTR_SWITCH_WIFI_2_4,
Expand Down Expand Up @@ -171,6 +172,13 @@ def __str__(self) -> str:
SKIP = 2, "Skip"


class EncryptionAlgorithm(StrEnum):
"""EncryptionAlgorithm enum"""

SHA1 = "sha1"
SHA256 = "sha256"


class Model(str, Enum):
"""Model enum"""

Expand Down
9 changes: 8 additions & 1 deletion custom_components/miwifi/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,26 @@ async def async_verify_access(
hass: HomeAssistant,
ip: str, # pylint: disable=invalid-name
password: str,
encryption: str,
timeout: int = DEFAULT_TIMEOUT,
) -> codes:
"""Verify ip and password.
:param hass: HomeAssistant: Home Assistant object
:param ip: str: device ip address
:param encryption: str: password encryption
:param password: str: device password
:param timeout: int: Timeout
:return int: last update success
"""

updater = LuciUpdater(
hass=hass, ip=ip, password=password, timeout=timeout, is_only_login=True
hass=hass,
ip=ip,
password=password,
encryption=encryption,
timeout=timeout,
is_only_login=True,
)

await updater.async_request_refresh()
Expand Down
47 changes: 31 additions & 16 deletions custom_components/miwifi/luci.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@
DIAGNOSTIC_DATE_TIME,
DIAGNOSTIC_MESSAGE,
)
from .enum import EncryptionAlgorithm
from .exceptions import LuciConnectionError, LuciError, LuciRequestError

_LOGGER = logging.getLogger(__name__)


# pylint: disable=too-many-public-methods
# pylint: disable=too-many-public-methods,too-many-arguments
class LuciClient:
"""Luci API Client."""

ip: str = CLIENT_ADDRESS # pylint: disable=invalid-name

_client: AsyncClient
_password: str | None = None
_encryption: str = EncryptionAlgorithm.SHA1
_timeout: int = DEFAULT_TIMEOUT

_token: str | None = None
Expand All @@ -51,13 +53,15 @@ def __init__(
client: AsyncClient,
ip: str = CLIENT_ADDRESS, # pylint: disable=invalid-name
password: str | None = None,
encryption: str = EncryptionAlgorithm.SHA1,
timeout: int = DEFAULT_TIMEOUT,
) -> None:
"""Initialize API client.
:param client: AsyncClient: AsyncClient object
:param ip: str: device ip address
:param password: str: device password
:param encryption: str: password encryption algorithm
:param timeout: int: Query execution timeout
"""

Expand All @@ -67,6 +71,7 @@ def __init__(
self._client = client
self.ip = ip # pylint: disable=invalid-name
self._password = password
self._encryption = encryption
self._timeout = timeout

self._url = CLIENT_URL.format(ip=ip)
Expand All @@ -83,18 +88,20 @@ async def login(self) -> dict:
_nonce: str = self.generate_nonce()
_url: str = f"{self._url}/api/{_method}"

_request_data: dict = {
"username": CLIENT_USERNAME,
"logtype": str(CLIENT_LOGIN_TYPE),
"password": self.generate_password_hash(_nonce, str(self._password)),
"nonce": _nonce,
}

try:
self._debug("Start request", _url, json.dumps(_request_data), _method, True)

async with self._client as client:
response: Response = await client.post(
_url,
data={
"username": CLIENT_USERNAME,
"logtype": str(CLIENT_LOGIN_TYPE),
"password": self.generate_password_hash(
_nonce, str(self._password)
),
"nonce": _nonce,
},
data=_request_data,
timeout=self._timeout,
)

Expand Down Expand Up @@ -379,14 +386,16 @@ async def image(self, hardware: str) -> bytes | None:

return None

@staticmethod
def sha1(key: str) -> str:
"""Generate sha1 by key.
def sha(self, key: str) -> str:
"""Generate sha by key.
:param key: str: the key from which to get the hash
:return str: sha1 from key.
:return str: sha from key.
"""

if self._encryption == EncryptionAlgorithm.SHA256:
return hashlib.sha256(key.encode()).hexdigest()

return hashlib.sha1(key.encode()).hexdigest()

@staticmethod
Expand Down Expand Up @@ -415,22 +424,28 @@ def generate_password_hash(self, nonce: str, password: str) -> str:
:param nonce: str: nonce
:param password: str: password
:return str: sha1 from password and nonce.
:return str: sha from password and nonce.
"""

return self.sha1(nonce + self.sha1(password + CLIENT_PUBLIC_KEY))
return self.sha(nonce + self.sha(password + CLIENT_PUBLIC_KEY))

def _debug(self, message: str, url: str, content: Any, path: str) -> None:
def _debug(
self, message: str, url: str, content: Any, path: str, is_only_log: bool = False
) -> None:
"""Debug log
:param message: str: Message
:param url: str: URL
:param content: Any: Content
:param path: str: Path
:param is_only_log: bool: Is only log
"""

_LOGGER.debug("%s (%s): %s", message, url, str(content))

if is_only_log:
return

_content: dict | str = {}

try:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/miwifi/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"domain": "miwifi",
"name": "MiWiFi",
"version": "2.7.0",
"version": "2.7.1",
"documentation": "https://github.com/dmamontov/hass-miwifi/blob/main/README.md",
"issue_tracker": "https://github.com/dmamontov/hass-miwifi/issues",
"config_flow": true,
Expand Down
6 changes: 4 additions & 2 deletions custom_components/miwifi/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"config": {
"error": {
"ip_address.not_matched": "Ungültige IP Adresse",
"password.not_matched": "Ungültiges Passwort",
"password.not_matched": "Falsches Passwort oder Verschlüsselungsalgorithmus",
"connection": "Fehler bei der Herstellung einer Verbindung",
"router.not.supported": "Der Router wird nicht unterstützt. Detaillierte Informationen wurden an das Benachrichtigungszentrum gesendet."
},
Expand All @@ -14,6 +14,7 @@
"data": {
"ip_address": "IP Adresse",
"password": "Passwort",
"encryption_algorithm": "Passwort Verschlüsselungsalgorithmus",
"stay_online": "Mindestaufenthalt in Sekunden online",
"scan_interval": "Scanintervall in Sekunden [PRO]",
"timeout": "Timeout von Anfragen in Sekunden [PRO]"
Expand All @@ -24,7 +25,7 @@
"options": {
"error": {
"ip_address.not_matched": "Ungültige IP Adresse",
"password.not_matched": "Ungültiges Passwort",
"password.not_matched": "Falsches Passwort oder Verschlüsselungsalgorithmus",
"connection": "Fehler bei der Herstellung einer Verbindung",
"router.not.supported": "Der Router wird nicht unterstützt. Detaillierte Informationen wurden an das Benachrichtigungszentrum gesendet."
},
Expand All @@ -35,6 +36,7 @@
"data": {
"ip_address": "IP Adresse",
"password": "Passwort",
"encryption_algorithm": "Passwort Verschlüsselungsalgorithmus",
"stay_online": "Mindestaufenthalt in Sekunden online",
"scan_interval": "Scanintervall in Sekunden [PRO]",
"activity_days": "Anzahl an Tagen, die nach der letzten Aktivität gewartet werden soll [PRO]",
Expand Down
6 changes: 4 additions & 2 deletions custom_components/miwifi/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"config": {
"error": {
"ip_address.not_matched": "Invalid IP",
"password.not_matched": "Invalid password",
"password.not_matched": "Wrong password or encryption algorithm",
"connection": "Failed to establish connection",
"router.not.supported": "The router is not supported. Detailed information has been sent to the notification center."
},
Expand All @@ -14,6 +14,7 @@
"data": {
"ip_address": "IP address",
"password": "Password",
"encryption_algorithm": "Password encryption algorithm",
"stay_online": "Minimum stay online in seconds",
"scan_interval": "Scan interval in seconds [PRO]",
"timeout": "Timeout of requests in seconds [PRO]"
Expand All @@ -24,7 +25,7 @@
"options": {
"error": {
"ip_address.not_matched": "Invalid IP",
"password.not_matched": "Invalid password",
"password.not_matched": "Wrong password or encryption algorithm",
"connection": "Failed to establish connection",
"router.not.supported": "The router is not supported. Detailed information has been sent to the notification center."
},
Expand All @@ -35,6 +36,7 @@
"data": {
"ip_address": "IP address",
"password": "Password",
"encryption_algorithm": "Password encryption algorithm",
"stay_online": "Minimum stay online in seconds",
"scan_interval": "Scan interval in seconds [PRO]",
"activity_days": "Allowed number of days to wait after the last activity [PRO]",
Expand Down
Loading

0 comments on commit 0d14ed6

Please sign in to comment.