Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

Commit

Permalink
Merge pull request #29 from Thomas55555/dev
Browse files Browse the repository at this point in the history
pylint f006320 29ae736
use aioautomower instead of husqvarna_automower 645230d
import constants from home assistant 75c954b
isort 54e5fde
bump to version 2021.3.4 a5ad401
  • Loading branch information
Thomas55555 authored Mar 15, 2021
2 parents eb7c2fc + a5ad401 commit cea1913
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 51 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ logger:
custom_components.husqvarna_automower: debug
custom_components.husqvarna_automower.vacuum: debug
custom_components.husqvarna_automower.config_flow: debug
husqvarna_automower: debug
aioautomower: debug
```

After a restart detailed log entries will appear in `/config/home-assistant.log`.
47 changes: 29 additions & 18 deletions custom_components/husqvarna_automower/__init__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
"""The Husqvarna Automower integration."""
import asyncio
import logging
import time
from datetime import timedelta

from aioautomower import GetAccessToken, GetMowerData, RefreshAccessToken
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from custom_components.husqvarna_automower.const import (
from homeassistant.const import (
CONF_ACCESS_TOKEN,
CONF_API_KEY,
CONF_PASSWORD,
CONF_USERNAME,
DOMAIN,
PLATFORMS,
STARTUP_MESSAGE,
)
from husqvarna_automower import GetAccessToken, GetMowerData, RefreshAccessToken
from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN, PLATFORMS, STARTUP_MESSAGE

SCAN_INTERVAL = timedelta(seconds=300)

Expand Down Expand Up @@ -60,6 +60,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):


class AuthenticationUpdateCoordinator(DataUpdateCoordinator):
"""Update Coordinator."""

def __init__(self, hass, username, password, api_key):
"""Initialize."""
_LOGGER.info("Inizialising UpdateCoordiantor")
Expand All @@ -69,44 +71,53 @@ def __init__(self, hass, username, password, api_key):
self.api_key = api_key
self.access_token = None
self.token_expires_at = 0
self.access_token_raw = None
self.provider = None
self.token_type = None
self.mower_api = None
self.get_token = GetAccessToken(self.api_key, self.username, self.password)
self.refresh_token = None

super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)

async def _async_update_data(self):
"""Update data via library."""
_LOGGER.info("Updating data")
if (self.access_token is None):
self.auth_api = GetAccessToken(self.api_key, self.username, self.password)
if self.access_token is None:
_LOGGER.debug("Getting new token, because Null")
try:
self.access_token_raw = await self.auth_api.async_get_access_token()
self.access_token_raw = await self.get_token.async_get_access_token()
self.access_token = self.access_token_raw["access_token"]
self.provider = self.access_token_raw["provider"]
self.token_type = self.access_token_raw["token_type"]
self.refresh_token = self.access_token_raw["refresh_token"]
self.token_expires_at = (
self.access_token_raw["expires_in"] + time.time()
)
_LOGGER.debug(f"Token expires at {self.token_expires_at} UTC")
_LOGGER.debug("Token expires at %i UTC", self.token_expires_at)
except Exception:
_LOGGER.debug(
f"Error message for UpdateFailed: {self.access_token_raw['status']}"
"Error message for UpdateFailed: %i",
self.access_token_raw["status"],
)
raise UpdateFailed("Error communicating with API")
elif self.token_expires_at < time.time():
self.auth_api = RefreshAccessToken(self.api_key, self.refresh_token)
_LOGGER.debug("Getting new token, because expired")
self.refresh_token = RefreshAccessToken(self.api_key, self.refresh_token)
try:
self.access_token_raw = await self.auth_api.async_refresh_access_token()
self.access_token_raw = (
await self.refresh_token.async_refresh_access_token()
)
self.access_token = self.access_token_raw["access_token"]
self.refresh_token = self.access_token_raw["refresh_token"]
self.token_expires_at = (
self.access_token_raw["expires_in"] + time.time()
)
_LOGGER.debug(f"Token expires at {self.token_expires_at} UTC")
_LOGGER.debug("Token expires at %i UTC", self.token_expires_at)
except Exception:
_LOGGER.debug(
f"Error message for UpdateFailed: {self.access_token_raw['status']}"
"Error message for UpdateFailed: %i",
self.access_token_raw["status"],
)
raise UpdateFailed("Error communicating with API")

Expand Down
32 changes: 19 additions & 13 deletions custom_components/husqvarna_automower/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
"""Config flow to add the integration via the UI."""
import logging
import time
from collections import OrderedDict

import voluptuous as vol
from aioautomower import GetAccessToken, GetMowerData
from homeassistant import config_entries
from homeassistant.core import callback

from custom_components.husqvarna_automower.const import ( # pylint: disable=unused-import
from homeassistant.const import (
CONF_ACCESS_TOKEN,
CONF_API_KEY,
CONF_PASSWORD,
CONF_USERNAME,
DOMAIN,
HUSQVARNA_URL,
)
from husqvarna_automower import GetAccessToken, GetMowerData
from homeassistant.core import callback

from .const import DOMAIN, HUSQVARNA_URL

CONF_ID = "unique_id"

Expand Down Expand Up @@ -48,11 +49,12 @@ async def async_step_user(self, user_input=None):

errors = {}
try:
await try_connection(
config_data = await try_connection(
user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
user_input[CONF_API_KEY],
)
_LOGGER.debug("Config_data %s", config_data)
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "auth"
Expand All @@ -70,16 +72,17 @@ async def async_step_user(self, user_input=None):
CONF_USERNAME: user_input[CONF_USERNAME],
CONF_PASSWORD: user_input[CONF_PASSWORD],
CONF_API_KEY: user_input[CONF_API_KEY],
CONF_ACCESS_TOKEN: config_data,
},
)


async def try_connection(username, password, api_key):
"""Try to connect to Husqvarna with the given inputs."""
_LOGGER.debug("Trying to connect to Husqvarna")
auth_api = GetAccessToken(api_key, username, password)
access_token_raw = await auth_api.async_get_access_token()
_LOGGER.debug(f"Access token raw: {access_token_raw}")
_LOGGER.debug(f"Access token status: {access_token_raw['status']}")
_LOGGER.debug("Access token status: %s", access_token_raw["status"])
if access_token_raw["status"] == 200:
_LOGGER.debug("Connected with the Authentication API")
access_token = access_token_raw["access_token"]
Expand All @@ -92,20 +95,23 @@ async def try_connection(username, password, api_key):
_LOGGER.error("Error 401 - Unauthorized check your credentials")
raise Exception
else:
_LOGGER.error(f"Error {access_token_raw['status']}")
_LOGGER.error("Error %s", access_token_raw["status"])
raise Exception
automower_api = GetMowerData(api_key, access_token, provider, token_type)
mower_data = await automower_api.async_mower_state()
if mower_data["status"] == 200:
_LOGGER.debug("Connected with the Automower Connect API")
elif mower_data["status"] == 403:
_LOGGER.error(
f"Error 403 - Make sure that you are connected to the Authentication API and the Automower Connect API on {HUSQVARNA_URL}"
"Error 403 - Make sure that you are connected to the Authentication API and the Automower Connect API on %s",
HUSQVARNA_URL,
)
raise Exception
else:
_LOGGER.error(f"Error {mower_data['status']}")
_LOGGER.error("Error %s", mower_data["status"])
raise Exception
_LOGGER.debug(f"Mower data: {mower_data}")

_LOGGER.debug("Mower data: %s", mower_data)
_LOGGER.debug("Successfully connected Authentication and Automower Connect API")
time.sleep(5)
return access_token
4 changes: 1 addition & 3 deletions custom_components/husqvarna_automower/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
NAME = "husqvarna_automower"
DOMAIN = "husqvarna_automower"
DOMAIN_DATA = f"{DOMAIN}_data"
VERSION = "2021.3.2"
VERSION = "2021.3.4"

ISSUE_URL = "https://github.com/Thomas55555/husqvarna_automower"
HUSQVARNA_URL = "https://developer.husqvarnagroup.cloud/"
Expand All @@ -21,8 +21,6 @@

# Configuration and options
CONF_ENABLED = "enabled"
CONF_USERNAME = "username"
CONF_PASSWORD = "password"
CONF_API_KEY = "api_key"


Expand Down
4 changes: 2 additions & 2 deletions custom_components/husqvarna_automower/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"dependencies": [],
"config_flow": true,
"codeowners": ["@Thomas55555"],
"requirements": ["husqvarna-automower==2021.3.2"],
"version": "2021.3.2"
"requirements": ["aioautomower==2021.3.4"],
"version": "2021.3.4"
}
36 changes: 22 additions & 14 deletions custom_components/husqvarna_automower/vacuum.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Creates a vacuum entity for the mower"""
import time

from aioautomower import Return
from homeassistant.components.vacuum import (
STATE_CLEANING,
STATE_DOCKED,
Expand All @@ -21,8 +23,7 @@
from homeassistant.helpers import entity
from homeassistant.helpers.update_coordinator import CoordinatorEntity, UpdateFailed

from custom_components.husqvarna_automower.const import DOMAIN, ERRORCODES, ICON
from husqvarna_automower import Return
from .const import DOMAIN, ERRORCODES, ICON

SUPPORT_STATE_SERVICES = (
SUPPORT_STATE
Expand Down Expand Up @@ -76,6 +77,15 @@ def __init__(self, coordinator, idx):
self.token_type = self.coordinator.data["token"]["token_type"]
self.mower_id = self.mower["id"]
self.api_key = self.coordinator.data["api_key"]
self.mower_command = None
self.mower_timestamp = None
self.mower_local_timestamp = None
self.readable_mower_local_timestamp = None
self.error_code = None
self.error_code_timestamp = None
self.next_start_timestamp = None
self.attributes = None
self.payload = None

@property
def available(self):
Expand Down Expand Up @@ -107,20 +117,18 @@ def state(self):
)
if self.mower_attributes["mower"]["state"] == "IN_OPERATION":
return f"{self.mower_attributes['mower']['activity']}"
elif self.mower_attributes["mower"]["state"] in [
if self.mower_attributes["mower"]["state"] in [
"FATAL_ERROR",
"ERROR",
"ERROR_AT_POWER_UP",
]:
self.error_code = self.mower_attributes["mower"]["errorCode"]
return ERRORCODES.get(self.error_code)
elif self.mower_attributes["mower"]["state"] == "RESTRICTED":
if self.mower_attributes["mower"]["state"] == "RESTRICTED":
if self.mower_attributes["planner"]["restrictedReason"] == "NOT_APPLICABLE":
return "Parked until further notice"
else:
return f"{self.mower_attributes['planner']['restrictedReason']}"
else:
return f"{self.mower_attributes['mower']['state']}"
return f"{self.mower_attributes['planner']['restrictedReason']}"
return f"{self.mower_attributes['mower']['state']}"

@property
def icon(self):
Expand Down Expand Up @@ -155,9 +163,9 @@ def device_state_attributes(self):
]
== 0
):
self.attr_errorCodeTimestamp = "-"
self.error_code_timestamp = "-"
else:
self.attr_errorCodeTimestamp = time.strftime(
self.error_code_timestamp = time.strftime(
"%Y-%m-%d %H:%M:%S",
time.gmtime(
(
Expand All @@ -175,9 +183,9 @@ def device_state_attributes(self):
]
== 0
):
self.attr_nextStartTimestamp = "-"
self.next_start_timestamp = "-"
else:
self.attr_nextStartTimestamp = time.strftime(
self.next_start_timestamp = time.strftime(
"%Y-%m-%d %H:%M:%S",
time.gmtime(
(
Expand All @@ -194,8 +202,8 @@ def device_state_attributes(self):
"activity": self.mower_attributes["mower"]["activity"],
"state": self.mower_attributes["mower"]["state"],
"errorCode": self.mower_attributes["mower"]["errorCode"],
"errorCodeTimestamp": self.attr_errorCodeTimestamp,
"nextStartTimestamp": self.attr_nextStartTimestamp,
"errorCodeTimestamp": self.error_code_timestamp,
"nextStartTimestamp": self.next_start_timestamp,
"action": self.mower_attributes["planner"]["override"]["action"],
"restrictedReason": self.mower_attributes["planner"]["restrictedReason"],
"statusTimestamp": time.strftime(
Expand Down

0 comments on commit cea1913

Please sign in to comment.