Skip to content

Commit

Permalink
Merge pull request #4 from Jezza34000/dev
Browse files Browse the repository at this point in the history
improve token management
  • Loading branch information
Jezza34000 authored Nov 25, 2022
2 parents d7e9746 + d4b829f commit 3b1adb0
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 48 deletions.
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![](https://img.shields.io/github/release/Jezza34000/homeassistant_weback_component/all.svg?style=for-the-badge)](https://github.com/Jezza34000/homeassistant_weback_component)
[![hacs_badge](https://img.shields.io/badge/HACS-Custom-41BDF5.svg?style=for-the-badge)](https://github.com/hacs/integration)

Home Assistant component for controling robot from brand like : Neatsvor / Tesvor / Orfeld / Abir...
Home Assistant component for controlling robot from brand like : Neatsvor / Tesvor / Orfeld / Abir...
Who using WeBack or Tesvor apps.

## Installation
Expand Down Expand Up @@ -36,9 +36,20 @@ weback_vacuum:
**application** : if you use "WeBack" do not try to change this field. \
**client_id**, **api_version**, **language**: seems to have no effect. Do not use it.
> **Warning** : Some user are expericing problem with the use of sharing option in WeBack's app. I recommand to not use this option and remove shared account to ensure a proper working
Config example :
``` YAML
weback_vacuum:
username: [email protected]
password: mysupersecuredpassword
region: 33
```
> Do not use any leading/ending characters like < > " ' +
Once configuration set you can restart Home Assistant.
After restart, a new vacuum entity is created with the name defined into WeBack apps.
Once set you can restart Home Assistant.
## Important : API change since 2022
Expand All @@ -52,6 +63,17 @@ Go to WeBack app :
* Add your robot to your new account
## Issues
If you find any bug or you're experiencing any problem, please set your Home Assistant log level to debug before opening any issues. And provide full log.
To set your HA into debug level copy this into your `configuration.yaml` :

``` YAML
logger:
default: error
logs:
custom_components.weback_vacuum: debug
```



35 changes: 24 additions & 11 deletions custom_components/weback_vacuum/VacDevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@


class VacDevice(WebackWssCtrl):
def __init__(self, thing_name, thing_nickname, sub_type, thing_status, wss_url, region_name, jwt_token):
def __init__(self, thing_name, thing_nickname, sub_type, thing_status,
user, password, region, country, app, client_id, api_version):
_LOGGER.debug("WebackApi RobotController __init__")
super().__init__(wss_url, region_name, jwt_token)
super().__init__(user, password, region, country, app, client_id, api_version)
self.name = thing_name
self.nickname = thing_nickname
self.sub_type = sub_type
Expand All @@ -33,9 +34,11 @@ async def watch_state(self):
# -> Properties

@property
def current_mode(self) -> str:
def current_mode(self):
""" Raw working_status field string """
return self.robot_status['working_status']
if 'working_status' in self.robot_status:
return self.robot_status['working_status']
return self.IDLE_MODE

@property
def raw_status(self) -> str:
Expand All @@ -52,7 +55,9 @@ def is_cleaning(self) -> bool:
@property
def is_available(self):
""" Boolean define if robot is connected to cloud """
return self.robot_status['connected'] == 'true'
if 'connected' in self.robot_status:
return self.robot_status['connected'] == 'true'
return False

@property
def is_charging(self):
Expand All @@ -62,22 +67,28 @@ def is_charging(self):
@property
def error_info(self):
""" Raw error_info field string """
return self.robot_status["error_info"]
if 'error_info' in self.robot_status:
return self.robot_status['error_info']
return None

@property
def battery_level(self):
""" Raw battery_level field integer """
return int(self.robot_status["battery_level"])
if 'battery_level' in self.robot_status:
return int(self.robot_status['battery_level'])
return 0

@property
def fan_status(self):
""" Raw fan_status field string """
return self.robot_status["fan_status"]
if 'fan_status' in self.robot_status:
return self.robot_status['fan_status']

@property
def mop_status(self):
""" Raw fan_status field string """
return self.robot_status["water_level"]
if 'water_level' in self.robot_status:
return self.robot_status['water_level']

@property
def fan_speed_list(self):
Expand All @@ -92,12 +103,14 @@ def mop_level_list(self):
@property
def clean_time(self):
"""Return clean time"""
return self.robot_status["clean_time"]
if 'clean_time' in self.robot_status:
return self.robot_status['clean_time']

@property
def clean_area(self):
"""Return clean area in square meter"""
return self.robot_status["clean_area"]
if 'clean_area' in self.robot_status:
return self.robot_status['clean_area']

@property
def vacuum_or_mop(self) -> int:
Expand Down
70 changes: 47 additions & 23 deletions custom_components/weback_vacuum/WebackApi.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,26 +156,26 @@ def save_token_file(self):
try:
config = configparser.ConfigParser()
config.add_section('weback_token')
config.set('weback_token', 'jwt_token', self.jwt_token)
config.set('weback_token', 'token_exp', self.token_exp)
config.set('weback_token', 'api_url', self.api_url)
config.set('weback_token', 'wss_url', self.wss_url)
config.set('weback_token', 'region_name', self.region_name)
config.set('weback_token', 'jwt_token', str(self.jwt_token))
config.set('weback_token', 'token_exp', str(self.token_exp))
config.set('weback_token', 'api_url', str(self.api_url))
config.set('weback_token', 'wss_url', str(self.wss_url))
config.set('weback_token', 'region_name', str(self.region_name))
with open('weback_creds', 'w') as configfile:
config.write(configfile)
_LOGGER.debug(f"WebackApi saved new creds")
except:
_LOGGER.debug(f"WebackApi failed to saved new creds")
except Exception as e:
_LOGGER.debug(f"WebackApi failed to saved new creds details={e}")

@staticmethod
def check_token_is_valid(token: str) -> bool:
def check_token_is_valid(token) -> bool:
"""
Check if token validity is still OK or not
"""
_LOGGER.debug(f"WebackApi checking token validity : {token}")
try:
now_date = datetime.today()
dt_token = datetime.strptime(token, "%Y-%d-%m %H:%M:%S.%f")
now_date = datetime.today() - timedelta(minutes=15)
dt_token = datetime.strptime(str(token), "%Y-%m-%d %H:%M:%S.%f")
if now_date < dt_token:
_LOGGER.debug(f"WebackApi token is valid")
return True
Expand Down Expand Up @@ -239,7 +239,7 @@ async def send_http(url, **params):
# _LOGGER.debug(f"WebackVacuumApi (WSS) null_callback: {message}")


class WebackWssCtrl:
class WebackWssCtrl(WebackApi):

# Clean mode
CLEAN_MODE_AUTO = 'AutoClean'
Expand Down Expand Up @@ -267,7 +267,7 @@ class WebackWssCtrl:

# Idle state
IDLE_MODE_HIBERNATING = 'Hibernating'
IDLE_MODE = "Idle"
IDLE_MODE = 'Idle'

# Standby/Paused state
CLEAN_MODE_STOP = 'Standby'
Expand Down Expand Up @@ -302,7 +302,10 @@ class WebackWssCtrl:
MOP_ON = 2

# Error state
ROBOT_ERROR = "Malfunction"
ROBOT_ERROR = 'Malfunction'

# Unknow state
ROBOT_UNKNOWN = 'unknown'

# Robot Error codes
ROBOT_ERROR_NO = "NoError"
Expand Down Expand Up @@ -379,25 +382,45 @@ class WebackWssCtrl:
WebSocket Weback API controller
Handle websocket to send/receive robot control
"""
def __init__(self, wss_url, region_name, jwt_token):
def __init__(self, user, password, region, country, app, client_id, api_version):
super().__init__(user, password, region, country, app, client_id, api_version)
_LOGGER.debug("WebackApi WSS Control __init__")
self.ws = None
self.authorization = "Basic KG51bGwpOihudWxsKQ=="
self.socket_state = SOCK_CLOSE
self.jwt_token = jwt_token
self.region_name = region_name
self.wss_url = wss_url
self.robot_status = None
self.subscriber = []
self.wst = None
self.ws = None
self._refresh_time = 60
self.sent_counter = 0

# Reloading cached creds
self.verify_cached_creds()

async def check_credentials(self):
"""
Check if credentials for WSS link are OK
"""
_LOGGER.debug(f"WebackApi (WSS) Checking credentials...")
if not self.region_name or not self.jwt_token or not self.check_token_is_valid(self.token_exp):
_LOGGER.debug(f"WebackApi (WSS) Credentials need renewal")
# Cred renewal necessary
if await self.login():
return True
else:
return False
_LOGGER.debug(f"WebackApi (WSS) Credentials are OK")
return True

async def open_wss_thread(self):
"""
Connect WebSocket to Weback Server and create a thread to maintain connexion alive
"""
if not await self.check_credentials():
_LOGGER.error(f"WebackApi (WSS) Failed to obtain WSS credentials")
return False

_LOGGER.debug(f"WebackApi (WSS) Addr={self.wss_url} / Region={self.region_name} / Token={self.jwt_token}")

try:
Expand Down Expand Up @@ -569,12 +592,13 @@ async def update_status(self, thing_name, sub_type):
def adapt_refresh_time(self, status):
"""Adapt refreshing time depending on robot status"""
_LOGGER.debug(f"WebackApi (WSS) adapt for : {status}")
if status['working_status'] in self.DOCKED_STATES:
_LOGGER.debug("WebackApi (WSS) > Set refreshing to 120s")
self._refresh_time = 120
else:
_LOGGER.debug("WebackApi (WSS) > Set refreshing to 5s")
self._refresh_time = 5
if 'working_status' in status:
if status['working_status'] not in self.DOCKED_STATES:
_LOGGER.debug("WebackApi (WSS) > Set refreshing to 5s")
self._refresh_time = 5
return
_LOGGER.debug("WebackApi (WSS) > Set refreshing to 120s")
self._refresh_time = 120

async def refresh_handler(self, thing_name, sub_type):
_LOGGER.debug("WebackApi (WSS) Start refresh_handler")
Expand Down
11 changes: 8 additions & 3 deletions custom_components/weback_vacuum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,14 @@ async def async_setup(hass, config):
robot["thing_nickname"],
robot["sub_type"],
robot["thing_status"],
weback_api.wss_url,
weback_api.region_name,
weback_api.jwt_token)
config[DOMAIN].get(CONF_USERNAME),
config[DOMAIN].get(CONF_PASSWORD),
config[DOMAIN].get(CONF_REGION),
config[DOMAIN].get(CONF_LANGUAGE),
config[DOMAIN].get(CONF_APP),
config[DOMAIN].get(CONF_CLIENT_ID),
config[DOMAIN].get(CONF_API_VERSION),
)
hass.data[DOMAIN].append(vacuum_device)

if hass.data[DOMAIN]:
Expand Down
11 changes: 3 additions & 8 deletions custom_components/weback_vacuum/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def __init__(self, device: VacDevice):
self.device.subscribe(lambda vacdevice: self.schedule_update_ha_state(False))
self._error = None

BASE_FEATURES = (
self._attr_supported_features = (
VacuumEntityFeature.TURN_ON
| VacuumEntityFeature.TURN_OFF
| VacuumEntityFeature.STATUS
Expand All @@ -84,13 +84,8 @@ def __init__(self, device: VacDevice):
| VacuumEntityFeature.LOCATE
| VacuumEntityFeature.START
| VacuumEntityFeature.SEND_COMMAND
| VacuumEntityFeature.FAN_SPEED
)

if self.device.vacuum_or_mop != 0:
_LOGGER.debug(f"Add fan_speed features for this robot")
supported_features = BASE_FEATURES | VacuumEntityFeature.FAN_SPEED

self._attr_supported_features = supported_features
_LOGGER.info(f"Vacuum initialized: {self.name}")

@property
Expand All @@ -109,7 +104,7 @@ def name(self):

@property
def available(self):
_LOGGER.debug("Vacuum: available", self.device.is_available)
_LOGGER.debug(f"Vacuum: available={self.device.is_available}")
"""Returns true if vacuum is online"""
return self.device.is_available

Expand Down

0 comments on commit 3b1adb0

Please sign in to comment.