diff --git a/custom_components/airbnk_mqtt/__init__.py b/custom_components/airbnk_mqtt/__init__.py index 79eab22..f4aade9 100644 --- a/custom_components/airbnk_mqtt/__init__.py +++ b/custom_components/airbnk_mqtt/__init__.py @@ -101,9 +101,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): lock_devices = {} for dev_id, dev_config in device_configs.items(): if dev_config[CONF_DEVICE_MQTT_TYPE] == CONF_CUSTOM_MQTT: - lock_devices[dev_id] = CustomMqttLockDevice( - hass, dev_config, entry.options - ) + lock_devices[dev_id] = CustomMqttLockDevice(hass, dev_config, entry.options) else: lock_devices[dev_id] = TasmotaMqttLockDevice( hass, dev_config, entry.options diff --git a/custom_components/airbnk_mqtt/airbnk_api.py b/custom_components/airbnk_mqtt/airbnk_api.py index e64cba0..611b31d 100644 --- a/custom_components/airbnk_mqtt/airbnk_api.py +++ b/custom_components/airbnk_mqtt/airbnk_api.py @@ -119,7 +119,7 @@ async def getCloudDevices(hass, userId, token): res = {} deviceConfigs = {} for dev_data in json_data["data"] or []: - if not dev_data["boardModel"].isnumeric(): + if dev_data["deviceType"][0] in ["W", "F"]: _LOGGER.info("Device '%s' is filtered out", dev_data["deviceName"]) else: res[dev_data["sn"]] = dev_data["deviceName"] diff --git a/custom_components/airbnk_mqtt/custom_device.py b/custom_components/airbnk_mqtt/custom_device.py index 6646396..bf65088 100644 --- a/custom_components/airbnk_mqtt/custom_device.py +++ b/custom_components/airbnk_mqtt/custom_device.py @@ -3,7 +3,6 @@ import logging import time from typing import Callable -from textwrap import wrap from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.components import mqtt @@ -68,7 +67,10 @@ class CustomMqttLockDevice: cmd = {} cmdSent = False last_advert_time = 0 + last_telemetry_time = 0 is_available = False + retries_num = DEFAULT_RETRIES_NUM + curr_try = 0 def __init__(self, hass: HomeAssistant, device_config, entry_options): _LOGGER.debug("Setting up CustomMqttLockDevice for sn %s", device_config["sn"]) @@ -76,7 +78,6 @@ def __init__(self, hass: HomeAssistant, device_config, entry_options): self._callbacks = set() self._lockConfig = device_config self._codes_generator = AirbnkCodesGenerator() - mac_addr = self._lockConfig[CONF_MAC_ADDRESS] self._lockData = self._codes_generator.decryptKeys( device_config["newSninfo"], device_config["appKey"] ) @@ -102,9 +103,10 @@ def device_info(self): def check_availability(self): curr_time = int(round(time.time())) - deltatime = curr_time - self.last_advert_time - # _LOGGER.debug("Last reply was %s secs ago", deltatime) - if deltatime >= MAX_NORECEIVE_TIME: + deltatime1 = curr_time - self.last_advert_time + deltatime2 = curr_time - self.last_telemetry_time + # _LOGGER.debug("Last reply was %s - %s secs ago", deltatime1, deltatime2) + if min(deltatime1, deltatime2) >= MAX_NORECEIVE_TIME: self.is_available = False @property @@ -173,6 +175,8 @@ def register_callback(self, callback: Callable[[], None]) -> None: def parse_telemetry_message(self, msg): # TODO _LOGGER.debug("Received telemetry %s", msg) + self.last_telemetry_time = int(round(time.time())) + self.is_available = True def parse_adv_message(self, msg): _LOGGER.debug("Received adv %s", msg) @@ -210,20 +214,34 @@ def parse_operation_message(self, msg): msg_state = payload["success"] if msg_state is False: - _LOGGER.error("Failed sending command: returned %s", msg_state) - self.curr_state = LOCK_STATE_FAILED - raise Exception("Failed sending command: returned %s", msg_state) + if self.curr_try < self.retries_num: + self.curr_try += 1 + time.sleep(0.5) + _LOGGER.debug("Retrying: attempt %i", self.curr_try) + self.curr_state = LOCK_STATE_OPERATING + for callback_func in self._callbacks: + callback_func() + self.send_mqtt_command() + else: + _LOGGER.error("No more retries: command FAILED") + self.curr_state = LOCK_STATE_FAILED + for callback_func in self._callbacks: + callback_func() + raise Exception("Failed sending command: returned %s", msg_state) return msg_sign = payload["sign"] if msg_sign == self.cmd["sign"]: self.cmdSent = True + self.parse_new_lockStatus(payload["lockStatus"]) + for callback_func in self._callbacks: callback_func() async def operateLock(self, lock_dir): _LOGGER.debug("operateLock called (%s)", lock_dir) + self.curr_try = 0 self.cmdSent = False self.curr_state = LOCK_STATE_OPERATING for callback_func in self._callbacks: @@ -234,12 +252,27 @@ async def operateLock(self, lock_dir): self.cmd["command1"] = "FF00" + opCode[0:36].decode("utf-8") self.cmd["command2"] = "FF01" + opCode[36:].decode("utf-8") self.cmd["sign"] = self._codes_generator.systemTime + self.send_mqtt_command() + + def send_mqtt_command(self): mqtt.publish( self.hass, BLEOpTopic % self._lockConfig[CONF_MQTT_TOPIC], json.dumps(self.cmd), ) + def parse_new_lockStatus(self, lockStatus): + _LOGGER.debug("Parsing new lockStatus: %s", lockStatus) + bArr = bytearray.fromhex(lockStatus) + if bArr[0] != 0xAA or bArr[3] != 0x02 or bArr[4] != 0x04: + _LOGGER.error("Wrong lockStatus msg: %s", lockStatus) + return + + lockEvents = (bArr[10] << 24) | (bArr[11] << 16) | (bArr[12] << 8) | bArr[13] + self.lockEvents = lockEvents + self.voltage = ((float)((bArr[14] << 8) | bArr[15])) * 0.01 + self.curr_state = (bArr[16] >> 4) & 3 + def parse_MQTT_advert(self, mqtt_advert): _LOGGER.debug("Parsing advert msg: %s", mqtt_advert) bArr = bytearray.fromhex(mqtt_advert) diff --git a/custom_components/airbnk_mqtt/tasmota_device.py b/custom_components/airbnk_mqtt/tasmota_device.py index 7406518..8020b70 100644 --- a/custom_components/airbnk_mqtt/tasmota_device.py +++ b/custom_components/airbnk_mqtt/tasmota_device.py @@ -259,9 +259,6 @@ async def async_parse_MQTT_message(self, msg): msg_state = payload[msg_type]["state"] if "FAIL" in msg_state: _LOGGER.error("Failed sending frame: returned %s", msg_state) - self.curr_state = LOCK_STATE_FAILED - for callback_func in self._callbacks: - callback_func() if self.curr_try < self.retries_num: self.curr_try += 1 @@ -276,6 +273,9 @@ async def async_parse_MQTT_message(self, msg): await self.async_sendFrame1() else: _LOGGER.error("No more retries: command FAILED") + self.curr_state = LOCK_STATE_FAILED + for callback_func in self._callbacks: + callback_func() raise Exception("Failed sending frame: returned %s", msg_state) return