diff --git a/custom_components/aqara_gateway/__init__.py b/custom_components/aqara_gateway/__init__.py index 6495bc5..dcc641e 100755 --- a/custom_components/aqara_gateway/__init__.py +++ b/custom_components/aqara_gateway/__init__.py @@ -45,7 +45,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): # migrate data (also after first setup) to options if entry.data: hass.config_entries.async_update_entry(entry, data={}, - options=entry.data) + options=entry.data) await _setup_logger(hass) @@ -194,7 +194,7 @@ def __init__(self, gateway: Gateway, device: dict, attr: str): self._unique_id = f"{self.device['mac']}_{self._attr}" self._name = (self.device['device_name'] + ' ' + - self._attr.replace('_', ' ').title()) + self._attr.replace('_', ' ').title()) self.entity_id = f"{DOMAIN}.{self._unique_id}" diff --git a/custom_components/aqara_gateway/binary_sensor.py b/custom_components/aqara_gateway/binary_sensor.py old mode 100644 new mode 100755 diff --git a/custom_components/aqara_gateway/config_flow.py b/custom_components/aqara_gateway/config_flow.py index 15a8479..8e47914 100755 --- a/custom_components/aqara_gateway/config_flow.py +++ b/custom_components/aqara_gateway/config_flow.py @@ -153,7 +153,7 @@ async def async_step_discovery_confirm(self, user_input=None): return self.async_show_form( step_id="discovery_confirm", description_placeholders={"name": self._name, - "device_info": self._device_info} + "device_info": self._device_info} ) async def async_step_zeroconf(self, discovery_info: DiscoveryInfoType): diff --git a/custom_components/aqara_gateway/core/const.py b/custom_components/aqara_gateway/core/const.py index e1ed3d5..2b87248 100755 --- a/custom_components/aqara_gateway/core/const.py +++ b/custom_components/aqara_gateway/core/const.py @@ -119,16 +119,18 @@ POWER = "power" VOLTAGE = "voltage" -DOMAINS = ['air_quality', - 'alarm_control_panel', - 'binary_sensor', - 'climate', - 'cover', - 'light', - 'remote', - 'select', - 'sensor', - 'switch'] +DOMAINS = [ + 'air_quality', + 'alarm_control_panel', + 'binary_sensor', + 'climate', + 'cover', + 'light', + 'remote', + 'select', + 'sensor', + 'switch' +] UNITS = { SensorDeviceClass.BATTERY: PERCENTAGE, diff --git a/custom_components/aqara_gateway/core/gateway.py b/custom_components/aqara_gateway/core/gateway.py index ffb3bba..a9c4545 100755 --- a/custom_components/aqara_gateway/core/gateway.py +++ b/custom_components/aqara_gateway/core/gateway.py @@ -9,6 +9,7 @@ from typing import Optional from random import randint from paho.mqtt.client import Client, MQTTMessage +from datetime import datetime from homeassistant.config_entries import ConfigEntry from homeassistant.core import Event, HomeAssistant @@ -57,7 +58,7 @@ def __init__(self, hass: HomeAssistant, host: str, config: dict, **options): self._debug = options.get('debug', '') # for fast access self.parent_scan_interval = (-1 if options.get('parent') is None - else options['parent']) + else options['parent']) self.default_devices = config['devices'] if config else None self.devices = {} @@ -165,7 +166,7 @@ async def async_run(self): if not self._mqtt_connect() or not self._prepare_gateway(): if self.host in self.hass.data[DOMAIN]["mqtt"]: self.hass.data[DOMAIN]["mqtt"].remove(self.host) - await asyncio.sleep(60) + await asyncio.sleep(10) continue self._mqttc.loop_start() @@ -199,36 +200,36 @@ def _prepare_gateway(self, get_devices: bool = False): device_name = Utils.get_device_name(self._model).lower() if "g2h pro" in device_name: shell = TelnetShellG2HPro(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "g2h" in device_name: shell = TelnetShellG2H(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "e1" in device_name: shell = TelnetShellE1(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "g3" in device_name: shell = TelnetShellG3(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m2 2022" in device_name: shell = TelnetShellM2POE(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m1s 2022" in device_name: shell = TelnetShellM1S22(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m3" in device_name: shell = TelnetShellM3(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) else: shell = TelnetShell(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) shell.login() - processes = shell.get_running_ps() + processes = shell.get_running_ps("mosquitto") public_mosquitto = shell.check_public_mosquitto() if not public_mosquitto: self.debug("mosquitto is not running as public!") - if "/data/bin/mosquitto -d" not in processes: - if "mosquitto" not in processes or not public_mosquitto: + if "mosquitto" not in processes or not public_mosquitto: + if "/data/bin/mosquitto -d" not in processes: shell.run_public_mosquitto() if get_devices: @@ -251,21 +252,27 @@ def _get_devices(self, shell): try: # 1. Read coordinator info value = {} - - zb_coordinator = shell.get_prop("sys.zb_coordinator") - model = shell.get_prop("persist.sys.model") + prop_raw = shell.get_prop("") + data = re.search(r"\[sys\.zb_coordinator\\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + zb_coordinator = data.group(1) if data else shell.get_prop("sys.zb_coordinator") + data = re.search(r"\[persist\.sys\.model\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + model = data.group(1) if data else shell.get_prop("persist.sys.model") if len(zb_coordinator) >= 1: raw = shell.read_file(zb_coordinator, with_newline=False) - did = shell.get_prop("persist.sys.did") - model = shell.get_prop("ro.sys.model") + data = re.search(r"\[persist\.sys\.did\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + did = data.group(1) if data else shell.get_prop("persist.sys.did") + data = re.search(r"\[persist\.sys\.model\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + model = data.group(1) if data else shell.get_prop("ro.sys.model") elif any(name in model for name in [ 'lumi.gateway', 'lumi.aircondition', 'lumi.camera.gwpagl01', 'lumi.camera.agl001', 'lumi.camera.acn003']): raw = shell.read_file( '/data/zigbee/coordinator.info', with_newline=False) - did = shell.get_prop("persist.sys.did") - model = shell.get_prop("ro.sys.model") + data = re.search(r"\[persist\.sys\.did\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + did = data.group(1) if data else shell.get_prop("persist.sys.did") + data = re.search(r"\[ro\.sys\.model\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + model = data.group(1) if data else shell.get_prop("ro.sys.model") else: raw = str(shell.read_file('/mnt/config/miio/device.conf')) if len(raw) <= 1: @@ -291,7 +298,8 @@ def _get_devices(self, shell): self._model = model # zigbee devices - zb_device = shell.get_prop("sys.zb_device") + data = re.search(r"\[sys\.zb_device\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + zb_device = data.group(1) if data else shell.get_prop("sys.zb_device") if len(zb_device) >= 1: raw = shell.read_file(zb_device, with_newline=False) else: @@ -429,25 +437,25 @@ def process_gateway_stats(self, payload: dict = None): device_name = Utils.get_device_name(self._model).lower() if "g2h pro" in device_name: shell = TelnetShellG2HPro(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "g2h" in device_name: shell = TelnetShellG2H(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "e1" in device_name: shell = TelnetShellE1(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "g3" in device_name: shell = TelnetShellG3(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m2 2022" in device_name: shell = TelnetShellM2POE(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m1s 2022" in device_name: shell = TelnetShellM1S22(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m3" in device_name: shell = TelnetShellM3(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) else: shell = TelnetShell(self.host, self.options.get(CONF_PASSWORD, '')) @@ -531,25 +539,25 @@ def _process_devices_info(self, prop, value): device_name = Utils.get_device_name(self._model).lower() if "g2h pro" in device_name: shell = TelnetShellG2HPro(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "g2h" in device_name: shell = TelnetShellG2H(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "e1" in device_name: shell = TelnetShellE1(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "g3" in device_name: shell = TelnetShellG3(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m2 2022" in device_name: shell = TelnetShellM2POE(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m1s 2022" in device_name: shell = TelnetShellM1S22(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) elif "m3" in device_name: shell = TelnetShellM3(self.host, - self.options.get(CONF_PASSWORD, '')) + self.options.get(CONF_PASSWORD, '')) else: shell = TelnetShell(self.host, self.options.get(CONF_PASSWORD, '')) @@ -895,9 +903,13 @@ def is_aqaragateway(host: str, if device_name and 'g2h pro' in device_name: shell = TelnetShellG2HPro(host, password) shell.login() - model = shell.get_prop("ro.sys.model") - name = shell.get_prop("ro.sys.name") - mac = shell.get_prop("persist.sys.miio_mac") + prop_raw = shell.get_prop("") + data = re.search(r"\[ro\.sys\.model\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + model = data.group(1) if data else shell.get_prop("ro.sys.model") + data = re.search(r"\[ro\.sys\.name\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + name = data.group(1) if data else shell.get_prop("ro.sys.name") + data = re.search(r"\[persist\.sys\.miio_mac\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + mac = data.group(1) if data else shell.get_prop("persist.sys.miio_mac") token = shell.get_token() elif device_name and 'g2h' in device_name: shell = TelnetShellG2H(host, password) @@ -924,9 +936,13 @@ def is_aqaragateway(host: str, else: shell = TelnetShell(host, password) shell.login() - model = shell.get_prop("persist.sys.model") - name = shell.get_prop("ro.sys.name") - mac = shell.get_prop("persist.sys.miio_mac") + prop_raw = shell.get_prop("") + data = re.search(r"\[persist\.sys\.model\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + model = data.group(1) if data else shell.get_prop("persist.sys.model") + data = re.search(r"\[ro\.sys\.name\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + name = data.group(1) if data else shell.get_prop("ro.sys.name") + data = re.search(r"\[persist\.sys\.miio_mac\]: \[([a-zA-Z0-9.-]+)\]", prop_raw) + mac = data.group(1) if data else shell.get_prop("persist.sys.miio_mac") token = shell.get_token() except (ConnectionError, EOFError, socket.error): diff --git a/custom_components/aqara_gateway/core/lock_data.py b/custom_components/aqara_gateway/core/lock_data.py index f33bf64..ca80f06 100755 --- a/custom_components/aqara_gateway/core/lock_data.py +++ b/custom_components/aqara_gateway/core/lock_data.py @@ -69,14 +69,14 @@ "someone detected": {"default": "Someone is lingering at the door"}, "li battery notify": {"default": "Li Battery notify", - "0": "Li Battery is abnormal", - "1": "Li Battery is normal"}, + "0": "Li Battery is abnormal", + "1": "Li Battery is normal"}, "battery notify": {"default": "Battery notify", - "0": "Battery is die", - "1": "Battery level is low", - "2": "Battery level is middle", - "3": "Battery level is full"}, + "0": "Battery is die", + "1": "Battery level is low", + "2": "Battery level is middle", + "3": "Battery level is full"}, "camera connected": {"default": "Camera is connected"}, "open in away mode": { "default": diff --git a/custom_components/aqara_gateway/core/shell.py b/custom_components/aqara_gateway/core/shell.py index 49248fc..858c819 100755 --- a/custom_components/aqara_gateway/core/shell.py +++ b/custom_components/aqara_gateway/core/shell.py @@ -8,7 +8,7 @@ WGET = "(wget http://master.dl.sourceforge.net/project/aqarahub/{0}?viasf=1 " \ - "-O /data/bin/{1} && chmod +x /data/bin/{1})" + "-O /data/bin/{1} && chmod +x /data/bin/{1})" CHECK_SOCAT = "(md5sum /data/socat | grep 92b77e1a93c4f4377b4b751a5390d979)" DOWNLOAD_SOCAT = "(wget -O /data/socat http://pkg.simple-ha.ru/mipsel/socat && chmod +x /data/socat)" @@ -41,7 +41,8 @@ def login(self): self.read_until(b"Password: ", timeout=1) self.write(self._password.encode() + b"\n") self.run_command("stty -echo") - self.read_until(b"/ # ", timeout=10) + self.write(b"\n") + self.read_until(b" # ", timeout=2) # self.run_command("export PS1='# '") @@ -50,8 +51,8 @@ def run_command(self, command: str, as_bytes=False) -> Union[str, bytes]: # pylint: disable=broad-except try: self.write(command.encode() + b"\n") - suffix = "\r\n{}".format(self._suffix) - raw = self.read_until(suffix.encode(), timeout=30) + suffix = "\n{}".format(self._suffix) + raw = self.read_until(suffix.encode(), timeout=15) except Exception: raw = b'' return raw if as_bytes else raw.decode() @@ -92,12 +93,16 @@ def run_public_mosquitto(self): def check_public_mosquitto(self) -> bool: """ get processes list """ raw = self.run_command("mosquitto") - if "Binding listener to interface" in raw: - return False - return True + if 'Binding listener to interface ""' in raw: + return True + if 'Binding listener to interface ' not in raw: + return True + return False - def get_running_ps(self) -> str: + def get_running_ps(self, ps=None) -> str: """ get processes list """ + if isinstance(ps, str): + return self.run_command(f"ps | grep {ps}") return self.run_command("ps") def redirect_ha_master2mqtt(self, pattern: str): diff --git a/custom_components/aqara_gateway/core/utils.py b/custom_components/aqara_gateway/core/utils.py index 4329550..9e8871c 100755 --- a/custom_components/aqara_gateway/core/utils.py +++ b/custom_components/aqara_gateway/core/utils.py @@ -9,6 +9,7 @@ from aiohttp import web from miio import Device, DeviceException +from homeassistant.components import persistent_notification from homeassistant.components.http import HomeAssistantView from homeassistant.helpers.device_registry import DeviceRegistry from homeassistant.helpers.typing import HomeAssistantType @@ -1908,8 +1909,8 @@ def gateway_alarm_mode_supported(model: str) -> Optional[bool]: def gateway_infrared_supported(model: str) -> Optional[bool]: """ return the gateway infrared supported """ if model in ('lumi.aircondition.acn05', 'lumi.gateway.iragl5', - 'lumi.gateway.iragl7', 'lumi.gateway.iragl01', - 'lumi.gateway.iragl8', 'lumi.gateway.agl001'): + 'lumi.gateway.iragl7', 'lumi.gateway.iragl01', + 'lumi.gateway.iragl8', 'lumi.gateway.agl001'): return True return False @@ -2000,7 +2001,7 @@ def __init__(self, hass: HomeAssistantType): self.url = "/{}".format(uuid.uuid4()) hass.http.register_view(self) - hass.components.persistent_notification.async_create( + persistent_notification.async_create( NOTIFY_TEXT % self.url, title=TITLE) def handle(self, rec: logging.LogRecord) -> None: diff --git a/custom_components/aqara_gateway/sensor.py b/custom_components/aqara_gateway/sensor.py index cad9163..37459ba 100755 --- a/custom_components/aqara_gateway/sensor.py +++ b/custom_components/aqara_gateway/sensor.py @@ -60,14 +60,6 @@ DEVICE_MAPPINGS, ) -GATEWAY_PLATFORMS = ["binary_sensor", - "sensor", - "switch", - "light", - "cover", - "lock"] -GATEWAY_PLATFORMS_NO_KEY = ["binary_sensor", "sensor"] - async def async_setup_entry(hass, entry, async_add_entities): """ setup config entry """