본문 바로가기

생활코딩

LG ThinQ API 로 가전 제품과 연동하기

반응형
LG ThinQ OpenAPI 연동 시리즈
1. LG ThinQ API 로 가전 제품과 연동하기
2. LG ThinQ API로 가전 제품과 연동하기 - 2 (MQTT)  
3. 라즈베리파이 - Home Assistant - LG ThinQ 연동

 

LG ThinQ API가 드디어 일반에게도 공개됐더군요. 작년 초에 공개할 계획이라는 기사를 보고 기다리다 잊고 있었는데, 궁금해서 찾아보니 작년 12월에 공개를 했더군요. 사용해 본 내용을 간단히 정리합니다.

0. 준비

개발자 사이트 가입

PAT(Personal Access Token) 발급

1. 클라이언트 아이디 등록

PAT를 발급받고 나서는 클라이언트 아이디를 등록해야 합니다. API를 설명하는 웹 페이지는 https://smartsolution.developer.lge.com/ko/apiManage/thinq_connect?s=1739167629244 인데 실제로 이러한 절차가 잘 설명돼 있지 않습니다. 이러한 단계를 설명하는 내용은 ThinQ Open API 사용을 위해 제공하는 파이썬 패키지의 GitHub Repsitory에 있는 README.md 에 나와 있습니다. 아직 초기 단계라 그런지 문서가 필수 내용은 있는 데 친절하지는 않더군요. 파이썬 패키지가 있다는 것도 고객 지원 > 질의응답 부분에서 누군가 질문한 내용의 답변에서 알 수 있었습니다.

https://github.com/thinq-connect/pythinqconnect

 

GitHub - thinq-connect/pythinqconnect

Contribute to thinq-connect/pythinqconnect development by creating an account on GitHub.

github.com

여기가 ThinQ Open API 사용을 편리하게 해주는 파이썬 패키지 Source Repo 입니다. API 명세들을 랩핑해서 비교적 편리하게 쓸 수 있게 해 주기 때문에 처음부터 이걸 사용하시는 것도 좋은 방법입니다. 

클라이언트 아이디 등록 부분인데 서론이 길었는데요, 공식 문서 웹사이트에는 절차가 잘 안 나오고 GitHub에 있는 공식 패키지의 Soure Repo에 있는 README.md에 설명이 잘 나와 있다는 말을 하다 보니 그렇게 됐습니다. 

저는 개인적으로 직접 코드를 작성해서 API를 호출하기도 해 봤는데요, 여기서는 pythinqconnect 사용하는 방법으로 예제 코드를 얘기하도록 하겠습니다.

Python을 어느 정도 사용할 수 있다고 전제하고 가상 환경 설정 등과 같은 기본적인 내용은 생략하도록 하겠습니다.

1.1 패키지 설치

pip install thinqconnect

1.2 등록

import asyncio
import uuid

from aiohttp import ClientSession
from thinqconnect import ThinQApi


async def main():
    client_id = str(uuid.uuid4())
    print(client_id)
    async with ClientSession() as session:
        thinq_api = ThinQApi(
            session=session,
            access_token="자신의 PAT",
            country_code="KR",
            client_id=client_id,
        )
        payload = {
            'type': "MQTT",
            'service-code': "SVC202",
            'device-type': "607",
        }
        response = await thinq_api.async_post_client_register(payload)

        print(response)


if __name__ == "__main__":
    asyncio.run(main())

 

다음의 코드를 적절히 저장한 후에 python 파일이름.py 식으로 실행합니다. 결과는 다음과 같이 나올 겁니다.

aaaaaaaaa-bbbb-11cc-22dd-0123456789ab
{}

 

payload 구성에 대한 내용은 모두 고정 값이기는 한데, API 명세에 잘 나옵니다. 클라이언트 아이디는 실제 유일한 값이면 무방한 건 같기는 합니다만, 위에 코드에 나오는 방식처럼 UUID V4 방식으로 생성해서 사용할 것을 권하고 있습니다. API 명세에 보면 API 값에 messageId와 timestamp도 포함돼 있습니다만, pythinqconnect 패키지는 각 API와 대응하는 메소드를 호출했을 때 응답 중 response 부분만 반환하게 돼 있습니다. 그래서, 클라이언트 생성 시 문제가 없는 경우 response가 빈 값으로 응답되기 때문에 {} 식으로 빈 값이 출력된 것입니다.

이렇게 등록된 아이디를 계속 사용하게 되므로 잘 저장해 둡니다.

2. 등록된 기기 조회

ThinQ Open API의 구성 체계는 다음과 같다고 생각하시면 됩니다.

  • ThinQ App: ThinQ 플랫폼 가전기기를 등록하는 역할
  • ThinQ API: 등록된 가전기기들을 운용할 수 있는 Server API 제공
  • pythinqconnect: ThinQ Open API를 보다 쉽게 사용할 수 있게 해주는 Python 패키지
  • ThinQ MQTT 서버: 조건에 맞는 이벤트와 푸시를 접속한 MQTT 클라이언트에게 전송

ThinQ 앱에서 등록한 기기들은 다음과 같이 조회할 수 있습니다.

import asyncio
from pprint import pprint

from aiohttp import ClientSession
from thinqconnect import ThinQApi


async def main():
    async with ClientSession() as session:
        thinq_api = ThinQApi(
            session=session,
            access_token="자신의PAT",
            country_code="KR",
            client_id="등록한클라이언트아이디",
        )

        response = await thinq_api.async_get_device_list()

        pprint(response)


if __name__ == "__main__":
    asyncio.run(main())

 

이렇게 조회하면 다음과 같은 내용을 볼 수 있습니다.

[{'deviceId': 'xxxx...',
  'deviceInfo': {'alias': '에어컨',
                 'deviceType': 'DEVICE_AIR_CONDITIONER',
                 'modelName': '...',
                 'reportable': True}},
 }]

 

여기서 중요한 점은 두 가지입니다.

첫 번째. ThinQ 앱에서 등록된 기기라도 모두 조회되지는 않는다는 점입니다. 공식 문서에서 디바이스 프로파일 정보를 제공하는 기기들만이 조회됩니다. 추후 계속 늘려 갈지는 모르겠지만, 테스트를 하면서 확인한 내용으로는 그렇습니다. 현재로서는 TV를 지원하지 않는 것이 제일 아쉬운 거 같습니다.

두 번째, 디바이스 아이디입니다. 이 아이디가 각 기기를 구분하는 값입니다. 각 기기별로 현재 상태를 확인하거나 설정을 변경할 때 이 아이디를 가지고 구분합니다.

3. 기기 상태 조회

import asyncio
from pprint import pprint

from aiohttp import ClientSession
from thinqconnect import ThinQApi


async def main():
    async with ClientSession() as session:
        thinq_api = ThinQApi(
            session=session,
            access_token="PAT",
            country_code="KR",
            client_id='등록한클라이언트아이디',
        )

        response = await thinq_api.async_get_device_status('디바이스아이디')

        pprint(response)


if __name__ == "__main__":
    asyncio.run(main())

다음은 실제 저희 집의 냉장고 상태를 조회한 내용입니다.

{'doorStatus': [{'doorState': 'CLOSE', 'locationName': 'MAIN'}],
 'refrigeration': {'expressMode': False, 'freshAirFilter': 'AUTO'},
 'temperature': [{'locationName': 'FRIDGE',
                  'targetTemperature': 4,
                  'unit': 'C'},
                 {'locationName': 'FREEZER',
                  'targetTemperature': -22,
                  'unit': 'C'}]}

4. 기기 제어

이제 대략적으로 어떤 식으로 사용하는지 아실 겁니다. 그래서, 기기 제어 정도만 설명하면 기본적인 API 사용은 더 이야기하지 않아도 될 거 같습니다.

import asyncio
from pprint import pprint

from aiohttp import ClientSession
from thinqconnect import ThinQApi


async def main():
    async with ClientSession() as session:
        thinq_api = ThinQApi(
            session=session,
            access_token="PAT",
            country_code="KR",
            client_id="등록한클라이언트아이디",
        )

        payload = {
            "temperature": {
                "locationName": "FRIDGE",
                "targetTemperature": 2,
                "unit": "C",
            }
        }

        response = await thinq_api.async_post_device_control(
            "기기의디바이스아이디",
            payload
        )

        pprint(response)


if __name__ == "__main__":
    asyncio.run(main())

 

위 코드는 냉장고의 냉장실 온도를 2℃로 설정하는 코드입니다. 기기 제어 API를 호출하는 방법은 다른 것들과 동일하여 어렵지 않으나 실제 설정하는 내용을 잘 지정해야 합니다. 설정 방법은 기기마다 다른데 이에 대한 내용은 기기 프로파일 정보를 통해서 알 수 있습니다. 앞서도 잠깐 언급했었는데, 기기 프로파일 정보는 디바이스 정보라는 이름으로 공식 문서에서 제공하고 있습니다.

https://smartsolution.developer.lge.com/ko/apiManage/device_profile?s=1739172817549

부가적으로 프로파일 조회 API를 통해서 조회할 수 있습니다.

 

프로파일 정보 부분에서 요청/응답 스키마 부분을 보면 어떤 식으로 요청(payload 지정)해야 기기를 제어할 수 있는지 알 수 있습니다. 다음은 위에서 예를 든 냉장고의 응답 스키마입니다.

냉장고 응답 스키마 일부분

응답 스키마에서 상위 속성과 하위 속성을 객체 형태로 지정하여 설정하면 됩니다. 주의할 접은 응답에서는 하위 계층이 배열 형태여도 요청 시에는 배열로 여러 개를 요청할 수 없습니다. 그래서, 온도(temperature)의 응답은 냉장실, 냉동실 등이 한 번에 나타나는 배열 형태지만, 조절을 위해 요청할 때는 위에서도 본 것처럼 다음과 같이 지정합니다.

{  
     "temperature"  :   { 
       "targetTemperature":  0, 
       "locationName": "FRIDGE", 
       "unit": "C" 
     } 
}

 

5. 마무리

이 정도 선이면 API 사용은 문제가 없을 거 같고, 이제 남은 부분은 푸시와 이벤트 등록을 한 후에 관련 정보를 MQTT를 통해서 받는 부분과 Home Assistant 를 통해 사용하는 거 같습니다. 이 부분들은 추후에 순차적으로 하나씩 다뤄보도록 하겠습니다.

개인적으로 ThinQ가 개인 사용자에게도 Open API를 공개한 것은 매우 반갑게 생각합니다. 그래서, 그 의도나 앞으로의 발전에 대해서는 기대가 큽니다만 현재 시점으로는 아쉬운 점들이 몇 가지 보였습니다.

  • 문서가 친절하지 못합니다. 공식 사이트에서 API 명세 위주로만 얘기하고 있는 점이나 pythinqconnect를 열심히 만들어 놓고, 공식 사이트에는 잘 언급하고 있지 않고 있는 점들은 빠르게 개선해야 할 거 같습니다. pythinqconnect는 소스 레벨로도 살펴봤는데, 열심히 잘 만드셨던데, 문서화가 부족하여 그 빛이 발하지 못하는 점이 아쉽더군요.
  • ThinQ 앱에서 등록하여 제어나 상태 조회가 가능한 기기들 전체를 지원하지 못합니다.
  • 기기들이 제공하는 정보가 제한적인 것들이 있습니다. 집에서 쓰고 있는 기기들만 봐도 연도가 올라갈 수도록 제공하는 정보들이 많아지고, 세세해지고 있는 것을 확인할 수 있었습니다. 이 부분은 물론 기기의 하드웨어 측면과 맞물려 가야 할 부분으로 보이며, 지속적으로 사용자에게 유익해 보이는 부분들에 대한 제어나 정보를 확대해 나갔으면 좋겠습니다.

당연히 계속해서 개선될 거라는 생각은 들고, 꾸준히 좋은 생태계로 잘 유지해 주길 바래 봅니다. 

 

가전 기기들을 이런 Open API를 통해서 제어하는 것은 편리함과 재미를 개인들에게 선사해 줍니다. 다만, 보안을 신경 안 쓸 수가 없습니다. 개인적으로도 기능이 많아지기를 기대하지만, 그에 대한 반대급부로 해킹으로 인한 보안 위험도 더 커지게 됩니다. 기업에서도 시스템을 구축하면 신경을 많이 쓰고 있겠지만, 사용하는 입장에서도 보안에 신경을 많이 써야 할 거 같습니다.

 

개인적으로 에어컨과 냉장고를 대상으로 1분에 한 번씩 상태를 조회하는 프로그램을 만들고, 해당 값을 이전에 라즈베리파이에 설치해 놓은 graphite에 저장해서 (역시 라즈베리파이에 설치된) Grafana 로 시각화를 해 봤습니다.

기기를 통해 얻은 지표 시각화

처음에는 Event/Push를 MQTT로 받아서 저장하는 방식으로 구현을 했는데, 이 값들은 보내주는 주기가 기기마다 다르고, 특정 기기는 특정 이벤트가 있어야지만 보내주는 상황이어서 주기적인 수치 저장을 위해 1분에 한 번씩 기기 상태 조회를 하는 방식을 선택했습니다.

 

여담으로 Soure Repository에 README.md 내용을 보다 보니 아주 살짝 잘못된 부분이 있어서 고친 후 PR을 날렸습니다. 그런데, pythinqconnect는 오픈 소스로 공개는 했지만 개발은 내부적으로 로드맵에 맞춰 해 나가기로 한 거라 외부 PR은 받지 않는다는 의도로 아주 정중한 답변을 받았습니다. 계속적인 관심도 부탁 하면서요. 혹시, 소스 코드에 대한 PR 하실 분들은 참고하시기 바랍니다.