Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 29 additions & 29 deletions custom_components/dataplicity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import inspect
import logging
from threading import Thread

from homeassistant.config_entries import ConfigEntry
Expand All @@ -10,60 +11,59 @@
from . import utils

DOMAIN = "dataplicity"

_LOGGER = logging.getLogger(__name__)

async def async_setup(hass: HomeAssistant, hass_config: dict):
real_install = package.install_package

def fake_install(pkg: str, *args, **kwargs):
if pkg == "dataplicity==0.4.40":
if pkg == "dataplicity==0.5.13":
return utils.install_package(pkg, *args, **kwargs)
return real_install(pkg, *args, **kwargs)

try:
package.install_package = fake_install

# latest dataplicity has bug with redirect_port
await async_process_requirements(hass, DOMAIN, ["dataplicity==0.4.40"])
# 0.5.13 verified working; older versions had a redirect_port bug
await async_process_requirements(hass, DOMAIN, ["dataplicity==0.5.13"])

# fix Python 3.11 support
# fix Python 3.11+ support (getargspec removed in 3.11)
if not hasattr(inspect, "getargspec"):

def getargspec(*args):
spec = inspect.getfullargspec(*args)
return spec.args, spec.varargs, spec.varkw, spec.defaults

inspect.getargspec = getargspec

return True
except:
except Exception:
import traceback
_LOGGER.error("Dataplicity setup failed:\n%s", traceback.format_exc())
return False
finally:
package.install_package = real_install


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
# fix https://github.com/AlexxIT/Dataplicity/issues/29
Client = await hass.async_add_executor_job(utils.import_client)

hass.data[DOMAIN] = client = Client(
serial=entry.data["serial"], auth_token=entry.data["auth"]
)
# replace default 80 port to Hass port (usual 8123)
client.port_forward.add_service("web", hass.config.api.port)
Thread(name=DOMAIN, target=client.run_forever).start()

async def hass_stop(event):
client.exit()

hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, hass_stop)

await utils.fix_middleware(hass)

return True

try:
Client = await hass.async_add_executor_job(utils.import_client)
hass.data[DOMAIN] = client = Client(
serial=entry.data["serial"], auth_token=entry.data["auth"]
)
# replace default 80 port to Hass port (usual 8123)
client.port_forward.add_service("web", hass.config.api.port)
Thread(name=DOMAIN, target=client.run_forever).start()
async def hass_stop(event):
client.exit()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, hass_stop)
await utils.fix_middleware(hass)
return True
except Exception:
import traceback
_LOGGER.error("async_setup_entry failed:\n%s", traceback.format_exc())
return False

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
client = hass.data[DOMAIN]
client.exit()
client = hass.data.pop(DOMAIN, None)
if client is not None:
client.exit()
return True
48 changes: 39 additions & 9 deletions custom_components/dataplicity/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@

_LOGGER = logging.getLogger(__name__)

RE_TOKEN = re.compile(r"https://www\.dataplicity\.com/([a-z0-9-]+)\.py")
RE_INSTALL_URL = re.compile(
r"https?://(?:www\.)?dataplicity\.com/([A-Za-z0-9_\-]+)\.(?:py|sh)"
)

RE_TOKEN_CHARS = re.compile(r"^[A-Za-z0-9_\-]+$")

RE_RECOVERY = re.compile(r"^([A-Za-z0-9_\-]{8,}):(.+)$")


class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
Expand All @@ -32,21 +38,45 @@ async def async_step_user(self, data=None, error=None):
errors={"base": error} if error else None,
)

m = RE_TOKEN.search(data["token"])
token = m[1] if m else data["token"]
# fix new format `https://www.dataplicity.com/3-********.py`
token = re.sub(r"^\d-", "", token)
input_str = data["token"].strip()

if not token.isalnum():
return await self.async_step_user(error="token")
if not input_str.lower().startswith("http"):
m_rec = RE_RECOVERY.match(input_str)
if m_rec:
serial, auth = m_rec.group(1), m_rec.group(2).strip()
return self.async_create_entry(
title="Dataplicity",
data={"auth": auth, "serial": serial},
description_placeholders={"device_url": ""},
)

m = RE_INSTALL_URL.search(input_str)
token = m.group(1) if m else input_str
token = re.sub(r"^\d-", "", token)

if not token.isalnum():
if not RE_TOKEN_CHARS.match(token):
return await self.async_step_user(error="token")
else:
m = RE_INSTALL_URL.search(input_str)
token = m.group(1) if m else input_str
token = re.sub(r"^\d-", "", token)

if not RE_TOKEN_CHARS.match(token):
return await self.async_step_user(error="token")

session = async_get_clientsession(self.hass)
resp = await utils.register_device(session, token)

device_class_hash = await utils.fetch_device_class_hash(session, token)
if device_class_hash is None:
return await self.async_step_user(error="token")

resp = await utils.register_device(session, token, device_class_hash)
if resp:
return self.async_create_entry(
title="Dataplicity",
data={"auth": resp["auth"], "serial": resp["serial"]},
description_placeholders={"device_url": resp["device_url"]},
description_placeholders={"device_url": resp.get("device_url", "")},
)

return await self.async_step_user(error="auth")
4 changes: 2 additions & 2 deletions custom_components/dataplicity/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"iot_class": "cloud_push",
"issue_tracker": "https://github.com/AlexxIT/Dataplicity/issues",
"requirements": [],
"version": "1.2.2"
}
"version": "1.3.0"
}
4 changes: 2 additions & 2 deletions custom_components/dataplicity/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
"step": {
"user": {
"title": "Register Dataplicity Device",
"description": "Sign up to [Dataplicity](https://www.dataplicity.com/) and paste full install line or URL or Token:\n`https://www.dataplicity.com/XXXXXXXX.py`",
"description": "Sign up to [Dataplicity](https://www.dataplicity.com/) and paste the install URL from Add device:\n`https://dataplicity.com/XXXXXXXX.sh`\n\nOr, to reuse already-provisioned credentials, paste them as `serial:auth`.",
"data": {
"token": "URL or Token"
"token": "Install URL or serial:auth"
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions custom_components/dataplicity/translations/ru.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
{
"config": {
"create_entry": {
"default": "Home Assistant добавлен в сервис Dataplicity. Включите **Wormhole** в [настройках устройства]({device_url}) для публичного HTTPS доступа."
"default": "Home Assistant добавлен в Dataplicity. Включите **Wormhole** в [настройках устройства]({device_url}) для публичного HTTPS доступа."
},
"abort": {
"win32": "Windows не поддерживается",
"ssl": "[SSL конфигурация](https://www.home-assistant.io/integrations/http/) не поддерживается"
"ssl": "[SSL конфиг](https://www.home-assistant.io/integrations/http/) не поддерживается"
},
"error": {
"auth": "Ошибка в процессе регистрации устройства",
"token": "Неправильная ссылка или токен"
"auth": "Ошибка при регистрации устройства",
"token": "Неверный URL или токен"
},
"step": {
"user": {
"title": "Регистрация устройства Dataplicity",
"description": "Зарегистрируйтесь в сервисе [Dataplicity](https://www.dataplicity.com/) и вставьте полную строку установки или ссылку или токен:\n`https://www.dataplicity.com/XXXXXXXX.py`",
"description": "Зарегистрируйтесь в сервисе [Dataplicity](https://www.dataplicity.com/) и вставьте ссылку установки из кнопки Add device:\n`https://dataplicity.com/XXXXXXXX.sh`\n\nИли, чтобы использовать уже полученные учётные данные, вставьте их в формате `serial:auth`.",
"data": {
"token": "Ссылка или токен"
"token": "Ссылка установки или serial:auth"
}
}
}
}
}
}
Loading