Xây dựng tính năng Remote Control của HASS dựa trên mã nguồn SniTun chính chủ

I. Giới thiệu chung

Home Assistant có lẽ là một cái tên quá quen thuộc đối với cộng đồng nghiên cứu và thích nghịch về nhà thông minh. Giới thiệu một cách ngắn gọn thì đây là dự án xây dựng một server điều khiển nhà thông minh đặt tại chính nhà mình, có khả năng điều khiển được nhiều loại thiết bị từ nhiều hãng khác nhau cũng như nhiều chuẩn kết nối từ Zigbee cho đến Wifi.

Home Assistant gọi tắt là Hass, là dự án mã nguồn mở nhà tự động (automation home) đặt quyền kiểm soát cục bộ và quyền riêng tư lên hàng đầu. Được hỗ trợ bởi cộng đồng những người đam mê tự làm và mày mò trên toàn thế giới. Hoàn hảo để chạy trên Raspberry Pi hoặc máy chủ cục bộ.

Trang chủ: https://www.home-assistant.io/

Hass là dự án mã nguồn mở và hoàn toàn miễn phí. Để duy trì dự án này thì có thêm dịch vụ cloud tích hợp gọi là Nabucasa. Nabucasa cho phép điều khiển từ xa Hass qua internet, cũng như tích hợp Alexa, Google Assistant,… Đây là dịch vụ thu phí với mức phí là 5$/1 tháng, một mức phí hợp lý và chi phí này được dùng để phát triển và bảo trì cho mã nguồn Hass. Tuy nhiên, đôi khi chúng ta lại muốn tự xây dựng giải pháp điều khiển từ xa (remote control) để đảm bảo tính riêng tư của bản thân. Đối với việc điều khiển từ xa thì việc mở port là thứ được ưa chuộng nhất. Tuy nhiên, đôi khi việc mở port lại không phải lúc nào cũng dễ dàng thực hiện hoặc thực hiện được. Chính vì vậy, mình đã nảy sinh ý tưởng là dựa vào mã nguồn của SniTun (là thành phần cốt yếu của Nabucasa) để có thể điều khiển từ xa, liệu bản thân mình có thể xây dựng được một cơ chế điều khiển từ xa như Nabucasa làm hay không.

Thật may mắn, hơn 2 tuần nghiên cứu và mày mò, mình đã xây dựng thành công. Tuy nhiên lại gặp nhiều vấn đề trong sự đón nhận từ cộng đồng, như là mình đá bát cơm của Nabucasa thì làm sao có tiền để phát triển Hass hoặc là việc làm của mình nó rườm rà và phức tạp, tuy nhiên mục tiêu hướng đến của mình là thử xem mình có thể phát triển được hay không. Nên tại thời điểm đó (tháng 12/2020) thì sau khi công khai thành quả của mình thì mình không công khai mã nguồn để tránh bị lạm dụng. Tuy nhiên, nghĩ lại việc công khai mã nguồn này cũng giúp các bạn hiểu rõ hơn cơ chế của remote control cũng như ai có nhu cầu tự xây dựng có thể tham khảo. Đồng thời, chi phí để xây dựng và duy trì một hệ thống như thế này cũng vượt qua chi phí 5$/1 tháng nên điều này mang tính chất nghiên cứu khoa học là chính. Mình cũng hy vọng mọi người có thể đóng góp, góp ý để mình hoàn thiện hơn.

II. Thực hiện

1. Các thành phần cần có

  • 1 máy tính có cài docker và có kết nối internet.
  • Tài khoản AWS.
  • Tài khoản CloudFlare
  • 1 VPS
  • 1 domain

2. Các bước tiến hành

2.1. Đăng ký và cấu hình Amazon Cognito

Khi mình đọc thử một vài đoạn code của phần Hass Cloud (Nabucasa) thì thấy có sử dụng Cognito để quản lý người dùng nên mình dùng luôn Cognito trong phần cloud mình tự xây dựng. Mục đích chính là để hạn chế thấp nhất việc phải sửa lại code của Hass.

Amazon Cognito là dịch vụ của Amazon Web Services cung cấp xác thực, ủy quyền và quản lý người dùng cho các ứng dụng web và di động của bạn. Người dùng có thể đăng nhập trực tiếp bằng tên người dùng và mật khẩu hoặc thông qua bên thứ ba như Facebook, Amazon, Google hoặc Apple.

Hai thành phần chính của Amazon Cognito là User pools and Identity pools:

  • User pools là các thư mục người dùng cung cấp tùy chọn đăng ký và đăng nhập cho người dùng ứng dụng web và thiết bị di động của bạn.
  • Identity pools cung cấp thông tin xác thực AWS để cấp cho người dùng của bạn quyền truy cập vào các dịch vụ AWS khác.
Link: https://console.aws.amazon.com/cognito/home?region=us-east-1#

Sau đó chọn Manage User Pools. Được kết quả như sau:

Sau đó chọn Create a user pool. Được kết quả như sau:

Điền tên tùy thích vào phần Pool name, sau đó chọn đến Step through settings, rồi chọn như sau:

Sau đó chọn Next step, được như sau:

Sau đó mọi người chọn Message customizations, ở trường Verify type thì chọn Link:

Sau đó chọn Save changes, được như sau:

Và chọn Create pool, kết quả sẽ như thế này.

Mọi người ghi chú lại phần Pool id để lúc sau dùng.

Mọi người chọn App clients, được giao diện như sau:

Sau đó chọn Add an app client

Mọi người điền tên tùy ý vào, nhớ bỏ dấu tích chọn tại Generate client secret, sau đó chọn Create app client, được kết quả như sau:

Ghi chú lại App client ID.

Như vậy có 2 nội dung chúng ta cần ghi chú là Pool ID và App Client ID.

Mọi người có thể tham khảo thêm bài viết này: https://dev.classmethod.jp/articles/co-ban-ve-amazon-cognito/

2.2. Quản lý domain thông qua Cloudflare

Lý do mình quản lý domain thông quan Cloudflare là để sau khi đăng nhập trong phần cloud của Hass sẽ có domain như thế này, như trong bài của mình là https://smarthome3.phuctu97.tech (đừng thử truy cập, cái này cũ rồi) thì mọi người sẽ thấy có phần subdomain là smarthome3 và cái này có https cho subdomain đó. Để thuận tiện cho việc cấp chứng chỉ SSL cho subdomain cũng như quản lý thì việc sử dụng API của Cloudflare là khá tối ưu.

No photo description available.

Mọi người đăng ký tài khoản tại Cloudflare, sau đó truy cập vào trang dashboard, chọn Add a Site để thêm domain vào cho Cloudflare quản lý.

Sau đó mọi người nhập domain mình cần quản lý vào, cụ thể ở đây là 191lab.tech

Sau khi nhấn Add site sẽ chuyển sang trang sau

Mọi người chọn vào cái Free $0 và chọn Continue, sẽ được chuyển sang trang này

Đợi một lúc sẽ như thế này

Kéo xuống dưới và chọn Continue, được như sau

Tiếp theo chúng ta vào phần quản lý domain của mình, cụ thể là domain 191lab.tech của mình. Domain này mình mua bên Hostinger. Sau đó cấu hình phần nameserver theo như yêu cầu của Cloudflare. Vẫn giữ trang Cloudflare như thế này, mở tab mới vào trang quản lý domain.

Ban đầu của mình như thế này:

Sau đó mình đổi lại như sau:

Tùy vào từng dịch vụ tên miền khác nhau mà giao diện sẽ khác nhau, nhưng về bản chất đều phải đổi nameserver như trên. Sau khi cập nhật nameserver mới thì mình tiến hành qua lại bên Cloudflare, và chọn Done, check nameservers. Sẽ được như sau:

Mọi người chọn Get started để tiến hành cấu hình một số cái, sau đó bấm Save.

Cuối cùng đươc như thế này:

Chọn Finish

Mọi người chọn Check nameserver và được kết quả như sau:

Bây giờ chúng ta không làm gì nữa cả và để đó cho domain cập nhật nameserver có hiệu lực. Chuyển xuống bước tiếp theo, xây dựng server.

Mọi người lưu phần Zone ID lại nhé

Sau khi có email thông báo của Cloudflare mình lại quay lại làm tiếp. Trong quá trình đợi email thông báo từ Cloudflare, bạn có thể làm bước 2.3 ở dưới.

Bây giờ chúng ta sẽ tiến hành tạo API Key cho cho Cloudflare.

Các bạn vào https://dash.cloudflare.com/profile/api-tokens

Sau đó chọn Create Token

Chọn Use template của Edit zone DNS

Mọi người sửa tên Token tại chỗ Token name để tiện quản lý

Ở phần Zone Resources mọi người chọn chính xác domain mình quản lý. Kết quả cuối sẽ như thế này

Sau đó mọi người chọn Continue và summary

Chọn tiếp Create Token

Kết quả cuối sẽ như thế này, mọi người lưu lại API Token này, lát sau sẽ sử dụng

2.3. Tiến hành chạy SniTun Server

Link tham khảo: https://github.com/NabuCasa/snitun

Link code: https://github.com/phuctu1901/hass_remote_control_snitun

Để khởi chạy SniTun Server ta sẽ cần một VPS đang hoạt động trên internet và có Python.

"""Test peer manager."""
import asyncio
import os
import logging
import sys
import json

from datetime import datetime, timedelta

from unittest.mock import patch
from snitun.exceptions import SniTunInvalidPeer
from snitun.server.peer import Peer
from snitun.server.peer_manager import PeerManager
from cryptography.fernet import Fernet, MultiFernet

from aiohttp import web

from snitun.server.listener_sni import SNIProxy

log_file = str(datetime.utcnow().strftime('%m_%d_%Y')) + '.log'
logging.basicConfig(filename=log_file, format='%(levelname)s | %(asctime)s | %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.DEBUG)
# logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
# log_file = str(datetime.utcnow().strftime('%m_%d_%Y')) + '.log'
_LOGGER = logging.getLogger(__name__)

from snitun.server.run import SniTunServer, SniTunServerSingle

FERNET_TOKENS = [
    "XIKL24X0Fu83UmPLmWkXOBvvqsLq41tz2LljwafDyZw=",
    "ep1FyYA6epwbFxrtEJ2dii5BGvTx5-xU1oUCrF61qMA=",
    "POI93oiBlBWkerhMN9oLoURZdqbqC7ItIwsuo6GepOA=",
    "IeG7_fUGD5uWwl971hZsc0QKiWuxuWJl2KljaVKLM0o=",
    "NYdjm7cgRzR6hHkn7M48t2TzjDV-H23Y4_z07GiZMxw="
    "7MGjNQCs4Uc6z_xWZj2w1w-OTaKwB2pdZaiz93FCjvA="
]


async def initialize_server():
    server = SniTunServerSingle(
        FERNET_TOKENS,host='0.0.0.0', port=443,throttling=500
        )
    await server.start()
    while True:
        await asyncio.sleep(0.1)
async def main():
    await initialize_server()

loop = asyncio.get_event_loop()
result = loop.run_until_complete(main())

Mọi người chú ý 2 chỗ, là FERNET_TOKENS và chỗ địa chỉ host 0.0.0.0 mọi người có thể để vậy hoặc đổi về public IP của VPS. Đối với địa chỉ host thì sẽ được thay thế bằng địa chỉ IP của VPS. Còn FERNET_TOKENS này thì bạn nên sinh ra cho mình một token, chạy code dưới để sinh và các token này cách nhau bằng dấu phẩy như ở trên nhé

from cryptography.fernet import Fernet
fernet_key= Fernet.generate_key()
print(fernet_key.decode()) # your fernet_key, keep it in secured place!

Để cho SniTun Server luôn chạy thì bạn có thể chạy Python này dưới dạng một service (VPS mình đang chạy CentOS). Mọi người tham khảo bài này nhé: https://tecadmin.net/setup-autorun-python-script-using-systemd/

Mọi người vào phần quản lý DNS domain trên Cloudflare, trỏ domain *.ui.191lab.tech về IP của server đang chạy SniTun Server

2.4. Xây dựng server quản lý:

Link code: https://github.com/phuctu1901/hass_remote_control_snitun

Mọi người clone repo trên về máy của mình

Mọi người truy cập theo đường dẫn sau: https://cognito-idp.us-east-1.amazonaws.com/us-east-1_n26MnE2AK/.well-known/jwks.json

Nhớ thay thế phần: us-east-1_n26MnE2AK bằng Pool ID của bạn tạo ra ở bước trên.

Sau khi truy cập mọi người sẽ được một file json và lưu nó lại vào thư mục đã clone ở trên, thay thế cho file jwks.json có sẵn.

Sau đó mọi người mở file .env trong thư mục server và cập nhật các giá trị đã tạo ở các bước phía trên

Tương tự như với Snitun Server, thì bạn nên tạo thành service để chạy. Đồng thời nhớ mở port trên firewall các cổng 8085, 80 và 443

2.5. Khởi chạy Hass với thay đổi về remote control

Mọi người truy cập vào thư mục custom_hass ở repo vừa clone trên. Trong file docker-compose.yml có đoạn sau:

./hass-nabucasa/hass_nabucasa:/usr/local/lib/python3.8/site-packages/hass_nabucasa

Mục đích là để mount các package hass_nabucasa đã được thay đổi các thông số cho package mặc định của nó.

Trong thư mục custom_hass mọi người mở file hass-nabucasa/hass_nabucasa/const.py. Cập nhật các tham số cho cognito_client_id, user_pool_id, region, subscription_info_url, remote_api_url bằng các tham số ở các bước trên.

III. Tạm kết

Như vậy là bạn có thể có một chức năng đăng ký, đăng nhập và remote control tương tự như sử dùng Nabucasa nhưng bạn sẽ không có được các tính năng tích hợp Google, Alexa. Cách thức xây dựng server trong bài viết tương đối rườm rà nhưng không hề khó khăn, nên ai muốn trải nghiệm có thể thử. Đồng thời đây chỉ thiêng về xem nó có hoạt động hay không nên còn nhiều cái cần cải tiến, như có một trang web riêng như trang https://remote.nabucasa.com/ sau khi đăng nhập nó tự động redirect tới trang điều khiển của mình, hoặc cho phép custom lại sub domain…

Cách này có nhiều hạn chế so với các cách dùng VPN, tuy nhiên vẫn là một trải nghiệm thú vị.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *