본문 바로가기

SmartHome. IoT/SmartHome 응용

[HA] 수도권 실시간 지하철 도착정보 불러오기

728x90
반응형

안녕하세요~ 레이군 입니다.

오늘은 HA에서 실시간 지하철 도착정보 불러오기를 해보겠습니다.

전 지하철을 이용해 출퇴근하고, 서울 시내에 어딘가를 가야 한다면(마트 제외) 가능하면 지하철을 이용하는 편입니다.

그런데 지하철을 타러 갈 때마다 그런 경우가 있죠.

에스컬레이터 내려왔는데, 지하철 도착 전광판에 당역 도착 떠있고...

'아, 30초만 먼저 알았으면 빨리 걷거나 뛰었을 텐데......'

특히 출근시간이라면 그 20, 30초의 차이가 몇 분으로 벌어지기도 하죠.

'그냥 카카오 지하철이나 지하철 앱에서 보면 되는 거 아냐?'

하실지도 모르지만...... 사실 그게 맞긴 하지만, 그냥 누군가 딱 알려주면 좋겠어서요.

자 본격적으로 시작해 볼까요?

아 참, 이 데이터는 서울시의 API를 따오는 거라 타 지역은 확인이 어렵습니다!

각 지역별 제공되는 API를 확인해 주세요.

수도권 노선중 인천 1/2호선, 김포 골드라인, 에버라인, 의정부선은 지원되지 않습니다.

데이터를 추출하자

네이버 맵 등에서 데이터를 가져오는 방법이 있긴 하지만, 가끔 업데이트가 되거나 하면 경로가 틀어지더라고요.

그래서 좀 더 안정적이라고 생각하는 openAPI 방식으로 가져오기로 합니다.

 

 

 

서울 열린데이터광장 (seoul.go.kr)

 

열린데이터광장 메인

데이터분류,데이터검색,데이터활용

data.seoul.go.kr

 

데이터 제공 및 API 신청은 이쪽에서 가능합니다.

데이터 사용에는 서울시 ID가 필요합니다.

로그인 혹은 회원가입을 해주세요

저는 없어서 후다닥 회원가입을 했습니다. 가입 절차 등은 뭐... 생략할게요.

로그인 완료 후 검색 하단의 지하철을 클릭 - 서울시 지하철 실시간 도착정보로 들어가시면 됩니다.

API 신청을 누르면, 이런 창이 나옵니다.

여기서 적당히 선택해서 신청을 해주세요.

그러면 인증 키가 나오는데, 인증키 복사 항목을 누르시고 컨트롤+C를 눌러 클립보드에 복사해 주세요.

확인 눌러주시면 됩니다.

그리고 하단에 보면 API 테스트 부분이 있습니다.

여기서 API검색 : 지하철 입력 후 -> 검색 클릭

하단 API명 박스 클릭 -> [[TOPIS] 지하철 실시간]서울시 지하철 실시간 도착정보 를 선택해주세요

 

그런 다음 아래 지하철 역명 에다가 내가 원하는 지하철역 이름을 넣어주세요.

저는 예시로 역삼 을 넣었습니다.

http://swopenapi.seoul.go.kr/api/subway/sample/xml/realtimeStationArrival/1/5/%EC%97%AD%EC%82%BC

그러면 위와 같은 값이 리턴되는데, 여기서 우리가 수정할 곳은 2곳 / 경우에 따라서 4곳입니다.

/sample/ 부분은 아까 따온 키로 넣어주시면 됩니다.

본 예제에선 /444174725372617934325545415550/ 로 변경됩니다.

그 옆에 /xml/ 부분도 변경합니다. 데이터 특성상 JSON이 더 편해서요. /json/ 으로 변경합니다.

이렇게 하면 기본적인 건 완료입니다.

그럼 저는 제 인증키를 사용해서 json 타입으로 역삼의 데이터를 가져와 보겠습니다.

http://swopenAPI.seoul.go.kr/api/subway/444174725372617934325545415550/json/realtimeStationArrival/1/5/%EC%97%AD%EC%82%BC

이렇게 추출된 URL을 웹사이트에 붙여 넣어 보세요.(위 API 키는 만료된 거라 실제로 동작이 안 됩니다. 키 발급받은 것으로 넣어주세요)

이런 내용이 보인다면 정상으로 호출된 겁니다.

아, 혹시 한 줄로 길게 보이시나요?

저는 엣지로 하면 항목별로 보이고 크롬으로 하면 위처럼 한 줄로 보이더라고요.

크롬에서 json 형태를 가독성 있게 보려면 jsonview 라는 확장 프로그램이 필요하다고 합니다.

전 귀찮아서 그냥 다른 브라우저로 봤습니다.

위에서 수정할 곳이 2곳 혹은 4곳이라고 알려드렸죠?

나머지 2군데는 역에서 불러오는 데이터가 많을 경우 사용됩니다.

일반적으로 지하철 도착정보는 상/하행 x 2대 = 4대분 정보가 필요합니다.

하지만 환승역 등으로 인하여 노선이 추가되는 경우(합정, 고속터미널 등) 더 많은 데이터가 나오죠.

4대분 정보 x 노선수 이니 합정같이 2개 노선이라면 8개 데이터, 고속터미널 같이 3개라면 12개 데이터가 나옵니다.

이 경우 호출 주소의 뒤쪽 /1/5/ 부분을 수정해 주셔야 하는데

앞쪽 숫자는 시작 / 뒤쪽 숫자는 끝 입니다.

/1/5/ = 1번째 데이터 ~ 5번째 데이터까지 표시입니다.

이걸 변경해서 /5/8/ 이렇게 하면, 5번째 데이터 ~ 8번째 데이터까지 표시가 되겠죠.

이 부분은 직접 테스트해보면서 필요한 만큼의 범위로 정하시면 됩니다.

뭐, 한국 인터넷 빠르고 API 호출 시 1회당 호출 양의 제한은 없으므로

그냥 범위 넓게 잡고 필요한 거만 빼서 쓰셔도 됩니다.

HA에서 센서 만들기

자 다음은 HA에서 센서를 만들어 보겠습니다.

센서 만드는 건 HA에서 지원하는 command line 센서를 사용할 예정입니다.

file editor를 포함한 사용하는 텍스트 에디터를 활용하여 configuration.yaml 파일을 열어주세요.

물론 이미 command_line: !include commandline.yaml 등으로 별도로 분리해 두셨다면 해당 파일을 열어주세요.

command_line:
  - sensor:
      name: Subway Arrival Messages_1
      command: "curl -s 'http://swopenapi.seoul.go.kr/api/subway/444174725372617934325545415550/json/realtimeStationArrival/1/5/%EC%97%AD%EC%82%BC' | jq -r '.realtimeArrivalList[2]'"
      value_template: "{{ value_json.arvlMsg2 }}"
      json_attributes:
        - arvlMsg3
      scan_interval: 1200
  - sensor:
      name: Subway Arrival Messages_2
      command: "curl -s 'http://swopenapi.seoul.go.kr/api/subway/444174725372617934325545415550/json/realtimeStationArrival/1/5/%EC%97%AD%EC%82%BC' | jq -r '.realtimeArrivalList[3]'"
      value_template: "{{ value_json.arvlMsg2 }}"
      json_attributes:
        - arvlMsg3
      scan_interval: 1200

 

제가 사용할 데이터는 arvlMsg2 인 도착까지의 소요시간 / arvlMsg3 인 현재 역 정보입니다.

command_line 플랫폼으로 sensor를 만들 것이고

name는 적당히 정해주시고

command: 부분은 "curl -s '아까 딴 URL' | jq -r ''.realtimeArrivalList[2]'" 로 하여 3번째 데이터를 불러옵니다.

([2] 부분은 위에서부터 [0] [1] [2] [3] 순서이므로 [2] 로 표기하면 3번째 데이터입니다)

센서의 값인 value 부분은 value_template: 으로 설정해서 "{{ value_json.arvlMsg2 }}"

즉, json 데이터 중 arvlMsg2의 항목을 표출하며

json_attributes: 로 센서의 속성 항목을 추가해서

- arvlMsg3 인 현재 역 데이터를 넣을 겁니다.

(물론 여기서 arvlMsg2(남은시간) / arvlMsg3(현재역) 은 변경해도 됩니다. 센서에 어떻게 표시되는지 차이니까요)

scan_interval 항목은 1200으로 해서 20분마다 업데이트로 했습니다.

지하철인데 20분마다 업데이트는 너무한 거 아닌가?라고 하실 수 있는데

이 부분은 내가 필요할 때 자동화로 업데이트하는 게 나아서요.

이 API는 1일 1000회 호출 제한이 있고, 사용 목적이 명확하므로 내가 쓰려고 할 때만 업데이트되는 게 좋습니다.

20분 지정한 것도... 그냥 센서 값이 뭔가 변했으면 좋겠다 싶은 마음에 정해둔 것뿐,

1시간(3600)이나 그 이상으로 해도 됩니다.

동일한 센서를 복사해서 넣어주고, realtimeArrivalList[2] 부분을 내가 원하는 항목에 맞게 변경해 줍니다.

순서는 위에 설명드렸죠?

처음 만든 센서는 바로 다음에 오는 열차, 다음에 만든 센서는 그다음 열차입니다.

 

############################

환승역의 경우, 값의 위치가 고정이 아니라 계속 변하네요. 이에 따라 조금 다르게 작업해주셔야 합니다.

합정역을 예로 들겠습니다.

http://swopenapi.seoul.go.kr/api/subway/sample/json/realtimeStationArrival/1/5/%ED%95%A9%EC%A0%95

위 주소로 접속하시면 데이터들이 나옵니다.

이런식으로 나오는데, 여기서 우리에게 필요한건 subwayId 항목과 ordkey 부분 입니다.

노선의 코드이며 ordkey 부분은 맨 앞 자리가 상/하행(외/내선), 두번째 자리가 첫번째, 두번째 도착예정을 뜻합니다.

위 예시에서 subwayId가 1002 니까 2호선 이고, ordkey 부분이 01로 시작하니 외선의 첫번째 도착 예정 열차 입니다.

그래서 이걸 필터링 걸어줘야 합니다.

 

  - sensor:
      name: Subway Arrival Messages_3
      command: curl -s 'http://swopenapi.seoul.go.kr/api/subway/sample/json/realtimeStationArrival/1/5/%ED%95%A9%EC%A0%95' | jq -r '.realtimeArrivalList[] | select(.subwayId == "1002") | select(.ordkey | startswith("01"))'
      value_template: "{{ value_json.arvlMsg2 }}"
      json_attributes:
        - arvlMsg3
      scan_interval: 600  # 10 minutes (adjust as needed)

원래 예제는 command 부분에 큰 따옴표가 앞뒤로 들어가 있었는데, 이 부분이 사라진점 유의해주세요.

중간에 필터 거는 부분에 큰따옴표를 쓰면서 충돌나서 지워야 합니다.

변경된 부분만 알려드리겠습니다.

.realtimeArrivalList[] => 해당 항목의 모든 내용을 불러올거라 숫자를 적지 않습니다.

| select(.subwayId == "1002") => 필터로subwayId가 1002와 동일한 항목만 걸러내고

| select(.ordkey | startswith("01")) => 추가 필터로 ordkey가 01로 시작하는 항목만 걸러냅니다.

2번째 도착열차를 표시하려면 "02" 로 기재해야 하고, 외선이 아닌 내선이라면 "11" 혹은 "12"가 되겠죠.

이 부분은 사용할 역의 데이터를 확인해 주세요

 

############################

 

특정 역의 경우, 1개 노선당 데이터가 3개씩 나오는 경우가 있는데(시청역 근처 1호선 등) 이때는 위 방법으로 안 됩니다.

조금 더 복잡한 방법으로 진행하셔야 합니다.

 

  - sensor:
      name: Subway Arrival Messages_3
      command: curl -s "http://swopenapi.seoul.go.kr/api/subway/444174725372617934325545415550/json/realtimeStationArrival/1/5/%EC%8B%9C%EC%B2%AD" | jq '.realtimeArrivalList[] | select(.subwayId == "1001") | select(.ordkey | startswith("0")) | {arvlMsg2, arvlMsg3, bstatnNm}' | sed -n 1,5p
      value_template: "{{ value_json.arvlMsg2 }}"
      json_attributes: 
        - arvlMsg3
        - bstatnNm
      scan_interval: 600  # 10 minutes (adjust as needed)
  - sensor:
      name: Subway Arrival Messages_4
      command: curl -s "http://swopenapi.seoul.go.kr/api/subway/444174725372617934325545415550/json/realtimeStationArrival/1/5/%EC%8B%9C%EC%B2%AD" | jq '.realtimeArrivalList[] | select(.subwayId == "1001") | select(.ordkey | startswith("0")) | {arvlMsg2, arvlMsg3, bstatnNm}' | sed -n 6,10p
      value_template: "{{ value_json.arvlMsg2 }}"
      json_attributes: 
        - arvlMsg3
        - bstatnNm
      scan_interval: 600  # 10 minutes (adjust as needed)
  - sensor:
      name: Subway Arrival Messages_5
      command: curl -s "http://swopenapi.seoul.go.kr/api/subway/444174725372617934325545415550/json/realtimeStationArrival/1/5/%EC%8B%9C%EC%B2%AD" | jq '.realtimeArrivalList[] | select(.subwayId == "1001") | select(.ordkey | startswith("0")) | {arvlMsg2, arvlMsg3, bstatnNm}' | sed -n 11,15p
      value_template: "{{ value_json.arvlMsg2 }}"
      json_attributes: 
        - arvlMsg3
        - bstatnNm
      scan_interval: 600  # 10 minutes (adjust as needed)
 

소스코드는 위 내용을 참조하시고 옵션 부분 추가설명 입니다.

| select(.ordkey | startswith("0")) -> 필터로 ordkey 부분이 0으로 시작하는(상행) 항목만 추출합니다.

| {arvlMsg2, arvlMsg3, bstatnNm}' -> 필터로 arvlMgs2, arvlMgs3, bstatnNm 항목만을 따로 보여줍니다.

 

필터가 걸리면 이런식으로 표시가 되는데, 여기서 첫번째 줄인 { 부터 5번째 줄인 } 까지가 첫번째 도착 차량 입니다.

| sed -n 1,5p -> 해당 항목중 1번째 줄에서 5번째 줄 까지만 표시합니다.

{ 부터 } 까지 있어야 json 형태로 인식하기 때문에 1~5번째 줄 까지로 선택합니다.

 

첫번째 센서는 1~5번재 줄 까지, 두번째 센서는 6~10번째 줄 까지, 세번째 센서는 10~15번째 줄 까지로 정했습니다.

물론 이건 bstatnNm항목(종착역) 을 넣으려고 한거고, 이 부분이 필요없다면 필터에서 빼주시면 됩니다.

그 경우, 결과값의 { 부터 } 까지가 5줄이 아닌 4줄로 나오니 표시 줄수를 조절해주셔야 합니다.

대표사진 삭제

사진 설명을 입력하세요.

위 내용을 적용한 센서는 이렇게 생성됩니다.

############################

 

 

다 만드셨으면 HA를 재시작 해주세요.

개발자도구 - YAML - 재시작 - Home Assistant 재시작 해주시면 됩니다.

빠르게 다시 로드하기 는 안됩니다. 커맨드라인 센서를 처음 만들었다면 무조건 재시작을 해줘야만 올라옵니다.

(이미 만든 커맨드라인 센서가 있다면, 빠르게 다시 로드로 가능)

 

HA가 재시작되면, 개발자 도구-상태 항목에 가서 지정한 센서 이름으로 검색해 봅니다.

저는 arvlMgs2를 value에 지정하고, arvlMgs3를 attribute에 지정했으므로 위와 같이 나옵니다.

반대로 하셨다면 센서 상태 값에 역 / 속성의 arvlMgs2에 남은 시간이 표현될 겁니다.

자동화 작성

자 이제 자동화로 이 데이터를 활용해서 노티를 날려보겠습니다.

노티 서비스는 여러 개가 있지만, 저는 가장 간단하게 사용 가능한 휴대폰용 HA 앱의 노티를 쓰겠습니다.

(만약 다른 노티 서비스를 사용하신다면, 변경해서 적용하시면 됩니다)

HA 앱이 뭔지 모르고 노티도 안 간다고요? 아래 공홈 링크를 참조해서 설정하세요.

https://www.home-assistant.io/integrations/mobile_app

 

Mobile App

The Mobile App integration allows a generic platform for integrating with mobile apps.

www.home-assistant.io

 

자동화의 트리거나 컨디션 부분은 개개인마다 다를 테니 이건 직접 설정해 주셔야 하고요, 저는 동작 부분만 다루겠습니다.

 

동작 추가하기에서 core로 검색해서 내리면, Home Assistant Core Intergration: 구성요소 업데이트 가 보입니다.

이거 고르시고요

구성요소 선택하기 항목에서 아까 지정해두었던 센서를 선택해 주세요.

2개 다 하셔야 합니다. 이번 열차 센서 / 다음 열차 센서 이니까요.

이건 선택사항이긴 한데, 블록 구축에서 지연시간(딜레이) 를 1초 정도 넣어주세요.

업데이트 -> 푸시 순서인데, 혹시 회선이 이상하거나 서버가 느리거나 해서 바로 갱신이 안 될 때 대비용입니다.

그냥 해보다가 이상하다 싶으면 1초 추가하시는 걸 권장 드려요.

다시 동작 추가하신 다음, noti 를 검색하면 모바일앱용 노티 서비스가 보일 겁니다.

메시지는 그냥 보낼 게 아니라 센서의 값을 불러와서 변형 후 보낼 것이기 때문에, 템플릿으로 작성하겠습니다.

{{ state_attr('sensor.subway_arrival_messages_1', 'arvlMsg3')  }} /
{{ states('sensor.subway_arrival_messages_1')}},

다음 열차: {{ state_attr('sensor.subway_arrival_messages_2', 'arvlMsg3')  }} /
{{ states('sensor.subway_arrival_messages_2') }} 도착

message 부분에 위와 같이 입력하면, 해당 구성은 UI에서 실행할 수 없다고 자동으로 yaml 편집으로 변경됩니다.

위와 같이 yaml 에디터로 변경됩니다.

이 경우 데이터가 1줄로 표시되는데, 저는 줄바꿈을 해주고 싶습니다.

다음 열차 부분에 줄 바꿈을 원해서 엔터 2번 쳐줍니다.

그런 다음 동작의 우측 상단 3점을 눌러 실행을 해봅니다.

폰으로 노티가 잘 날아왔습니다.

이제 적당히 역 도착 2~3분 전쯤 울릴 수 있는 트리거를 잡아주시면 되겠죠?

트리거 아이디어는... 사람마다 다르니 이건 각자의 아이디어 싸움으로...!

오늘 내용은 여기까지입니다~

다음에 또 재미난 거 들고 올게요~

 

728x90
반응형