Web of Things (WoT) 스크립팅 API

W3C 그룹 노트

이 문서에 대한 자세한 정보
이 버전:
https://www.w3.org/TR/2023/NOTE-wot-scripting-api-20231003/
최신 공개 버전:
https://www.w3.org/TR/wot-scripting-api/
최신 편집자 초안:
https://w3c.github.io/wot-scripting-api/
이력:
https://www.w3.org/standards/history/wot-scripting-api/
커밋 이력
편집자:
Zoltan Kis (Intel)
Daniel Peintner (Siemens AG)
Cristiano Aguzzi (초청 전문가)
Johannes Hund (이전 편집자, Siemens AG 재직 당시)
Kazuaki Nimura (이전 편집자, Fujitsu Ltd. 재직 당시)
피드백:
GitHub w3c/wot-scripting-api (풀 리퀘스트, 새 이슈, 열린 이슈)
public-wot-wg@w3.org 제목 줄에 [wot-scripting-api] … 메시지 주제 … (아카이브)
저장소
GitHub에서 보기
버그 신고
기여자
GitHub의 기여자

초록

Web of Things는 엔터티(Thing들)로 구성되며, 이들은 기계가 해석할 수 있는 Thing Description(TD)으로 자신의 기능을 설명하고 WoT Interface를 통해 이러한 기능을 노출할 수 있습니다. 즉, 값을 읽고 쓰기 위한 Properties, 반환값이 있거나 없는 원격 절차를 실행하기 위한 Action들, 그리고 알림을 신호하기 위한 Event들로 모델링된 네트워크 상호작용입니다.

주요 Web of Things(WoT) 개념은 Web of Things (WoT) Architecture 1.1 명세에 설명되어 있습니다.

스크립팅은 WoT에서 선택적 구성 요소이며, 일반적으로 WoT Runtime 스크립트 관리를 실행할 수 있는 게이트웨이나 브라우저에서 사용되어, 새로운 유형의 엔드포인트로 WoT 지원을 확장하고 TD Directory와 같은 WoT 애플리케이션을 구현하는 편리한 방법을 제공합니다.

이 명세는 스크립트가 Thing들을 발견하고 조작하며, 스크립트가 지정한 WoT Interactions로 특징지어지는 로컬 정의 Thing들을 노출할 수 있게 하는 WoT Interface를 나타내는 애플리케이션 프로그래밍 인터페이스(API)를 설명합니다.

이 문서에서 정의한 API는 의도적으로 Web of Things (WoT) Thing Description 1.1 명세를 긴밀히 따릅니다. 그 위에 더 추상적인 API를 구현하거나, WoT 네트워크 대면 인터페이스(즉, WoT Interface)를 직접 구현할 수 있습니다.

편집자 주

이 명세는 적어도 Eclipse Thingweb 프로젝트, 즉 node-wot로도 알려진 프로젝트에서 구현되어 있으며, 현재 기준 오픈 소스 구현으로 간주됩니다. 소스 코드 예제를 확인하십시오.

이 문서의 상태

이 절은 이 문서가 공개된 시점의 상태를 설명합니다. 현재 W3C 출판물 목록과 이 기술 보고서의 최신 개정판은 https://www.w3.org/TR/의 W3C 기술 보고서 색인에서 확인할 수 있습니다.

구현자는 이 명세가 불안정한 것으로 간주된다는 점을 인지해야 합니다. 이 명세가 결국 후보 권고안 단계에 도달하기 전에 구현하는 데 관심이 있는 벤더는 저장소를 구독하고 논의에 참여해야 합니다.

편집자 주: W3C WoT WG가 피드백을 요청합니다

이 초안에는 GitHub Issues 페이지, 즉 WoT Scripting API 저장소를 사용하여 기여해 주십시오. 보안 및 개인정보 보호 고려 사항에 대한 피드백은 WoT Security and Privacy Issues를 사용하십시오.

이 문서는 Web of Things Working GroupNote track을 사용하여 Group Note로 공개했습니다.

이 Group Note는 Web of Things Working Group의 승인을 받았지만, W3C 자체나 그 회원의 승인을 받은 것은 아닙니다.

이 문서는 초안 문서이며 언제든지 다른 문서로 업데이트, 대체 또는 폐기될 수 있습니다. 이 문서를 진행 중인 작업 이외의 것으로 인용하는 것은 부적절합니다.

W3C Patent Policy는 이 문서에 어떠한 라이선스 요구 사항이나 약속도 부과하지 않습니다.

이 문서는 2023년 6월 12일 W3C Process Document의 적용을 받습니다.

1. 소개

WoT는 Thing이 사용되는 방식, 즉 Web of Things (WoT) Architecture 1.1 용어에서 정의된 "consumed" 및 "exposed"에 기반한 계층적 상호운용성을 제공합니다.

TD를 소비함으로써, 클라이언트 Thing은 원격 장치의 서버 Thing이 노출한 Properties, ActionsEvents에 접근할 수 있게 하는 로컬 런타임 리소스 모델을 생성합니다.

Thing을 노출하려면 다음이 필요합니다: 이 명세는 스크립트로 Thing을 노출하고 소비하는 방법을 설명합니다. 또한, Thing 발견을 위한 일반 API를 정의합니다.
참고

일반적으로 스크립트는 더 단순한 장치를 WoT Thing으로 노출하고 제어하며, 스크립트를 처리(예: 설치, 제거, 업데이트 등)하고 실행할 수 있는 수단을 가진 브리지나 게이트웨이에서 사용되도록 의도됩니다.

참고

이 명세는 WoT Runtime이 단일 또는 다중 테넌시, 스크립트 배포 및 생명주기 관리를 포함하여 스크립트를 처리하고 실행하는 방식에 대해 가정하지 않습니다. API는 이미 스크립트 관리를 구현할 수 있게 하는 일반 메커니즘을 지원합니다. 예를 들어 Actions (액션 핸들러)가 스크립트 생명주기 관리 작업을 구현하는 관리자 Thing을 노출하는 방식입니다.

2. 사용 사례 시나리오

이 절은 비규범적입니다.

[WOT-USE-CASES] 문서에 나열된 비즈니스 사용 사례는 여기에 설명된 스크립팅 사용 사례 시나리오에 기반하여 이 API로 구현할 수 있습니다.

2.1 Thing 소비하기

2.2 Thing 노출하기

2.3 발견

3. 적합성

비규범적으로 표시된 절뿐 아니라, 이 명세의 모든 작성 지침, 다이어그램, 예제 및 참고는 비규범적입니다. 이 명세의 그 밖의 모든 내용은 규범적입니다.

이 문서의 핵심 단어 MAY, MUST, 및 SHOULD는 여기에 표시된 것처럼 모두 대문자로 나타나는 경우에만 BCP 14 [RFC2119] [RFC8174]에 설명된 대로 해석됩니다.

편집자 주

이 명세는 이전에 W3C Recommendation이 될 것으로 예상되던 Working Draft였습니다. 그러나 지금은 정보성 진술만 포함하는 WG Note입니다. 따라서 이 Conformance 절의 설명을 어떻게 다룰지 고려해야 합니다.

이 명세는 다음 user agent (UA) 클래스에 대한 적합성 기준을 설명합니다.

소형 임베디드 구현의 요구사항 때문에, WoT 클라이언트와 서버 인터페이스를 분리할 필요가 있었습니다. 또한, 발견은 분산 애플리케이션이지만, 일반적인 시나리오는 이 명세의 일반 발견 API로 다루어졌습니다. 이로 인해 이 API를 구현하는 UA에 대해 3개의 적합성 클래스, 즉 클라이언트용, 서버용, 발견용 클래스가 사용되었습니다. 이 API를 사용하는 애플리케이션은 WoT API objectconsume(), produce()discover() 메서드 존재 여부를 내부 검사하여 UA가 어떤 적합성 클래스를 구현하는지 판단할 수 있습니다.

WoT Consumer UA

이 적합성 클래스의 구현은 ConsumedThing 인터페이스와 WoT API objectconsume() 메서드를 MUST 구현해야 합니다.

WoT Producer UA

이 적합성 클래스의 구현은 ExposedThing 인터페이스와 WoT API objectproduce() 메서드를 MUST 구현해야 합니다.

WoT Discovery UA

이 적합성 클래스의 구현은 ThingDiscoveryProcess 인터페이스, discover() 메서드, exploreDirectory() 메서드 및 WoT API objectrequestThingDescription() 메서드를 MUST 구현해야 합니다.

이러한 적합성 클래스들은 단일 UA에서 MAY 구현될 수 있습니다.

이 명세는 여러 프로그래밍 언어에서 WoT Scripting API를 구현하는 데 사용할 수 있습니다. 인터페이스 정의는 [WEBIDL]에 지정되어 있습니다.

UA는 브라우저에서 구현되거나, Node.js 같은 별도의 런타임 환경 또는 소형 임베디드 런타임에서 구현될 수 있습니다.

브라우저에서 실행되는 ECMAScript를 사용하여 이 문서에 정의된 API를 구현하는 구현은 Web IDL 명세 [WEBIDL]에 정의된 ECMAScript Bindings와 일관된 방식으로 이를 구현해야 MUST 합니다.

런타임에서 TypeScript 또는 ECMAScript를 사용하여 이 문서에 정의된 API를 구현하는 구현은 TypeScript 명세 [TYPESCRIPT]에 정의된 TypeScript Bindings와 일관된 방식으로 이를 구현해야 MUST 합니다.

4. 용어와 규칙

일반 WoT 용어는 [WOT-ARCHITECTURE]에 정의되어 있습니다: Thing, Thing Description (줄여서 TD), Partial TD, Web of Things (줄여서 WoT), WoT Interface, Protocol Bindings, WoT Runtime, Consuming a Thing Description, TD Directory, Property, Action, Event, DataSchema, Form, SecurityScheme, NoSecurityScheme 등입니다.

WoT Interaction Interaction Affordance의 동의어입니다. Interaction Affordance(또는 줄여서 affordance)는 [WOT-TD]에서 Thing 기능을 지칭할 때 사용하는 용어이며, 이는 TD issue 282에 설명되어 있습니다. 그러나 이 용어는 TD 시맨틱 맥락 밖에서는 잘 이해되지 않습니다. 따라서 가독성을 위해, 이 문서는 이전 용어인 WoT interaction 또는 간단히 interaction을 대신 사용합니다.

WoT network interfaceWoT Interface의 동의어입니다.

JSON Schema이 명세들에 정의되어 있습니다.

Promise, Error, JSON, JSON.stringify, JSON.parse, internal method internal slot은 [ECMASCRIPT]에 정의되어 있습니다.

5. ThingDescription 타입

WebIDLtypedef object ThingDescription;

[WOT-TD]에 정의된 것처럼 Thing Description (TD)을 나타냅니다. 이는 구문 분석된 JSON 객체이며, JSON Schema 검증을 사용하여 검증될 것으로 예상됩니다.

5.1 Thing Description 가져오기

URL이 주어진 TD를 가져오는 것은 가져오기 세부 정보를 지정하는 표준화된 옵션을 이미 제공하는 Fetch API 또는 HTTP 클라이언트 라이브러리 같은 외부 메서드로 수행해야 합니다.

예제 1: Thing Description 가져오기
try {
  let res = await fetch('https://tds.mythings.biz/sensor11');
  // ... additional checks possible on res.headers
  let td = await res.json();
  let thing = await WOT.consume(td);
  console.log("Thing name: " + thing.getThingDescription().title);
} catch (err) {
  console.log("Fetching TD failed", err.message);
}

5.2 Thing Description 확장하기

Web of Things (WoT) Thing Description 1.1 명세는 기본값을 통해 축약된 Thing Description 사용을 허용하고, 클라이언트가 주어진 TD에 명시적으로 정의되지 않은 속성에 대해 Web of Things (WoT) Thing Description 1.1 명세에 지정된 기본값으로 이를 확장하도록 요구한다는 점에 유의하십시오.

td가 주어졌을 때 TD를 확장하려면 다음 단계를 실행합니다:
  1. [WOT-TD]의 TD 기본값 표에 있는 각 항목에 대해, 해당 용어가 td에 정의되어 있지 않으면 [WOT-TD]에 지정된 기본값으로 용어 정의를 추가합니다.

5.3 Thing Description 검증하기

[WOT-TD] 명세는 TD가 어떻게 검증되어야 하는지 정의합니다. 따라서 이 API는 ThingDescription 객체가 매개변수로 사용되기 전에 검증될 것을 기대합니다. 이 명세는 기본적인 TD 검증을 다음과 같이 정의합니다.

td가 주어졌을 때 TD를 검증하려면 다음 단계를 실행합니다:
  1. td에 대한 JSON Schema 검증이 실패하면 "TypeError"를 throw하고 중지합니다.
편집자 주: 기본값 처리

필수 필드의 기본값을 채우기 위한 추가 단계가 추가될 수 있습니다.

6. WOT 네임스페이스

WoT API object를 싱글턴으로 정의하며, 적합성 클래스별로 그룹화된 API 메서드를 포함합니다.

WebIDL[SecureContext, Exposed=(Window,Worker)]
namespace WOT {
  // methods defined in UA conformance classes
};

6.1 consume() 메서드

WebIDLpartial namespace WOT {
  Promise<ConsumedThing> consume(ThingDescription td);
};
WoT Consumer 적합성 클래스에 속합니다. td 인수를 기대하며, Thing을 조작하기 위한 클라이언트 인터페이스를 나타내는 ConsumedThing 객체로 resolve되는 Promise를 반환합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. td에서 구성된 새 ConsumedThing 객체를 thing으로 둡니다.
  4. [WOT-TD] 및 [WOT-PROTOCOL-BINDINGS]에 설명된 대로 td를 내부 검사하는 것에 기반하여 WoT Interactions를 설정합니다. 기본 플랫폼에 Protocol Bindings 초기화를 요청합니다.
    편집자 주

    구현은 WoT interactions를 구현하기 위해 Protocol Bindings를 사용하는 방식의 복잡성을 캡슐화합니다. 향후 그 요소들이 표준화될 수 있습니다.

  5. promisething으로 Resolve합니다.
편집자 주

ConsumedThing을 구성하는 것과 consume() 메서드를 사용하는 것의 차이에 유의하십시오. 후자는 프로토콜 바인딩도 초기화하지만, 단순히 구성된 객체는 호출되기 전까지 WoT Interactions가 초기화되지 않습니다.

6.2 produce() 메서드

WebIDLtypedef object ExposedThingInit;

partial namespace WOT {
  Promise<ExposedThing> produce(ExposedThingInit init);
};
WoT Producer 적합성 클래스에 속합니다. init 인수를 기대하며, 요청 핸들러를 정의할 수 있는 능력, 즉 서버 인터페이스로 ConsumedThing을 확장하는 ExposedThing 객체로 resolve되는 Promise를 반환합니다. init 객체는 ExposedThingInit 타입의 인스턴스입니다. 구체적으로, ExposedThingInit 값은 ExposedThing의 초기화에 사용되는 딕셔너리이며, [WOT-ARCHITECTURE]에 설명된 Partial TD를 나타냅니다. 따라서 이는 Thing Description과 같은 구조를 가지지만 일부 정보를 생략할 수 있습니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. init으로 구성된 새 ExposedThing 객체를 thing으로 둡니다.
  4. promisething으로 Resolve합니다.

6.2.1 ExposedThingInit 확장하기

init이 주어졌을 때 ExposedThingInit을 확장하고 유효한 td를 결과로 얻으려면 다음 단계를 실행합니다:
  1. init에 대해 ExposedThingInit 검증을 실행합니다. 실패하면 SyntaxErrorthrow하고 중지합니다.
  2. init이 주어졌을 때 clone을 실행한 결과를 td로 둡니다.
  3. td.["securityDefinitions"]의 각 scheme에 대해, 적어도 하나의 Protocol Binding에서 지원되는지 기본 플랫폼에 확인을 요청합니다. 지원되지 않으면 td에서 scheme을 제거합니다.
  4. td.["security"]가 td.["securityDefinitions"]에 존재하지 않으면, td에서 security를 제거합니다.
  5. td.properties, td.actions 및 td.events의 각 affordance에 대해 다음 하위 단계를 실행합니다:
    1. affordance.forms의 각 form에 대해:
      1. form.contentType이 런타임에서 유효한 것으로 인식되지 않으면 form에서 contentType을 제거합니다.
      2. form.href가 알 수 없는 스키마를 가지면 form에서 href를 제거합니다.
      3. form.href가 절대값이고 그 authority가 런타임에서 유효한 것으로 인식되지 않으면 form에서 href를 제거합니다.
      4. form.href가 다른 ExposedThings에서 이미 사용 중이면, form에서 href를 제거합니다.
  6. TD JSON Schema에 따라 td에서 누락된 필수 속성을 검색합니다.
    편집자 주

    편집자들은 이 단계가 모호하다고 봅니다. 다음 반복에서 개선되거나 제거될 것입니다.

  7. missing 속성에 대해 다음 하위 단계를 실행합니다:
    1. missingtitle이면 런타임에서 고유한 이름을 생성하고 title에 할당합니다.
    2. missing@context이면 최신 지원 Thing Description 컨텍스트 URI를 할당합니다.
    3. missinginstance이면 문자열 1.0.0을 할당합니다.
    4. missingforms이면 사용 가능한 Protocol Bindings와 콘텐츠 타입 인코더를 사용하여 Forms 목록을 생성합니다. 그런 다음 얻은 목록을 forms에 할당합니다.
    5. missingsecurity이면 securityDefinitions 필드에서 첫 번째로 지원되는 SecurityScheme의 레이블을 할당합니다. SecurityScheme을 찾을 수 없으면 nosec이라는 NoSecurityScheme을 생성하고 문자열 nosecsecurity에 할당합니다.
      Issue 1

      security 값을 적절히 생성하는 방법에 대한 논의는 아직 열려 있습니다. 이슈 #299를 참조하십시오

    6. missinghref이면, href가 없는 부분 FormformStub으로 정의합니다. formStub의 요구사항을 만족하는 첫 번째 Protocol Binding을 사용하여 유효한 url을 생성합니다. urlhref에 할당합니다. 만족하는 Protocol Binding을 찾을 수 없으면 td에서 formStub을 제거합니다.
    7. missingvalue를 값으로 사용하여 td에 추가합니다
  8. td에 대해 TD 검증을 실행합니다. 실패하면 오류를 다시 throw하고 중지합니다
  9. td를 반환합니다

6.2.2 ExposedThingInit 검증하기

init이 주어졌을 때 ExposedThingInit을 검증하려면 다음 단계를 실행합니다:
  1. TD JSON Schema를 구문 분석하고 exposedThingInitSchema라는 객체에 로드합니다
  2. optionaltitle, @context, instance, forms, security, 및 href 문자열을 포함하는 목록으로 둡니다.
  3. exposedThingInitSchema의 각 속성 및 하위 속성 keyrequired와 같으면 다음 단계를 실행합니다:
    1. key valueArray이면 optional의 요소와 같은 모든 요소를 제거합니다
    2. key valuestring이면, valueoptional의 요소 중 하나와 같을 때 exposedThingInitSchema에서 key를 제거합니다
  4. initexposedThingInitSchema가 주어졌을 때 JSON Schema로 객체 검증을 수행한 결과를 반환합니다.
    편집자 주

    JSON Schema로 객체 검증 단계는 아직 논의 중입니다. 현재 이 명세는 JSONSchema의 검증 프로세스를 참조합니다. initexposedThingInitSchema로 검증할 때는 이 문서를 따르십시오. 작업 그룹은 대체 형식적 접근 방식을 평가하고 있다는 점에 유의하십시오.

6.3 discover() 메서드

WebIDLpartial namespace WOT {
  Promise<ThingDiscoveryProcess> discover(optional ThingFilter filter = {});
};
WoT Discovery 적합성 클래스에 속합니다. 타입이 ThingFilter인 선택적 filter 인수와 일치하는 Thing Description들에 대해 ThingDescription 객체를 제공할 발견 프로세스를 시작합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. 구현이 발견을 지원하지 않으면, promise NotSupportedErrorreject하고 중지합니다.
  4. ThingDiscoveryProcess 객체를 discovery로 둡니다.
  5. discovery.[[filter]]filter로 설정합니다.
  6. discovery.[[url]]undefined로 설정합니다.
  7. 구현이 일반적으로 필터를 지원하지 않고 filterundefined 또는 null이 아니면, promise NotSupportedErrorreject하고 중지합니다.
  8. 기본 플랫폼에서 발견을 시작할 수 없으면, promiseOperationErrorreject하고 중지합니다.
  9. 스크립트가 접근할 수 있고 WoT Runtime에 프로비저닝되어 지원되는 임의의 수단으로 발견 프로세스를 시작하도록 기본 플랫폼에 요청하고, discovery를 전달합니다.
  10. promisediscoveryResolve합니다.

6.4 exploreDirectory() 메서드

WebIDLpartial namespace WOT {
  Promise<ThingDiscoveryProcess> exploreDirectory(USVString url,
      optional ThingFilter filter = {});
};
WoT Discovery 적합성 클래스에 속합니다. TD Directory URL이 주어지면, 타입이 ThingFilter인 선택적 filter 인수와 일치하는 Thing Description들에 대해 ThingDescription 객체를 제공할 발견 프로세스를 시작합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. 구현이 디렉터리 발견을 지원하지 않으면, promise NotSupportedErrorreject하고 중지합니다.
  4. ThingDiscoveryProcess 객체를 discovery로 둡니다.
  5. discovery.[[url]]url로 설정합니다.
  6. discovery.[[filter]]filter로 설정합니다.
  7. 디렉터리 발견 프로세스를 시작하도록 기본 플랫폼에 요청합니다.
    참고

    이는 발견 알고리즘의 더 자세한 내용을 위한 자리 표시자입니다. 구현은 [WOT-DISCOVERY] 및 [WOT-PROTOCOL-BINDINGS] 명세에 설명된 절차를 따라야 합니다. 일부 규범 단계가 아래에 표시되어 있습니다.

    1. url TD Directory가 아니거나, 기본 구현이 url로 표시된 Protocol Binding을 지원할 수 없으면, promise NotSupportedErrorreject하고 이 단계를 종료합니다.
    2. 구현이 일반적으로 필터를 지원하지 않고 filterundefined 또는 null이 아니면, promise NotSupportedErrorreject하고 중지합니다.
    3. discovery가 주어졌을 때 발견 프로세스를 실행합니다.
      참고

      이 지점부터 오류는 error에만 기록되며, 더 이상 promise에 영향을 주지 않습니다.

  8. promisediscoveryResolve합니다.

6.5 requestThingDescription() 메서드

WebIDLpartial namespace WOT {
  Promise<ThingDescription> requestThingDescription(USVString url);
};
WoT Discovery 적합성 클래스에 속합니다. 지정된 URL에서 Thing Description을 요청합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. 구현이 Thing Description 가져오기를 지원하지 않으면, promise NotSupportedErrorreject하고 중지합니다.
  4. url로 지정된 Protocol Binding을 사용하여 Thing Description을 가져오도록 기본 플랫폼에 요청한 결과를 td로 둡니다. td 가져오기가 실패하면, promiseNotFoundErrorreject하고 중지합니다.
  5. promisetdResolve합니다.

7. 상호작용 데이터 처리

Web of Things (WoT) Thing Description 1.1 명세에 지정된 것처럼, WoT interactionsDataSchema를 확장하고, 상호작용을 위해 그중 하나가 선택되는 여러 가능한 Form을 포함합니다. Form은 데이터를 설명하기 위한 contentType을 포함합니다. 특정 콘텐츠 타입의 경우, DataSchemaJSON Schema를 기반으로 정의되어, 이러한 내용을 JavaScript 타입으로 표현하고 최종적으로 데이터에 대한 범위 제약을 설정할 수 있게 합니다.

7.1 InteractionInput 타입

WebIDLtypedef any DataSchemaValue;
typedef (ReadableStream or DataSchemaValue) InteractionInput;

WoT Consumer 적합성 클래스에 속하며, 애플리케이션 스크립트가 UA에 제공하는 WoT Interaction 데이터를 나타냅니다.

DataSchemaValue는 [WoT-TD]에 정의된 DataSchema에서 허용되는 ECMAScript 값입니다. 가능한 값은 null, boolean, number, string, array, 또는 object 타입이어야 MUST 합니다.

ReadableStreamThing DescriptionDataSchema가 없고, 스트림으로 표현할 수 있는 FormcontentType만 있는 WoT Interactions에 사용되도록 의도됩니다.

실제로는, ECMAScript 값Thing Description에 정의된 DataSchema가 있거나, 구현이 Thing Description에 정의된 FormcontentType에 매핑할 수 있는 WoT Interactions에 사용할 수 있습니다.

이 문서의 알고리즘은 입력 데이터가 WoT Interactions에서 정확히 어떻게 사용되는지를 지정합니다.

7.2 InteractionOutput 인터페이스

WoT Consumer 적합성 클래스에 속합니다. InteractionOutput 객체는 항상 구현에 의해 생성되며, WoT Interactions에서 반환된 데이터를 애플리케이션 스크립트에 노출합니다.

이 인터페이스는 대다수 IoT 사용 사례를 포괄해야 하는 편의 함수, 즉 value() 함수를 노출합니다. 그 구현은 데이터를 검사하고, DataSchema를 준수하면 이를 구문 분석하며, 그렇지 않으면 조기에 실패하여 기본 스트림을 방해하지 않은 채 남겨 둡니다. 따라서 애플리케이션 스크립트가 직접 스트림 읽기를 시도하거나, 데이터를 ArrayBuffer로 처리할 수 있습니다.

WebIDL[SecureContext, Exposed=(Window,Worker)]
interface InteractionOutput {
  readonly attribute ReadableStream? data;
  readonly attribute boolean dataUsed;
  readonly attribute Form? form;
  readonly attribute DataSchema? schema;
  Promise<ArrayBuffer> arrayBuffer();
  Promise<DataSchemaValue> value();
};

data 속성은 WoT Interactions의 원시 payload를 ReadableStream으로 나타내며, 초기값은 null입니다.

dataUsed 속성은 데이터 스트림이 disturbed되었는지를 알려 줍니다. 초기값은 false입니다.

form 속성은 이 WoT Interaction을 위해 Thing Description에서 선택된 Form을 나타내며, 초기값은 null입니다.

schema 속성은 payload의 DataSchema ([WoT-TD]에 정의됨)를 JSON 객체로 나타내며, 초기값은 null입니다.

[[value]] 내부 슬롯은 WoT Interaction의 구문 분석된 값을 나타내며, 초기값은 undefined입니다(null은 유효한 값이라는 점에 유의하십시오).

7.2.1 value() 함수

WoT Interaction이 반환한 데이터를 구문 분석하고, 존재한다면 상호작용 DataSchema가 설명한 타입의 값, 또는 상호작용 FormcontentType이 설명한 타입의 값을 반환합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. this.[[value]]undefined가 아니면, promise를 그 값으로 resolve하고 중지합니다.
  3. this.data ReadableStream이 아니거나, dataUsedtrue이거나, formobject가 아니거나, schema 또는 그 typenull 또는 undefined이면, promise NotReadableErrorreject하고 중지합니다.
  4. form.contentTypeapplication/json이 아니고, form.contentType에서 [JSON-SCHEMA]로의 매핑이 Protocol Bindings에 없으면, promise NotSupportedErrorreject하고 중지합니다.
  5. data에서 reader를 가져온 결과를 reader로 둡니다. 예외가 throw되면, promise를 그 예외로 reject하고 중지합니다.
  6. readerdata에서 모든 바이트를 읽은 결과를 bytes로 둡니다.
  7. dataUsedtrue로 설정합니다.
  8. form.contentTypeapplication/json이 아니고, form.contentType에서 [JSON-SCHEMA]로의 매핑이 Protocol Bindings에 있으면, 해당 매핑으로 bytes를 변환합니다.
  9. bytes에 대해 parse JSON from bytes를 실행한 결과를 json으로 둡니다. 그것이 throw하면, promise를 그 예외로 reject하고 중지합니다.
  10. jsonschema에 대해 check data schema를 실행한 결과로 [[value]]를 설정합니다. 그것이 throw하면, promise를 그 예외로 reject하고 중지합니다.
  11. promise[[value]]Resolve합니다.

7.2.2 arrayBuffer() 함수

호출되면 다음 단계를 실행해야 MUST 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. data ReadableStream이 아니거나 dataUsedtrue이면, promise NotReadableErrorreject하고 중지합니다.
  3. data에서 reader를 가져온 결과를 reader로 둡니다. 예외가 throw되면, promise를 그 예외로 reject하고 중지합니다.
  4. readerdata에서 모든 바이트를 읽은 결과를 bytes로 둡니다.
  5. dataUsedtrue로 설정합니다.
  6. 내용이 bytes인 새 ArrayBufferarrayBuffer로 둡니다. 그것이 throw하면, promise를 그 예외로 reject하고 중지합니다.
  7. promisearrayBufferResolve합니다.

7.2.3 데이터 스키마 확인 알고리즘

payloadschema에 대해 데이터 스키마 확인 단계를 실행하려면,
  1. schema.typetype으로 둡니다.
  2. type"null"이고 payloadnull이 아니면 TypeError를 throw하고 중지합니다. 그렇지 않으면 null을 반환합니다.
  3. type"boolean"이고 payload가 falsy 값이거나 바이트 길이가 0이면 false를 반환하고, 그렇지 않으면 true를 반환합니다.
  4. type"integer" 또는 "number"이면,
    1. payload가 number가 아니면 TypeError를 throw하고 중지합니다.
    2. form.minimum이 정의되어 있고 payload가 더 작거나, form.maximum이 정의되어 있고 payload가 더 크면, RangeError를 throw하고 중지합니다.
  5. type"string"이면 payload를 반환합니다.
  6. type"array"이면 다음 하위 단계를 실행합니다:
    1. payload가 array가 아니면 TypeError를 throw하고 중지합니다.
    2. form.minItems가 정의되어 있고 payload.length가 그것보다 작거나, form.maxItems가 정의되어 있고 payload.length가 그것보다 크면, RangeError를 throw하고 중지합니다.
    3. payload의 각 요소 itemschema.items에 대해 데이터 스키마 확인 단계를 실행하여 얻은 항목의 배열을 payload로 둡니다. 어느 단계에서든 throw하면, 해당 예외를 다시 throw하고 중지합니다.
  7. type"object"이면 다음 하위 단계를 실행합니다:
    1. payload 또는 schema.propertiesobject가 아니면, TypeError를 throw하고 중지합니다.
    2. payload의 각 key에 대해:
      1. payload[key]를 prop으로 둡니다.
      2. interaction.properties[key]를 propSchema로 둡니다.
      3. proppropSchema에 대해 데이터 스키마 확인 단계를 실행한 결과를 prop으로 둡니다. 이것이 throw하면, 해당 예외를 다시 throw하고 중지합니다.
    3. schema.required가 array이면 이를 required로 두고, 그렇지 않으면 빈 배열로 둡니다.
    4. required의 각 key에 대해, keypayload에 없으면 SyntaxError를 throw하고 중지합니다.
  8. payload를 반환합니다.

7.2.4 상호작용 요청 생성 알고리즘

주어진 ConsumedThing 객체 thing에 대해, source, formschema가 주어졌을 때 상호작용 요청 생성을 하려면 다음 단계를 실행합니다:
  1. InteractionOutput 객체를 idata로 둡니다.
  2. idata.formform으로 설정하고, idata.schemaschema로 설정하며, |idata.datanull로 설정하고 idata.[[value]]undefined로 설정합니다.
  3. source ReadableStream 객체이면, idata.datasource로 두고, idata를 반환한 뒤 중지합니다.
  4. schema와 그 type이 정의되어 있고 null이 아니면, 다음 하위 단계를 실행합니다:
    1. type"null"이고 source"null"이 아니면, TypeError를 throw하고 중지합니다.
    2. type"boolean"이고 source가 falsy 값이면 idata.[[value]]false로 설정하고, 그렇지 않으면 true로 설정합니다.
    3. type"integer" 또는 "number"이고 source가 number가 아니거나, form.minimum이 정의되어 있고 source가 더 작거나, form.maximum이 정의되어 있고 source가 더 크면, RangeError를 throw하고 중지합니다.
    4. type"string"이고 source가 string이 아니면, source가 주어졌을 때 serialize JSON to bytes를 실행한 결과를 idata.[[value]]로 둡니다. 그것이 실패이면, SyntaxError를 throw하고 중지합니다.
    5. type"array"이면 다음 하위 단계를 실행합니다:
      1. source가 array가 아니면, TypeError를 throw하고 중지합니다.
      2. source의 길이를 length로 둡니다.
      3. form.minItems가 정의되어 있고 length가 그것보다 작거나, form.maxItems가 정의되어 있고 length가 그것보다 크면, RangeError를 throw하고 중지합니다.
      4. source의 각 item에 대해, schema.itemsitemschema로 두고, item, formitemschema가 주어졌을 때 상호작용 요청 생성 단계를 실행한 결과를 item으로 둡니다. 이것이 throw하면, 해당 예외를 다시 throw하고 중지합니다.
      5. data.[[value]]source로 설정합니다.
    6. type"object"이면 다음 하위 단계를 실행합니다:
      1. source가 object가 아니면, TypeError를 throw하고 중지합니다.
      2. schema.properties가 object가 아니면, TypeError를 throw하고 중지합니다.
      3. source의 각 key에 대해,
        1. source[key]를 value로 둡니다.
        2. properties.interactions[key]를 propschema로 둡니다.
        3. value, formpropschema에 대해 상호작용 요청 생성 단계를 실행한 결과를 value로 둡니다. 이것이 throw하면, 해당 예외를 다시 throw하고 중지합니다.
      4. schema.required가 array이면, required의 각 item에 대해 itemsource의 속성 이름인지 확인합니다. itemsource에서 발견되지 않으면, SyntaxError를 throw하고 중지합니다.
      5. data.[[value]]source로 설정합니다.
  5. idata.[[value]] internal slotunderlying source로 사용하여 생성된 새 ReadableStream으로 idata.data를 설정합니다.
  6. idata를 반환합니다.

7.2.5 상호작용 응답 구문 분석 알고리즘

주어진 ConsumedThing 객체 thing에 대해, response, formschema가 주어졌을 때 상호작용 응답 구문 분석을 하려면 다음 단계를 실행합니다:
  1. InteractionOutput 객체를 result로 둡니다.
  2. result.schemaschema로 둡니다.
  3. result.formform으로 둡니다.
  4. response의 payload 데이터를 underlying source로 하는 새 ReadableStream으로 result.data를 둡니다.
  5. result.dataUsedfalse로 둡니다.
  6. result를 반환합니다.

7.3 InteractionInputInteractionOutput 사용하기

다음 그림에 나타난 것처럼, InteractionOutput 인터페이스는 구현이 스크립트에 데이터를 제공할 때마다 사용되며, InteractionInput은 스크립트가 구현에 데이터를 전달할 때 사용됩니다.

그림 1 데이터를 읽을 때 사용되는 데이터 구조

ConsumedThing이 데이터를 읽을 때, 구현으로부터 InteractionOutput 객체로 데이터를 받습니다.

ExposedThing 읽기 핸들러는 읽은 데이터를 구현에 InteractionInput으로 제공합니다.

그림 2 데이터를 쓸 때 사용되는 데이터 구조

ConsumedThing이 데이터를 쓸 때, 구현에 InteractionInput으로 데이터를 제공합니다.

ExposedThing 쓰기 핸들러는 구현으로부터 데이터를 InteractionOutput 객체로 받습니다.

그림 3 Action을 호출할 때 사용되는 데이터 구조

ConsumedThingAction을 호출할 때, 매개변수를 InteractionInput으로 제공하고, Action의 출력을 InteractionOutput 객체로 받습니다.

ExposedThing 액션 핸들러는 구현으로부터 인수를 InteractionOutput 객체로 받고, Action 출력을 InteractionInput으로 구현에 제공합니다.

7.4 오류 처리

이 API의 알고리즘은 애플리케이션 스크립트에 보고될 오류를 정의합니다.

다른 통신 종단에 보고되는 오류는 Protocol Bindings에 의해 매핑되고 캡슐화됩니다.

그림 4 WoT 상호작용의 오류 처리
편집자 주

이 주제는 Issue #200에서 아직 논의 중입니다. 스크립트 오류를 프로토콜 오류로, 또는 그 반대로 매핑할 때 일관성을 보장하기 위해 표준화된 오류 매핑이 필요합니다. 특히, 알고리즘이 "Protocol Bindings에서 받은 오류"라고 말할 때, 이는 명시적인 오류 매핑 알고리즘으로 분리될 것입니다. 현재는 구현에 의해 캡슐화됩니다.

8. ConsumedThing 인터페이스

Thing을 조작하기 위한 클라이언트 API를 나타냅니다. WoT Consumer 적합성 클래스에 속합니다.

WebIDL[SecureContext, Exposed=(Window,Worker)]
interface ConsumedThing {
  constructor(ThingDescription td);
  Promise<InteractionOutput> readProperty(DOMString propertyName,
                              optional InteractionOptions options = {});
  Promise<PropertyReadMap> readAllProperties(
                              optional InteractionOptions options = {});
  Promise<PropertyReadMap> readMultipleProperties(
                              sequence<DOMString> propertyNames,
                              optional InteractionOptions options = {});
  Promise<undefined> writeProperty(DOMString propertyName,
                              InteractionInput value,
                              optional InteractionOptions options = {});
  Promise<undefined> writeMultipleProperties(
                              PropertyWriteMap valueMap,
                              optional InteractionOptions options = {});
  /*Promise<undefined> writeAllProperties(
                              PropertyWriteMap valueMap,
                              optional InteractionOptions options = {});*/
  Promise<InteractionOutput> invokeAction(DOMString actionName,
                              optional InteractionInput params = {},
                              optional InteractionOptions options = {});
  Promise<Subscription> observeProperty(DOMString name,
                              InteractionListener listener,
                              optional ErrorListener onerror,
                              optional InteractionOptions options = {});
  Promise<Subscription> subscribeEvent(DOMString name,
                              InteractionListener listener,
                              optional ErrorListener onerror,
                              optional InteractionOptions options = {});
  ThingDescription getThingDescription();
};

dictionary InteractionOptions {
  unsigned long formIndex;
  object uriVariables;
  any data;
};

[SecureContext, Exposed=(Window,Worker)]
interface Subscription {
  readonly attribute boolean active;
  Promise<undefined> stop(optional InteractionOptions options = {});
};

[SecureContext, Exposed=(Window,Worker)]
interface PropertyReadMap {
  readonly maplike<DOMString, InteractionOutput>;
};

[SecureContext, Exposed=(Window,Worker)]
interface PropertyWriteMap {
  readonly maplike<DOMString, InteractionInput>;
};

callback InteractionListener = undefined(InteractionOutput data);
callback ErrorListener = undefined(Error error);
편집자 주: writeAllProperties 메서드는 어디에 있는가?

writeAllProperties() 메서드는 아직 논의 중입니다. 그동안은 대신 writeMultipleProperties() 메서드를 사용하십시오.

8.1 ConsumedThing의 내부 슬롯

ConsumedThing 객체에는 다음 내부 슬롯이 있습니다:

내부 슬롯 초기값 설명(비규범적)
[[td]] null ConsumedThingThing Description입니다.
[[activeSubscriptions]] {} Event를 나타내는 string 이름을 key로 하고, valueSubscription 객체인 ordered map입니다.
[[activeObservations]] {} Property를 나타내는 string 이름을 key로 하고, valueSubscription 객체인 ordered map입니다.

8.2 ConsumedThing 구성하기

가져온 Thing Description을 JSON 객체로 얻은 뒤, ConsumedThing 객체를 생성할 수 있습니다.

ThingDescription tdConsumedThing을 생성하려면 다음 단계를 실행합니다:
  1. td에 대해 TD 검증 단계를 실행합니다. 실패하면 SyntaxErrorthrow하고 중지합니다.
  2. td에 대해 TD 확장 단계를 실행합니다. 실패하면 오류를 다시 throw하고 중지합니다.
  3. ConsumedThing 객체를 thing으로 둡니다.
  4. thing 내부 슬롯 [[td]]td로 설정합니다.
  5. thing을 반환합니다.

8.3 getThingDescription() 메서드

ConsumedThing 객체의 [[td]]를 반환하며, 이는 해당 ConsumedThingThing Description을 나타냅니다. 애플리케이션은 그와 상호작용하기 전에 그 기능을 내부 검사하기 위해 [[td]]에 저장된 Thing 메타데이터를 참조할 수 있습니다.

8.4 readProperty() 메서드

Property 값을 읽습니다. 인수로 propertyName과 선택적으로 options를 받습니다. 이 메서드는 InteractionOutput 객체로 표현된 Property 값으로 resolve되거나 오류 시 reject되는 Promise를 반환합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. [[td]].properties.propertyNameinteraction으로 둡니다.
  4. interactionundefined이면, promiseNotFoundErrorreject하고 중지합니다.
  5. option.formIndex가 정의되어 있으면, interaction.forms 배열에서 formIndex와 연관된 Formform으로 둡니다. 그렇지 않으면 interaction.forms에서 opreadpropertyForm 중 구현이 선택한 것을 form으로 둡니다.
  6. form이 failure이면, promiseSyntaxErrorreject하고 중지합니다.
  7. formoptions.uriVariables에 지정된 선택적 URI 템플릿을 사용하여 propertyName Property의 값을 가져오도록 기본 플랫폼(Protocol Bindings를 통해)에 요청합니다.
  8. 요청이 실패하면, Protocol Bindings에서 받은 오류로 promisereject하고 중지합니다.
  9. 요청에 대해 받은 응답을 response로 둡니다.
  10. response, forminteraction에 대해 상호작용 응답 구문 분석을 실행한 결과를 data로 둡니다. 실패하면 promiseSyntaxErrorreject하고 중지합니다.
  11. promisedataResolve합니다.

8.5 readMultipleProperties() 메서드

한 번의 요청으로 여러 Property 값을 읽습니다. 인수로 propertyNames와 선택적으로 options를 받습니다. 이 메서드는 propertyNames의 키를 이 알고리즘이 반환한 값에 매핑하는 PropertyReadMap 객체로 resolve되는 Promise를 반환합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. option.formIndex가 정의되어 있으면, [[td]].forms 배열에서 formIndex와 연관된 Formform으로 둡니다. 그렇지 않으면 [[td]].forms 배열에서 opreadmultiplepropertiesForm 중 구현이 선택한 것을 form으로 둡니다.
  4. form이 failure이면, promiseSyntaxErrorreject하고 중지합니다.
  5. result를 객체로 두고, propertyNames의 각 문자열 name에 대해 키가 name이고 값이 null인 속성을 추가합니다.
  6. formoptions uriVariables에 지정된 선택적 URI 템플릿을 사용하여 propertyNames로 주어진 Property 값을 가져오도록 기본 플랫폼(Protocol Bindings를 통해)에 요청합니다.
  7. 이를 Protocol Bindings로 단일 요청으로 수행할 수 없으면, promise NotSupportedErrorreject하고 중지합니다.
  8. 응답을 처리하고 result의 각 key에 대해 다음 하위 단계를 실행합니다:
    1. result[key]를 value로 둡니다.
    2. this.[[td]].properties[key]를 schema로 둡니다.
    3. value, formschema에 대해 상호작용 응답 구문 분석을 실행한 결과를 property로 둡니다.
  9. 위 단계가 어느 시점에서든 throw하면, promise를 그 예외로 reject하고 중지합니다.
  10. promiseresultResolve합니다.

8.6 readAllProperties() 메서드

한 번의 요청으로 Thing의 모든 속성을 읽습니다. 선택적 인수로 options를 받습니다. 이 메서드는 Property 이름의 키를 이 알고리즘이 반환한 값에 매핑하는 PropertyReadMap 객체로 resolve되는 Promise를 반환합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. subscription.[[interaction]].formsforms로 둡니다.
  4. formsundefined이면, promiseSyntaxErrorreject하고 중지합니다.
  5. option.formIndexundefined가 아니고 forms.length보다 작으면, subscription.[[form]]forms.[formIndex]로 설정합니다.
  6. 그렇지 않으면 forms에서 op"readallproperties"Form 중 구현이 선택한 것을 subscription.[[form]]으로 설정합니다.
  7. subscription.[[form]]이 failure이면, promiseSyntaxErrorreject하고 중지합니다.
  8. formoptions.uriVariables의 선택적 URI 템플릿이 주어졌을 때, TD에서 모든 Property 정의를 가져오도록 Protocol Bindings를 사용하여 기본 플랫폼에 요청합니다.
  9. ThingProtocol Bindings로 이를 단일 요청으로 수행할 수 없으면, promise NotSupportedErrorreject하고 중지합니다.
  10. 요청이 실패하면, Protocol Bindings에서 받은 오류로 promisereject하고 중지합니다.
  11. 응답을 처리하고, 응답에서 얻은 키와 값을 가진 객체를 result로 둡니다.
  12. 응답을 처리하고 result의 각 key에 대해 다음 하위 단계를 실행합니다:
    1. result[key]를 value로 둡니다.
    2. this.[[td]].properties[key]를 schema로 둡니다.
    3. value, formschema에 대해 상호작용 응답 구문 분석을 실행한 결과를 property로 둡니다.
  13. promiseresultResolve합니다.

8.7 writeProperty() 메서드

단일 Property를 씁니다. 인수로 propertyName, value와 선택적으로 options를 받습니다. 이 메서드는 성공 시 resolve되고 실패 시 reject되는 Promise를 반환합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. this.[[td]].properties[propertyName]를 interaction으로 둡니다.
  4. interactionundefined이면, promiseNotFoundErrorreject하고 중지합니다.
  5. option.formIndexundefined가 아니면, interaction.forms 배열에서 formIndex와 연관된 Formform으로 둡니다. 그렇지 않으면 interaction.forms에서 opwritepropertyForm 중 구현이 선택한 것을 form으로 둡니다.
  6. form이 failure이면, promiseSyntaxErrorreject하고 중지합니다.
  7. value, form interaction이 주어졌을 때 상호작용 요청 생성 단계를 실행한 결과를 data로 둡니다. 그것이 throw하면, promise를 그 예외로 reject하고 중지합니다.
  8. dataoptions의 선택적 URI 템플릿 uriVariables를 사용하여 propertyName으로 주어진 Property를 쓰도록 기본 플랫폼(Protocol Bindings를 통해)에 요청합니다.
  9. 요청이 실패하면, Protocol Bindings에서 받은 오류로 promisereject하고 중지합니다.
  10. 그렇지 않으면 promiseresolve합니다.
편집자 주

Issue #193에서 논의된 것처럼, 설계 결정은 쓰기 상호작용이 쓰인 값(선택적)이 아니라 성공 또는 오류만 반환한다는 것입니다. TD는 정밀도와 대체 형식을 포함하여 Property 값의 스키마를 포착해야 합니다. 상호작용에서 반환값이 예상되는 경우, Property 대신 Action을 사용해야 합니다.

8.8 writeMultipleProperties() 메서드

한 번의 요청으로 여러 Property 값을 씁니다. 인수로 Property 이름을 키로 하고 Property 값을 값으로 하는 객체인 properties와 선택적으로 options를 받습니다. 이 메서드는 성공 시 resolve되고 실패 시 reject되는 Promise를 반환합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. option.formIndex가 정의되어 있으면, [[td]].forms 배열에서 formIndex와 연관된 Formform으로 둡니다. 그렇지 않으면 [[td]].forms 배열에서 opwritemultiplepropertiesForm 중 구현이 선택한 것을 form으로 둡니다.
  4. form이 failure이면, promiseSyntaxErrorreject하고 중지합니다.
  5. properties 객체의 키를 요소로 하는 string 배열을 propertyNames로 둡니다.
  6. propertyNames의 각 name에 대해, this.[[td]].properties[name]를 property로 둡니다.
  7. propertynull 또는 undefined이거나 writeable이 아니면, promise NotSupportedErrorreject하고 중지합니다.
  8. result를 객체로 두고, propertyNames의 각 문자열 name에 대해 키가 name이고 값이 null인 속성을 추가합니다.
  9. schemas를 객체로 두고, propertyNames의 각 name에 대해 키가 name이고 값이 this.[[td]].properties[name]인 속성을 추가합니다.
  10. properties의 각 키 key에 대해, properties[key], formschema[key]의 값을 사용하여 상호작용 요청 생성 단계를 실행합니다. 어떤 name에 대해 throw하면, promise를 그 예외로 reject하고 중지합니다.
  11. properties에 제공된 각 Property를, options의 선택적 URI 템플릿 uriVariables와 함께 쓰도록 기본 플랫폼(Protocol Bindings를 통해)에 단일 요청을 보냅니다.
  12. ThingProtocol Bindings로 이를 단일 요청으로 수행할 수 없으면, promise NotSupportedErrorreject하고 중지합니다.
  13. 요청이 실패하면, Protocol Bindings에서 받은 오류를 반환하고 중지합니다.
  14. 그렇지 않으면 promiseresolve합니다.

8.9 observeProperty() 메서드

Property 값 변경 알림을 요청합니다. 인수로 propertyName, listener와 선택적으로 onerroroptions를 받습니다. 이 메서드는 성공 시 resolve되고 실패 시 reject되는 Promise를 반환합니다.
편집자 주

이 알고리즘은 각 Property마다 하나의 활성 Subscription만 허용합니다. 기존 Subscription이 활성 상태인 동안 새 Subscription이 만들어지면 런타임은 NotAllowedError를 throw합니다.

이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. ConsumedThing 객체의 참조를 thing으로 둡니다.
  2. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  3. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  4. listenerFunction이 아니면, promise TypeErrorreject하고 중지합니다.
  5. onerrornull이 아니고 Function이 아니면, promise TypeErrorreject하고 중지합니다.
  6. thing.[[activeObservations]][propertyName] [=map/exists]이면, promise NotAllowedErrorreject하고 중지합니다.
  7. 내부 슬롯이 다음과 같이 설정된 새 Subscription 객체를 subscription으로 둡니다:
    • subscription.[[type]]"property"로 둡니다.
    • subscription.[[name]]propertyName으로 둡니다.
    • subscription.[[interaction]][[td]].properties[propertyName]로 둡니다.
    • subscription.[[thing]]thing으로 둡니다.
    • subscription.[[interaction]].formsforms로 둡니다.
    • formsundefined이면, promise SyntaxErrorreject하고 중지합니다.
    • option.formIndexundefined가 아니고 forms.length보다 작으면, subscription.[[form]]forms.[formIndex]로 설정합니다.
    • 그렇지 않으면, forms에서 op"observeproperty"Form 중 구현이 선택한 것을 subscription.[[form]]으로 설정합니다.
    • subscription.[[form]]이 failure이면, promise SyntaxErrorreject하고 중지합니다.
    • subscription.[[interaction]]undefined이면, promise NotFoundErrorreject하고 중지합니다.
  8. formoptions의 선택적 URI 템플릿 uriVariables가 주어졌을 때, propertyName으로 식별되는 Property를 관찰하도록 기본 플랫폼에 요청합니다.
  9. 요청이 실패하면, Protocol Bindings에서 받은 오류로 promisereject하고 중지합니다.
  10. thing.[[activeObservations]][|propertyName]를 subscription으로 Set하고, promiseresolve합니다.
  11. 기본 플랫폼이 propertyNamekey로 하는 이 subscription에 대해 새 Propertyvalue가 포함된 알림을 감지할 때마다 다음 하위 단계를 실행합니다:
  12. 기본 플랫폼이 이 구독에 대한 오류를 감지할 때마다 다음 하위 단계를 실행합니다:
    • 오류가 복구 불가능하며 구독을 중지시키는 경우, subscription.activefalse로 설정하고 추가 알림을 억제합니다.
    • NetworkErrorerror로 두고, 그 message를 기본 오류 조건을 반영하도록 설정합니다.
    • onerrorFunction이면, error를 인수로 이를 호출합니다.

8.10 invokeAction() 메서드

Action 호출을 요청하고 결과를 반환합니다. 인수로 actionName, 선택적으로 params와 선택적으로 options를 받습니다. 이 메서드는 InteractionOutput 객체로 표현된 Action의 결과로 resolve되거나 오류로 reject되는 Promise를 반환합니다. 이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. this.[[td]].actions[actionName]를 interaction으로 둡니다.
  4. interactionobject가 아니면, promiseNotFoundErrorreject하고 중지합니다.
  5. subscription.[[interaction]].formsforms로 둡니다.
  6. formsundefined이면, promiseSyntaxErrorreject하고 중지합니다.
  7. option.formIndexundefined가 아니고 forms.length보다 작으면, subscription.[[form]]forms.[formIndex]로 설정합니다.
  8. 그렇지 않으면, forms에서 op"invokeaction"Form 중 구현이 선택한 것을 subscription.[[form]]으로 설정합니다.
  9. subscription.[[form]]이 failure이면, promiseSyntaxErrorreject하고 중지합니다.
  10. params, forminteraction에 대해 상호작용 요청 생성 단계를 실행한 결과를 args로 둡니다. 그것이 throw하면, promise를 그 예외로 reject하고 중지합니다.
  11. argsoptions.uriVariables가 주어졌을 때 actionName으로 식별되는 Action을 호출하도록 기본 플랫폼(Protocol Bindings를 통해)에 요청합니다.
  12. 요청이 로컬에서 실패하거나 네트워크를 통해 오류를 반환하면, Protocol Bindings에서 받은 오류로 promisereject하고 중지합니다.
  13. 응답에서 반환된 응답을 value로 둡니다.
  14. value, forminteraction으로 상호작용 응답 구문 분석을 실행한 결과를 result로 둡니다. 그것이 throw하면, promise를 그 예외로 reject하고 중지합니다.
  15. promiseresultResolve합니다.

8.11 subscribeEvent() 메서드

Event 알림 구독을 요청합니다. 인수로 eventName, listener와 선택적으로 onerroroptions를 받습니다. 이 메서드는 성공 또는 실패를 신호하기 위한 Promise를 반환합니다.
편집자 주

이 알고리즘은 각 Event마다 하나의 활성 Subscription만 허용합니다. 기존 Subscription이 활성 상태인 동안 새 Subscription이 만들어지면 런타임은 NotAllowedError를 throw합니다.

이 메서드는 다음 단계를 MUST 실행해야 합니다:
  1. ConsumedThing 객체의 참조를 thing으로 둡니다.
  2. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  3. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  4. listenerFunction이 아니면, promise TypeErrorreject하고 중지합니다.
  5. onerrornull이 아니고 Function이 아니면, promise TypeErrorreject하고 중지합니다.
  6. thing.[[activeSubscriptions]][eventName]이 exist하지 않으면, promise NotAllowedErrorreject하고 중지합니다.
  7. 내부 슬롯이 다음과 같이 설정된 새 Subscription 객체를 subscription으로 둡니다:
  8. [[form]], options.uriVariables에 지정된 선택적 URI 템플릿 및 options.data에 지정된 선택적 구독 데이터가 주어졌을 때, eventName으로 식별되는 Event를 구독하도록 Protocol Bindings를 통해 기본 플랫폼에 요청합니다.
  9. 요청이 실패하면, Protocol Bindings에서 받은 오류로 promisereject하고 중지합니다.
  10. eventNamething.[[activeSubscriptions]][eventName]의 subscription으로 Set합니다.
  11. promiseResolve합니다.
  12. 기본 플랫폼이 eventNamekey로 하는 Event subscription에 대한 알림을 감지할 때마다 다음 하위 단계를 실행합니다:
    1. Event와 함께 제공된 데이터, subscription.[[form]]subscription.[[interaction]]에 대해 상호작용 응답 구문 분석을 실행한 결과를 인수로 listener를 호출합니다.
  13. 기본 플랫폼이 eventNamekey로 하는 Event subscription에 대한 오류를 감지할 때마다 다음 하위 단계를 실행합니다:
    • 오류가 복구 불가능하며 구독을 중지시키는 경우, subscription.activefalse로 설정하고 추가 알림을 억제합니다.
    • NetworkErrorerror로 두고, 그 message를 기본 오류 조건을 반영하도록 설정합니다.
    • onerrorFunction이면, error를 인수로 이를 호출합니다.

8.12 InteractionOptions 딕셔너리

Thing Description에 따라 애플리케이션 스크립트에 노출되어야 하는 상호작용 옵션을 보관합니다.

formIndex 속성이 정의되어 있으면, 주어진 WoT 상호작용에 사용할 TD의 이 인덱스로 식별되는 Form 정의에 대한 애플리케이션 힌트를 나타냅니다. 구현은 상호작용을 수행할 때 이 인덱스의 Form을 사용해야 SHOULD 하지만, 인덱스를 찾을 수 없거나 유효하지 않으면 이 값을 재정의할 수 MAY 있습니다. 정의되어 있지 않으면, 구현은 주어진 Wot Interaction에 대해 TD에 나열된 출현 순서대로 Form 정의를 사용하려고 시도해야 SHOULD 합니다.

uriVariables 속성이 정의되어 있으면, WoT Interaction과 함께 사용될 URI 템플릿 변수를 나타내며, 이는 [WOT-TD]에 정의된 구문 분석된 JSON 객체로 표현됩니다.

편집자 주

URI 변수 지원은 Web of Things (WoT) Thing Description 1.1 명세에서 드러난, 이를 사용하는 기존 RESTful 엔드포인트를 설명할 수 있어야 한다는 필요에서 비롯되었습니다. 그러나 이러한 종류의 상호작용을 표현하기 위해 Action을 사용하고 URI 변수를 액션 매개변수로 모델링하는 Thing Description을 작성할 수 있어야 합니다. 그 경우 구현은 매개변수를 URI 변수로 직렬화할 수 있으므로, options 매개변수를 생략할 수 있습니다.

data 속성이 정의되어 있으면, 상호작용에 전달해야 하는 추가 opaque 데이터를 나타냅니다.

8.13 PropertyReadMap 타입

Property 이름을 해당 Property가 취할 수 있는 값을 나타내는 InteractionOutput 객체에 매핑한 map을 나타냅니다. 여러 Properties가 한 번에 관련되는 상호작용에서 property bag으로 사용됩니다.

8.14 PropertyWriteMap 타입

Property 이름을 해당 Property가 취할 수 있는 값을 나타내는 InteractionInput에 매핑한 map을 나타냅니다. 여러 Properties가 한 번에 관련되는 상호작용에서 property bag으로 사용됩니다.

8.15 InteractionListener 콜백

사용자가 제공하는 콜백으로, 타입이 InteractionOutput인 인수를 받으며, Property 변경을 관찰하고 Event 알림을 처리하는 데 사용됩니다. Events 구독은 WoT 상호작용이고 옵션이나 데이터까지 받을 수 있으므로, 소프트웨어 이벤트로 모델링되지 않습니다.

8.16 ErrorListener 콜백

사용자가 제공하는 콜백으로, 타입이 Error인 인수를 받으며, Protocol Bindings에서 애플리케이션으로 치명적 오류와 비치명적 오류를 전달하는 데 사용됩니다.

8.17 Subscription 인터페이스

Property 변경 및 Event 상호작용에 대한 구독을 나타냅니다.

active boolean 속성은 구독이 활성 상태인지, 즉 오류나 stop() 메서드 호출로 인해 중지되지 않았는지를 나타냅니다.

8.17.1 Subscription의 내부 슬롯

Subscription 객체에는 다음 내부 슬롯이 있습니다:
내부 슬롯 초기값 설명(비규범적)
[[type]] null Subscription이 어떤 WoT Interaction을 참조하는지 나타냅니다. 값은 "property", "event" 또는 null일 수 있습니다.
[[name]] null Property 또는 Event 이름입니다.
[[interaction]] null WoT interaction을 설명하는 Thing Description 조각입니다.
[[form]] null 구독과 연관된 Form입니다.
[[thing]] null 구독과 연관된 ConsumedThing입니다.

8.17.2 stop() 메서드

구독에 대한 알림 전달을 중지합니다. 선택적 매개변수 options를 받고 Promise를 반환합니다. 호출되면 이 메서드는 다음 단계를 실행해야 MUST 합니다:

  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. optionsformIndex가 정의되어 있으면, [[interaction]]forms 배열에서 formIndex와 연관된 FormunsubscribeForm으로 둡니다.
  4. 그렇지 않으면 [[form]]이 주어졌을 때 일치하는 구독 해제 form 찾기 알고리즘을 실행한 결과를 unsubscribeForm으로 둡니다.
  5. unsubscribeForm이 failure이면, promiseSyntaxErrorreject하고 중지합니다.
  6. [[type]] "property"이면, unsubscribeFormoptions의 선택적 URI 템플릿 uriVariables가 주어졌을 때, [[name]]으로 식별되는 Property 관찰을 중지하도록 Protocol Bindings를 통해 기본 플랫폼에 요청합니다.
  7. 그렇지 않고 [[type]]"event"이면, unsubscribeForm, options의 선택적 URI 템플릿 uriVariablesoptions.data에 지정된 선택적 구독 해제 데이터가 주어졌을 때, [[name]]으로 식별되는 Event 구독을 해제하도록 Protocol Bindings를 통해 기본 플랫폼에 요청합니다.
  8. 요청이 실패하면, Protocol Bindings에서 받은 오류로 promisereject하고 중지합니다.
  9. 그렇지 않으면:
  10. 기본 플랫폼이 이 구독에 대한 추가 알림을 받으면, 구현은 이를 조용히 억제해야 SHOULD 합니다.

8.17.3 구독 해제 Form 찾기

참고

이 알고리즘은 개발 중이며 비규범적입니다. 구현은 주어진 subscribe Form에 대해 일치하는 unsubscribe Form을 찾기 위해 다른 알고리즘을 선택할 수 MAY 있습니다.

Subscription 객체의 컨텍스트에서 subscribeForm이 주어졌을 때 일치하는 구독 해제 form 찾기를 하려면 다음 단계를 실행합니다:
  1. 빈 배열을 results로 둡니다.
  2. [[interaction]].forms의 각 form에 대해,
    1. form에 내부 슬롯 [[matchLevel]]을 추가하고, 그 값을 0으로 설정합니다.
    2. [[type]]"property"이면 form.op"unobserveproperty"인지, 또는 [[type]]"event"이면 form.op"unsubscribeevent"인지 확인하고,
      1. form의 내부 슬롯 [[matchLevel]]을 1로 설정하고, formresults에 추가합니다.
      2. form.href와 [[subscribeForm]].href same origin-domain이면, form.[[matchLevel]]을 증가시킵니다.
      3. form.contentType이 [[subscribeForm]]의 contentType과 같고 form.[[matchLevel]]이 2보다 크면, form.[[matchLevel]]을 증가시킵니다.
  3. results가 비어 있으면 null을 반환하고 이 단계를 종료합니다.
  4. 가장 높은 [[matchLevel]] 값을 가진 results의 첫 번째 form을 반환합니다.

8.18 ConsumedThing 예제

다음 예제는 URL로 TD를 가져오고, ConsumedThing을 생성하며, 메타데이터(title)를 읽고, property 값을 읽고, property 변경을 구독하고, WoT event를 구독한 다음, 구독을 해제하는 방법을 보여 줍니다.

예제 2: 데이터 값을 사용하는 Thing Client API 예제
try {
  let res = await fetch("https://tds.mythings.org/sensor11");
  let td = res.json();
  let thing = new ConsumedThing(td);
  console.log("Thing " + thing.getThingDescription().title + " consumed.");
} catch (e) {
  console.log("TD fetch error: " + e.message);
};

try {
  // subscribe to property change for “temperature”
  await thing.observeProperty("temperature", async (data) => {
    try {
      console.log("Temperature changed to: " + await data.value());
    } catch (error) {
      console.error("Cannot read the observed property temperature");
      console.error(error);
    }
  });
  // subscribe to the “ready” event defined in the TD
  await thing.subscribeEvent("ready", async (eventData) => {
    try {
      console.log("Ready; index: " + await eventData.value());
      // run the “startMeasurement” action defined by TD
      await thing.invokeAction("startMeasurement", { units: "Celsius" });
      console.log("Measurement started.");
    } catch (error) {
      console.error("Cannot read the ready event or startMeasurement failed");
      console.error(error)
    }
  });
} catch (e) {
  console.log("Error starting measurement.");
}

setTimeout(async () => {
  try {
    const temperatureData = await thing.readProperty("temperature")
    const temperature = await temperatureData.value();
    console.log("Temperature: " + temperature);

    await thing.unsubscribe("ready");
    console.log("Unsubscribed from the ‘ready’ event.");
  } catch (error) {
    console.log("Error in the cleanup function");
  }
}, 10000);

다음은 DataSchema 없이 property를 읽기 위해 InteractionOutput을 고급 방식으로 사용하는 예를 보여 줍니다.

예제 3: arrayBuffer를 사용하는 Thing Client API 예제
/*
* takePicture affordance form:
* "form": {
*   "op": "invokeaction",
*   "href" : "http://camera.example.com:5683/takePicture",
*   "response": {
*     "contentType": "image/jpeg",
*     "contentCoding": "gzip"
*   }
*}
* See https://www.w3.org/TR/wot-thing-description/#example-23
*/
let response;
let image;
try {
  response = await thing.invokeAction(“takePicture”));
  image = await response.value() // throws NotReadableError --> schema not defined
} catch(ex) {
  image = await response.arrayBuffer();
  // image: ArrayBuffer [0x1 0x2 0x3 0x5 0x15 0x23 ...]
}

마지막으로, 다음 두 예제는 InteractionOutput에서 ReadableStream을 사용하는 방법을 보여 줍니다.

예제 4: readable stream을 사용하는 Thing Client API 예제(예: 비디오 스트림)
/*{
"video": {
  "description" : "the video stream of this camera",
  "forms": [
    {
      "op": "readproperty",
      "href": "http://camera.example.com/live",
      "subprotocol": "hls"
      "contentType": "video/mp4"
    }
  ]
}}*/

const video = await thing.readProperty("video")
const reader = video.data.getReader()
reader.read().then(function processVideo({ done, value }) {
  if (done) {
    console.log("live video stoped");
    return;
  }
  const decoded = decode(value)
  UI.show(decoded)
  // Read some more, and call this function again
  return reader.read().then(processText);
});

여기서는 JSON 객체가 너무 커서 메모리에 전체를 읽어 들일 수 없다고 가정합니다. 따라서 스트리밍 처리를 사용하여 원격 Web Thing이 기록한 이벤트의 총 개수를 얻습니다.

예제 5: readable stream을 사용하는 Thing Client API 예제(예: json 객체 세기)
/*
* "eventHistory":
* {
*   "description" : "A long list of the events recorded by this thing",
*   "type": "array",
*   "forms": [
*     {
*       "op": "readproperty",
*       "href": "http://recorder.example.com/eventHistory",
*     }
*   ]
* }
*/

// Example of streaming processing: counting json objects
let objectCounter = 0
const parser = new Parser() //User library for json streaming parsing (i.e. https://github.com/uhop/stream-json/wiki/Parser)

parser.on('data', data => data.name === 'startObject' && ++objectCounter);
parser.on('end', () => console.log(`Found ${objectCounter} objects.`));

const response = await thing.readProperty(“eventHistory”)
await response.data.pipeTo(parser);

// Found N objects

9. ExposedThing 인터페이스

ExposedThing 인터페이스는 요청 핸들러와 Property, Action, Event 상호작용을 정의할 수 있게 하는 Thing을 조작하기 위한 서버 API입니다.

WebIDL[SecureContext, Exposed=(Window,Worker)]
interface ExposedThing {
  ExposedThing setPropertyReadHandler(DOMString name,
          PropertyReadHandler handler);
  ExposedThing setPropertyWriteHandler(DOMString name,
          PropertyWriteHandler handler);
  ExposedThing setPropertyObserveHandler(DOMString name,
          PropertyReadHandler handler);
  ExposedThing setPropertyUnobserveHandler(DOMString name,
          PropertyReadHandler handler);
  Promise<undefined> emitPropertyChange(DOMString name,
          optional InteractionInput data);

  ExposedThing setActionHandler(DOMString name, ActionHandler action);

  ExposedThing setEventSubscribeHandler(DOMString name,
          EventSubscriptionHandler handler);
  ExposedThing setEventUnsubscribeHandler(DOMString name,
          EventSubscriptionHandler handler);
  Promise<undefined> emitEvent(DOMString name,
          optional InteractionInput data);

  Promise<undefined> expose();
  Promise<undefined> destroy();

  ThingDescription getThingDescription();
};

callback PropertyReadHandler = Promise<InteractionInput>(
        optional InteractionOptions options = {});

callback PropertyWriteHandler = Promise<undefined>(
        InteractionOutput value,
        optional InteractionOptions options = {});

callback ActionHandler = Promise<InteractionInput>(
        InteractionOutput params,
        optional InteractionOptions options = {});

callback EventSubscriptionHandler = Promise<undefined>(
        optional InteractionOptions options = {});

9.1 ExposedThing의 내부 슬롯

ExposedThing 객체에는 다음 내부 슬롯이 있습니다:

내부 슬롯 초기값 설명(비규범적)
[[td]] null ExposedThingThing Description입니다.
[[readHandlers]] {} property 이름을 키로 하고 PropertyReadHandler를 값으로 하는 Map입니다
[[writeHandlers]] {} property 이름을 키로 하고 PropertyWriteHandler를 값으로 하는 Map입니다
[[observeHandlers]] {} property 이름을 키로 하고 PropertyReadHandler를 값으로 하는 Map입니다
[[unobserveHandlers]] {} property 이름을 키로 하고 Function을 값으로 하는 Map입니다
[[actionHandlers]] {} action 이름을 키로 하고 ActionHandler를 값으로 하는 Map입니다
[[subscribeHandlers]] {} event 이름을 키로 하고 EventSubscriptionHandler를 값으로 하는 Map입니다
[[unsubscribeHandlers]] {} event 이름을 키로 하고 EventSubscriptionHandler를 값으로 하는 Map입니다
[[propertyObservers]] {} property 이름을 키로 하고 listener의 Array를 값으로 하는 Map입니다
[[eventListeners]] {} event 이름을 키로 하고 listener의 Array를 값으로 하는 Map입니다

9.2 ExposedThing 구성하기

ExposedThing 인터페이스는 ConsumedThing을 확장합니다. 이는 전체 또는 부분 ThingDescription 객체에서 구성됩니다.

참고

기존 ThingDescription 객체는 선택적으로 수정될 수 있으며(예를 들어 내부 properties, actions, events 속성에서 요소를 추가하거나 제거하는 방식), 그 결과 객체는 ExposedThing 객체를 구성하는 데 사용할 수 있습니다. 이것이 Property, Action, Event 정의를 추가하고 제거하는 현재 방식이며, 예제에 설명되어 있습니다.

참고

expose()를 호출하기 전에는 ExposedThing 객체가 어떤 요청도 처리하지 않는다는 점에 유의하십시오. 이를 통해 먼저 ExposedThing을 구성한 다음, 요청 처리를 시작하기 전에 그 Properties와 서비스 핸들러를 초기화할 수 있습니다.

ExposedThingInit init으로 ExposedThing을 구성하려면 다음 단계를 실행합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. init에 대해 ExposedThingInit 확장 단계를 실행합니다. 실패하면 오류를 다시 throw하고 중지합니다. 그렇지 않으면 얻은 td를 저장합니다
  3. td에 대해 TD 확장 단계를 실행합니다. 실패하면 오류를 다시 throw하고 중지합니다.
  4. ExposedThing 객체를 thing으로 둡니다.
  5. thing[[td]]td로 설정합니다.
  6. thing을 반환합니다.

9.3 getThingDescription() 메서드

ThingThing Description을 나타내는 ExposedThing 객체의 [[td]]를 반환합니다. 애플리케이션은 그와 상호작용하기 전에 그 기능을 내부 검사하기 위해 [[td]]에 저장된 Thing 메타데이터를 참조할 수 있습니다.

9.4 PropertyReadHandler 콜백

Property를 읽기 위한 외부 요청이 수신될 때 호출되는 함수이며, 그러한 요청을 어떻게 처리할지 정의합니다. 이는 Promise를 반환하고, ReadableStream 객체 또는 ECMAScript 값DataSchema에 부합하는 값으로 resolve되거나, 오류로 reject됩니다.

9.5 setPropertyReadHandler() 메서드

인수로 namehandler를 받습니다. name과 일치하는 지정된 Property를 읽기 위한 요청이 수신될 때 무엇을 할지 정의하는 서비스 핸들러를 설정합니다. 오류 시 throw합니다. 체이닝을 지원하기 위해 this 객체에 대한 참조를 반환합니다.

편집자 주

여러 개 또는 모든 Properties를 읽기 위한 요청을 처리하기 위해 핸들러를 등록할 필요는 없다는 점에 유의하십시오. 요청과 응답은 단일 네트워크 요청으로 전송되지만, ExposedThing은 단일 read handler에 대한 여러 호출을 사용하여 이를 구현할 수 있습니다.

handler 콜백 함수는 Property 읽기를 구현해야 하며, 기본 플랫폼에서 Property를 읽기 위한 요청이 수신될 때 구현에 의해 호출되어야 SHOULD 합니다.

주어진 어떤 Property에 대해서도 핸들러는 최대 하나만 있어야 MUST 하므로, 새로 추가된 핸들러는 이전 핸들러를 대체해야 MUST 합니다. 주어진 어떤 Property에도 핸들러가 초기화되어 있지 않으면, 구현은 [[td]] 내부 슬롯에 제공된 Thing Description에 기반한 기본 property read handler를 구현해야 SHOULD 합니다.

namehandler가 주어진 상태로 메서드가 호출되면, 구현은 다음 단계를 실행해야 MUST 합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. [[td]].properties[name]이 exist하지 않으면, NotFoundErrorthrow하고 중지합니다.
  3. this.[[readHandlers]][name]을 handler로 설정합니다.

9.6 Property 읽기 요청 처리

Property name을 읽기 위한 네트워크 요청이 options와 함께 구현에 수신되면, 다음 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. nameoptions서버 property 읽기 단계를 실행한 결과를 value로 둡니다:
    1. [[td]].properties.nameinteraction으로 둡니다.
    2. name을 가진 Property가 존재하지 않으면, NotFoundError를 throw하고 중지합니다.
    3. nullhandler로 둡니다.
    4. interaction에 대해 [[readHandlers]] 내부 슬롯에 사용자가 제공한 PropertyReadHandler가 있으면, 그것을 handler로 둡니다.
    5. 그렇지 않고 구현이 제공한 기본 read handler가 있으면, 그것을 handler로 둡니다.
    6. handlernull이면, NotSupportedError를 throw하고 중지합니다.
    7. options가 주어졌을 때 handler를 호출한 결과를 value로 둡니다. 실패하면 오류를 throw하고 중지합니다.
    8. value를 반환합니다.
      참고

      여기에서 반환된 valueDataSchema에 부합해야 SHOULD 하거나, handler가 생성한 ReadableStream 객체여야 SHOULD 합니다.

  4. 이전 단계에서 오류가 throw되었으면, Protocol Bindings를 따르는 방식으로 생성된 응답과 함께 오류를 되돌려 보내고 중지합니다.
  5. 반환된 value를 직렬화하여 Protocol Bindings를 따르는 방식으로 생성된 응답에 추가합니다.

9.7 여러 Properties 읽기 요청 처리

객체 propertyNames에 주어진 여러 Properties를 읽기 위한 네트워크 요청이 options와 함께 수신되면, propertyNamesoptions에 대해 다음 여러 properties 읽기 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. propertyNames에 정의된 키 name을 가진 각 property에 대해,
    1. nameoptions에 대해 서버 property 읽기 단계를 실행한 결과를 value로 둡니다. 그것이 throw하면, Protocol Bindings를 따르는 방식으로 생성된 응답에 오류를 되돌려 보내고 중지합니다.
    2. propertyNames.namevalue로 설정합니다.
  4. Protocol Bindings에 따라 propertyNames에서 생성된 단일 응답을 보내 요청에 응답합니다.

9.8 모든 Properties 읽기 요청 처리

모든 Properties를 읽기 위한 네트워크 요청이 options와 함께 수신되면, 다음 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. Thing에 정의된 모든 properties로 생성되고 값이 null로 설정된 객체를 properties로 둡니다.
  4. propertiesoptions에 대해 여러 properties 읽기 단계를 실행합니다.

9.9 setPropertyObserveHandler() 메서드

인수로 namehandler를 받습니다. name과 일치하는 지정된 Property를 관찰하기 위한 요청이 수신될 때 무엇을 할지 정의하는 서비스 핸들러를 설정합니다. 오류 시 throw합니다. 체이닝을 지원하기 위해 this 객체에 대한 참조를 반환합니다.

handler 콜백 함수는 Property 읽기를 구현하고 InteractionOutput 객체로 resolve하거나 오류로 reject해야 합니다.

주어진 어떤 Property에 대해서도 핸들러는 최대 하나만 있어야 MUST 하므로, 새로 추가된 핸들러는 이전 핸들러를 대체해야 MUST 합니다. 주어진 어떤 Property에도 핸들러가 초기화되어 있지 않으면, 구현은 Thing Description에 기반한 기본 property read handler를 구현해야 SHOULD 합니다.

namehandler가 주어진 상태로 메서드가 호출되면, 구현은 다음 단계를 실행해야 MUST 합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. this.[[td]].properties[name]이 exist하지 않으면, NotFoundErrorthrow하고 중지합니다.
  3. this[[observeHandlers]][name]을 handler로 설정합니다.

9.10 Property observe 요청 처리

Property name을 관찰하기 위한 네트워크 요청이 options와 함께 구현에 수신되면, 다음 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. this.[[td]].properties[name]이 exist하지 않으면, 응답에 NotFoundError를 되돌려 보내고 중지합니다.
  4. Property 값 변경에 대해 알릴 수 있도록, 요청 발신자 정보를 optionsthis.[[propertyObservers]][name]와 함께 내부적으로 저장합니다.
참고

property의 값이 변경될 때마다, 애플리케이션 스크립트가 emitPropertyChange()를 명시적으로 호출해야 합니다.

9.11 setPropertyUnobserveHandler() 메서드

인수로 namehandler를 받습니다. name과 일치하는 지정된 Property의 관찰을 해제하기 위한 요청이 수신될 때 무엇을 할지 정의하는 서비스 핸들러를 설정합니다. 오류 시 throw합니다. 체이닝을 지원하기 위해 this 객체에 대한 참조를 반환합니다.

handler 콜백 함수는 구현이 unobserve 요청을 수신할 때 무엇을 할지 구현해야 합니다.

주어진 어떤 Property에 대해서도 핸들러는 최대 하나만 있어야 MUST 하므로, 새로 추가된 핸들러는 이전 핸들러를 대체해야 MUST 합니다. 주어진 어떤 Property에도 핸들러가 초기화되어 있지 않으면, 구현은 Thing Description에 기반한 기본 핸들러를 구현해야 SHOULD 합니다.

namehandler가 주어진 상태로 메서드가 호출되면, 구현은 다음 단계를 실행해야 MUST 합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. this.[[td]].properties[name]이 exist하지 않으면, NotFoundErrorthrow하고 중지합니다.
  3. this.[[unobserveHandlers]][name]을 handler로 설정합니다.

9.12 Property unobserve 요청 처리

options와 함께 Property name의 관찰을 해제하기 위한 네트워크 요청이 구현에 수신되면, 다음 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. this.[[td]].properties[name]이 exist하지 않으면, 응답에 NotFoundError를 되돌려 보내고 중지합니다.
  4. this.[[unobserveHandlers]][name]을 handler로 둡니다;
  5. handlerFunction이면, options를 인수로 그것을 호출한 다음, Protocol Bindings를 따르는 응답을 되돌려 보내고 중지합니다.
  6. 그렇지 않고 this.[[propertyObservers]][name]이 exists이면, 이를 this.[[propertyObservers]]에서 제거하고, Protocol Bindings에 정의된 대로 응답을 되돌려 보내고 중지합니다.
  7. 그렇지 않으면 Protocol Bindings에 정의된 대로 응답에 NotFoundError를 되돌려 보내고 중지합니다.

9.13 emitPropertyChange() 메서드

Property 이름을 나타내는 name 인수와, 선택적으로 data 인수를 받습니다. data 인수로 제공된 데이터와 함께 지정된 Property의 모든 observer에게 알림 방출을 트리거합니다. 제공되지 않은 경우에는 해당 Property와 연관된 observe handler 또는 read handler에 의해 얻습니다. 이 메서드는 다음 단계를 실행해야 MUST 합니다:
  1. Promisepromise로 둡니다.
  2. promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  3. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  4. 첫 번째 인수를 name으로 둡니다.
  5. this.[[td]].properties[name]를 property로 둡니다.
  6. propertyundefined이면, promiseNotFoundErrorreject하고 중지합니다.
  7. 두 번째 인수를 data로 둡니다.
  8. dataundefined이면, 다음 하위 단계를 실행합니다:
    1. null.handler로 둡니다.
    2. name [[readHandlers]]exist하지 않으면, promisereject하고 중지합니다.
    3. [[readHandlers]][name]를 handler로 둡니다.
    4. handlernull 또는 undefined이면, promisereject하고 중지합니다.
    5. null이 주어진 상태로 handler를 호출한 결과를 handled로 둡니다.
    6. handledrejected이면, promise reject하고 중지합니다.
    7. 그렇지 않고 handledvalueresolved되면, valuedata로 둡니다.
  9. [[propertyObservers]][name]의 각 observer에 대해 다음 하위 단계를 실행합니다:
    1. observer와 함께 저장된 상호작용 옵션을 options로 둡니다.
    2. Protocol Bindings에 따라 dataoptions로부터 reply를 생성하도록 기본 플랫폼에 요청합니다.
      편집자 주

      이 절은 확장되어야 하며/또는 [WOT-PROTOCOL-BINDINGS]의 알고리즘을 참조해야 합니다.

    3. replyobserver에게 보냅니다.
  10. promiseResolve합니다.

9.14 PropertyWriteHandler 콜백

Property를 쓰기 위한 외부 요청이 수신될 때 호출되는 함수이며, 그러한 요청을 어떻게 처리할지 정의합니다. 인수로 value를 받고 Promise를 반환하며, 핸들러를 설정할 때 제공된 이름으로 식별되는 Property의 값이 업데이트되면 resolve되고, property를 찾을 수 없거나 값을 업데이트할 수 없으면 오류로 reject됩니다.

편집자 주

이 콜백 함수의 코드는 필요하다면 이전 값을 알아내기 위해 업데이트 전에 property를 읽을 수 있다는 점에 유의하십시오. 따라서 이전 값은 이 함수에 제공되지 않습니다.

참고

값은 stream과 같이 DataSchema로 설명되지 않은 값을 표현할 수 있도록 구현에 의해 InteractionOutput 객체로 제공됩니다.

9.15 setPropertyWriteHandler() 메서드

인수로 namehandler를 받습니다. 핸들러를 설정할 때 주어진 name과 일치하는 Property를 쓰기 위한 요청이 수신될 때 무엇을 할지 정의하는 서비스 핸들러를 설정합니다. 오류 시 throw합니다. 체이닝을 지원하기 위해 this 객체에 대한 참조를 반환합니다.

참고

readonly Properties에 대해서도 Issue 199에서 설명한 것처럼 write handler를 지정할 수 있다는 점에 유의하십시오. 이 경우 write handler는 애플리케이션별 방식으로 요청을 실패시키도록 정의할 수 있습니다.

주어진 어떤 Property에 대해서도 write handler는 최대 하나만 있어야 MUST 하므로, 새로 추가된 핸들러는 이전 핸들러를 대체해야 MUST 합니다. 주어진 어떤 Property에도 write handler가 초기화되어 있지 않으면, 구현은 Thing Description에 기반하여 Property가 writeable이면 기본 property update를, Property가 observable이면 변경 시 observer 알림을 구현해야 SHOULD 합니다.

namehandler가 주어진 상태로 메서드가 호출되면, 구현은 다음 단계를 실행해야 MUST 합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. this.[[td]].properties[name]이 exist하지 않으면, NotFoundErrorthrow하고 중지합니다.
  3. this.[[writeHandlers]][name]을 handler로 설정합니다.

9.16 Property 쓰기 요청 처리

새 값 valueoptions와 함께 Property name을 쓰기 위한 네트워크 요청이 수신되면, 구현은 name, value, optionsmode"single"로 설정한 상태로 다음 property 업데이트 단계를 실행해야 MUST 합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. this.[[td]].properties[name]를 interaction으로 둡니다.
  4. interactionundefined이면, 응답에 NotFoundError를 반환하고 중지합니다.
  5. this.[[writeHandlers]][name]를 handler로 둡니다.
  6. handlerundefined이고 구현이 제공한 기본 write handler가 있으면, 그것을 handler로 둡니다.
  7. handler undefined이면, 응답과 함께 NotSupportedError를 되돌려 보내고 중지합니다.
  8. nameoptions가 주어진 상태로 handler를 호출한 결과를 promise로 둡니다. 실패하면 응답에 오류를 반환하고 중지합니다.
  9. mode"single"이면, Protocol Bindings를 따라 성공을 보고하는 방식으로 요청에 응답하고 중지합니다.

9.17 여러 Properties 쓰기 요청 처리

객체 propertyNames에 주어진 여러 Properties를 쓰기 위한 네트워크 요청이 options와 함께 수신되면, 다음 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. propertyNames에 정의된 키 name과 값 value를 가진 각 property에 대해, name, value, optionsmode"multiple"로 설정하여 property 업데이트 단계를 실행합니다. 실패하면 그 오류로 요청에 응답하고 중지합니다.
  4. Protocol Bindings에 따라 단일 응답을 보내 요청에 응답합니다.

9.18 ActionHandler 콜백

Action을 호출하기 위한 외부 요청이 수신될 때 호출되는 함수이며, 그러한 요청을 어떻게 처리할지 정의합니다. 이는 params가 주어져 호출되며, 선택적으로 options 객체와 함께 호출됩니다. 이는 오류로 reject되거나 Action이 반환한 값을 InteractionInput으로 resolve하는 Promise를 반환합니다.

참고

애플리케이션 스크립트는 ActionHandler에서 ReadableStream 객체를 반환할 수 MAY 있습니다. 그러면 구현은 stream을 사용하여 Action의 응답을 구성합니다.

9.19 setActionHandler() 메서드

인수로 nameaction을 받습니다. name과 일치하는 Action을 호출하기 위한 요청이 수신될 때 무엇을 할지 정의하는 handler 함수를 설정합니다. 오류 시 throw합니다. 체이닝을 지원하기 위해 this 객체에 대한 참조를 반환합니다.

action 콜백 함수는 Action을 구현하며, 기본 플랫폼에서 Action 호출 요청이 수신될 때 구현에 의해 호출되어야 SHOULD 합니다.

주어진 어떤 Action에 대해서도 핸들러는 최대 하나만 있어야 MUST 하므로, 새로 추가된 핸들러는 이전 핸들러를 대체해야 MUST 합니다.

nameaction이 주어진 상태로 메서드가 호출되면, 다음 단계를 실행합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. this.[[td]].actions[name]를 interaction으로 둡니다.
  3. interactionundefined이면, NotFoundErrorthrow하고 중지합니다.
  4. this.[[actionHandlers]][name]을 action으로 설정합니다.

9.20 Action 요청 처리

inputs와 선택적으로 options가 주어진 상태로, name으로 식별되는 Action을 호출하기 위한 네트워크 요청이 수신되면, 다음 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. this.[[td]].properties[name]를 interaction으로 둡니다.
  4. interactionundefined이면, 응답에 NotFoundError를 반환하고 중지합니다.
  5. this.[[actionHandlers]][name]를 handler로 둡니다.
  6. handler undefined이면, Protocol Bindings를 따르는 방식으로 생성된 응답과 함께 NotSupportedError를 반환하고 중지합니다.
  7. name, inputsoptions가 주어진 상태로 handler를 호출한 결과를 promise로 둡니다.
  8. promise가 reject되면, 응답과 함께 오류를 보내고 중지합니다.
  9. promisedata로 resolve되면, data를 사용하여 Protocol Bindings에 따라 응답을 생성하고 보냅니다.

9.21 EventSubscriptionHandler 콜백

Event를 구독하기 위한 외부 요청이 수신될 때 호출되는 함수이며, 그러한 요청을 어떻게 처리할지 정의합니다. 이는 구현이 제공하고 구독자로부터 오는 options 객체가 주어진 상태로 호출됩니다. 이는 오류로 reject되거나 구독이 수락되면 resolve되는 Promise를 반환합니다.

9.22 setEventSubscribeHandler() 메서드

인수로 namehandler를 받습니다. name과 일치하는 지정된 Event에 대한 구독 요청이 수신될 때 무엇을 할지 정의하는 handler 함수를 설정합니다. 오류 시 throw합니다. 체이닝을 지원하기 위해 this 객체에 대한 참조를 반환합니다.

handler 콜백 함수는 구독 요청이 수신될 때 무엇을 할지, 예를 들어 필요한 초기화를 구현해야 SHOULD 합니다. Events를 방출하기 위한 핸들러는 별도로 설정된다는 점에 유의하십시오.

주어진 어떤 Event에 대해서도 event subscribe handler는 최대 하나만 있어야 MUST 하므로, 새로 추가된 핸들러는 이전 핸들러를 대체해야 MUST 합니다.

namehandler가 주어진 상태로 메서드가 호출되면, 다음 단계를 실행합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. this.[[td]].events[name]를 interaction으로 둡니다.
  3. interactionundefined이면, NotFoundErrorthrow하고 중지합니다.
  4. this.[[subscribeHandlers]][name]을 handler로 설정합니다.
  5. this를 반환합니다.

9.23 Event subscribe 요청 처리

name에 대한 Event 구독 요청이 선택적 options와 함께 기본 플랫폼에 수신되면, 다음 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. this.[[td]].events[name]를 interaction으로 둡니다.
  4. interactionundefined이면, NotFoundError를 되돌려 보내고 중지합니다.
  5. this.[[subscribeHandlers]][name]이 Function이면, options를 인수로 이를 호출하고 중지합니다.
  6. 그렇지 않으면 기본 subscriber 메커니즘을 구현합니다:
    1. options (여기서 uriVariablesdata가 사용될 수 있음)와 Event 알림 응답을 생성하는 데 필요한 subscriber 정보로 구성된 tuple을 subscriber로 둡니다.
    2. this.[[eventListeners]][name]을 subscriber로 설정합니다.

9.24 setEventUnsubscribeHandler() 메서드

인수로 namehandler를 받습니다. name과 일치하는 지정된 Event의 구독이 해제될 때 무엇을 할지 정의하는 handler 함수를 설정합니다. 오류 시 throw합니다. 체이닝을 지원하기 위해 this 객체에 대한 참조를 반환합니다.

handler 콜백 함수는 구독 해제 요청이 수신될 때 무엇을 할지 구현해야 SHOULD 합니다.

주어진 어떤 Event에 대해서도 핸들러는 최대 하나만 있어야 MUST 하므로, 새로 추가된 핸들러는 이전 핸들러를 대체해야 MUST 합니다.

namehandler가 주어진 상태로 메서드가 호출되면, 다음 단계를 실행합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. this.[[td]].events[name]를 interaction으로 둡니다.
  3. interactionundefined이면, NotFoundErrorthrow하고 중지합니다.
  4. this.[[unsubscribeHandlers]][name]을 handler로 설정합니다.
  5. this를 반환합니다.

9.25 Event unsubscribe 요청 처리

name에 대한 Event 구독 해제 요청이 선택적으로 options와 함께 기본 플랫폼에 수신되면, 다음 단계를 실행합니다:
  1. 이 작업이 지원되지 않으면, Protocol Bindings에 따라 NotSupportedError를 되돌려 보내고 중지합니다.
  2. 이 작업이 허용되지 않으면, Protocol Bindings에 따라 NotAllowedError를 되돌려 보내고 중지합니다.
  3. this.[[td]].events[name]를 interaction으로 둡니다.
  4. interactionundefined이면, NotFoundError를 되돌려 보내고 중지합니다.
  5. this.[[unsubscribeHandlers]][name]이 exists이고 Function이면, options를 인수로 이를 호출하고 중지합니다.
  6. 그렇지 않으면 namethis.[[eventListeners]]에 [=map/exists]하면, nameremove합니다.
  7. this를 반환합니다.

9.26 Events 처리

이름이 nameEventemitEvent() 메서드에 의해 data와 함께 방출되면, 다음 단계를 실행합니다:
  1. [[eventListeners]].namelisteners로 둡니다.
  2. listeners의 각 subscriber에 대해 다음 하위 단계를 실행합니다:
    1. Protocol Bindings에 따라 datasubscriber(그 options 포함)로부터 Event 알림 response를 생성합니다.
    2. dataundefined이면, 알림 responseProtocol Bindings가 지정한 대로 빈 데이터 페이로드를 포함한다고 가정합니다.
    3. 기본 프로토콜 스택이 event 오류 전달을 허용하고 UA가 오류 조건을 감지한 경우, Protocol Bindings에 따라 data, subscriber 및 그 options를 사용하여 response를 오류 알림으로 생성합니다.
      참고

      오류 보고는 프로토콜별이며 구현에 의해 캡슐화됩니다. 클라이언트 측에서는 클라이언트 UA가 오류를 감지하면 구독과 함께 전달된 오류 listener가 호출됩니다.

    4. subscriber로 식별되는 구독자에게 response를 보냅니다.

9.27 emitEvent() 메서드

Event 이름을 나타내는 name과 선택적으로 data를 인수로 받습니다. 선택적 데이터와 함께 Event 방출을 트리거합니다. 이 메서드는 다음 단계를 실행해야 MUST 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. [[td]].events.nameinteraction으로 둡니다.
  4. 이름이 nameEvent를 찾을 수 없으면, promiseNotFoundErrorreject하고 중지합니다.
  5. 선택적 data와 함께 Event를 방출하도록 기본 플랫폼에 요청합니다. events 처리 단계를 호출합니다.

9.28 expose() 메서드

Thing에 대한 외부 요청 처리를 시작하여, Properties, ActionEvent를 사용하는 WoT Interactions가 가능해지도록 합니다. 이 메서드는 다음 단계를 실행해야 MUST 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. [[td]]에 대해 TD 확장 단계를 실행합니다.
  4. [[td]]에 대해 TD 검증을 실행합니다. 실패하면 promise TypeErrorreject하고 중지합니다.
  5. [[td]].properties의 각 key에 대해, 값 변경 시 observer에게 알리는 데 필요한 observe 요청 데이터를 저장하기 위해 this.[[propertyObservers]].key를 빈 Array로 초기화합니다.
  6. this.[[td]].events의 각 key에 대해, event 방출 시 subscriber에게 알리는 데 필요한 subscribe 요청 데이터를 저장하기 위해 this.[[eventListeners]].key를 빈 Array로 초기화합니다.
  7. [WOT-TD]와 [WOT-PROTOCOL-BINDINGS]에 설명된 대로 [[td]]를 내부 검사하는 것에 기반해 WoT Interactions를 설정합니다. 기본 플랫폼에 요청하여 Protocol Bindings를 초기화한 다음, Protocol Bindings에 기반하여 WoT Interactions ( Properties 읽기, 쓰기 및 관찰, Action 호출, Event 구독 관리)에 대한 외부 요청 처리를 시작합니다. 구현은 어떤 이유로든 이 단계를 reject할 수 MAY 있습니다 (예: interaction form에 대한 추가 검사와 제약을 강제하려는 경우).
  8. 요청 중 오류가 있었다면, Protocol Bindings에서 본 오류 코드로 error.message가 설정된 Error 객체 errorpromisereject하고 중지합니다.
  9. 그렇지 않으면 promiseresolve하고 중지합니다.

9.29 destroy() 메서드

Thing에 대한 외부 요청 처리를 중지하고 객체를 파괴합니다. 최종적인 등록 해제는 이 메서드를 호출하기 전에 수행되어야 한다는 점에 유의하십시오. 이 메서드는 다음 단계를 실행해야 MUST 합니다:
  1. Promise promise를 반환하고, 다음 단계를 병렬로 실행합니다.
  2. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, promiseSecurityErrorreject하고 중지합니다.
  3. Protocol Bindings에 기반하여 WoT Interactions에 대한 외부 요청 처리를 중지하도록 기본 플랫폼에 요청합니다.
  4. 요청 중 오류가 있었다면, Protocol Bindings에서 본 오류 코드로 message가 설정된 Error 객체 errorpromisereject하고 중지합니다.
  5. 그렇지 않으면 promiseresolve하고 중지합니다.

9.30 ExposedThing 예제

다음 예제는 미리 구성된 부분 TD 객체를 기반으로 ExposedThing을 생성하는 방법을 보여 줍니다.

예제 6: 단순 Property로 ExposedThing 생성하기
try {
  let temperaturePropertyDefinition = {
    type: "number",
    minimum: -50,
    maximum: 10000
  };
  let tdFragment = {
    properties: {
      temperature: temperaturePropertyDefinition
    },
    actions: {
      reset: {
        description: "Reset the temperature sensor",
        input: {
          temperature: temperatureValueDefinition
        },
        output: null,
        forms: []
      },
    },
    events: {
      onchange: temperatureValueDefinition
    }
  };
  let thing1 = await WOT.produce(tdFragment);
  // initialize Properties
  await thing1.writeProperty("temperature", 0);
  // add service handlers
  thing1.setPropertyReadHandler("temperature", () => {
     return readLocalTemperatureSensor();  // Promise
  });
  // start serving requests
  await thing1.expose();
} catch (err) {
   console.log("Error creating ExposedThing: " + err);
}

다음 예제는 기존 ExposedThing에서 Property 정의를 추가하거나 수정하는 방법을 보여 줍니다: 그 td 속성을 가져와 추가하거나 수정한 다음, 그것으로 또 다른 ExposedThing을 생성합니다.

예제 7: 객체 Property 추가
try {
  // create a deep copy of thing1's TD
  let instance = JSON.parse(JSON.stringify(thing1.td));
  const statusValueDefinition = {
    type: "object",
    properties: {
      brightness: {
        type: "number",
        minimum: 0.0,
        maximum: 100.0,
        required: true
      },
      rgb: {
        type: "array",
        "minItems": 3,
        "maxItems": 3,
        items : {
            "type" : "number",
            "minimum": 0,
            "maximum": 255
        }
      }
  };
  instance["name"] = "mySensor";
  instance.properties["brightness"] = {
    type: "number",
    minimum: 0.0,
    maximum: 100.0,
    required: true,
  };
  instance.properties["status"] = statusValueDefinition;
  instance.actions["getStatus"] = {
    description: "Get status object",
    input: null,
    output: {
      status : statusValueDefinition;
    },
    forms: [...]
  };
  instance.events["onstatuschange"] = statusValueDefinition;
  instance.forms = [...];  // update
  var thing2 = new ExposedThing(instance);
  // TODO: add service handlers
  await thing2.expose();
  });
} catch (err) {
   console.log("Error creating ExposedThing: " + err);
}

다음은 ExposedThingInit 확장 단계를 사용하여 ExposedThingInit에서 Thing Description을 생성하는 예제 세트를 다룹니다. 가정으로, 런타임은 HTTP 및 COAP protocol binding을 지원하며 192.168.0.1에서 호스팅됩니다.

다음 예제는 기본값을 가진 하나의 Property가 포함된 단순 Thing Description을 생성하기 위해 ExposedThingInit을 활용하는 방법을 보여 줍니다.

편집자 주

TODO: ExposedThingInit에 알고리즘에 의해 대체되는 제안값이 포함되는 예제를 더 추가합니다.

10. ThingDiscoveryProcess 인터페이스

Discovery는 참여하는 네트워크 노드 (클라이언트, 서버, 디렉터리 서비스)의 프로비저닝과 지원이 필요한 분산 애플리케이션입니다. 이 API는 다양한 IoT 배포에서 지원되는 일반적인 discovery 방식의 클라이언트 측을 모델링합니다.

ThingDiscoveryProcess 객체는 discovery process를 제어하고 결과를 반환하는 속성과 메서드를 제공합니다.

WebIDL[SecureContext, Exposed=(Window,Worker)]
interface ThingDiscoveryProcess {
  constructor(optional ThingFilter filter = {});
  readonly attribute boolean done;
  readonly attribute Error? error;
  undefined stop();
  async iterable<ThingDescription>;
};

ThingDiscoveryProcess 객체에는 다음 internal slot이 있습니다.

Internal Slot 초기값 설명(비규범적)
[[filter]] undefined discovery에서 사용되는 ThingFilter 객체입니다.
[[url]] undefined discovery에서 TD Directory를 나타내는 URL입니다.

done 속성은 discovery가 중지되었거나 더 이상 보고할 결과 없이 완료된 경우 true입니다.

error 속성은 discovery process 중 발생한 마지막 오류를 나타냅니다. 일반적으로 discovery를 중지시키는 심각한 오류에 사용됩니다.

ThingDiscoveryProcess 객체는 async iterator 개념을 구현합니다.

10.1 ThingDiscoveryProcess 구성하기

filterThingDiscoveryProcess를 생성하려면 다음 단계를 실행합니다:
  1. filter가 객체 또는 null이 아니면, TypeErrorthrow하고 중지합니다.
  2. ThingDiscoveryProcess 객체를 discovery로 둡니다.
  3. discovery.[[filter]]filter로 설정합니다.
  4. discovery.donefalse로 설정합니다.
  5. discovery.errornull로 설정합니다.
  6. discovery를 반환합니다.

10.2 ThingFilter dictionary

Thing을 discovery하기 위한 제약 조건을 key-value 쌍으로 포함하는 객체를 나타냅니다.

WebIDLdictionary ThingFilter {
  object? fragment;
  
};

fragment 속성은 발견된 Thing과 속성별로 매칭하는 데 사용되는 템플릿 객체를 나타냅니다.

편집자 주

query 속성은 WoT Discovery 태스크포스에서 표준화될 때까지 ThingFilter에서 일시적으로 제거되었습니다. 이는 구현이 수락하는 query string, 예를 들어 SPARQL 또는 JSON query를 나타냈습니다. 지원은 WoT Runtime에서 로컬로 구현되거나, TD Directory의 서비스로 원격 구현될 예정이었습니다.

편집자 주

url 속성은 제거되었습니다. 이 속성은 discovery 요청을 제공하는 대상 엔티티, 예를 들어 TD Directory의 URL 또는 직접 대상으로 지정된 Thing의 URL을 나타냈지만, 이제는 전용 메서드로 구현됩니다.

10.3 discovery process 알고리즘

이미 시작된 discovery process 동안 무엇을 해야 하는지 설명합니다. 이 알고리즘은 discovery가 주어졌을 때 다음 단계를 실행합니다:
  1. Thing Description으로 이어지는 새 link가 발견되어 기본 플랫폼에서 제공될 때마다 다음 하위 단계를 실행합니다:
    1. 기본 discovery process에서 사용하는 Protocol Binding(link로 지정됨)을 사용하여 td를 JSON 객체로 가져옵니다. HTTP(S) Binding의 경우, 이 과정은 td를 가져오기 위해 Fetch API를 사용할 수 있습니다.
    2. 실패하면 discovery.error 속성을 SyntaxError로 설정하고, td를 폐기한 다음 discovery process를 계속합니다.
  2. Thing Description td가 발견되어 기본 플랫폼 또는 이전 단계에서 제공될 때마다 다음 하위 단계를 실행합니다:
    참고

    이 시점에서 구현은 discovery process의 흐름을 제어할 수 MAY 있습니다 (예를 들어 메모리 제약에 따라 결과를 큐에 넣거나, 큐가 너무 커지면 discovery를 일시적으로 중지하거나, 큐가 충분히 비워지면 discovery를 재개할 수 있습니다). 이 단계들은 발견/가져온 각 td에 대해 실행됩니다.

    1. discovery.[[filter]].fragmentfragment로 둡니다.
    2. fragmentobject이면, 그 안에 정의된 각 key에 대해:
      1. keyjson 안에 exists하는지, 그리고 json[key]가 fragment.key와 같은지 확인합니다.
      2. 이 검사 중 어느 하나라도 실패하면, td를 폐기하고 discovery process를 계속합니다.
    3. asyncIterator를 사용하여 td를 yield합니다.
      편집자 주

      적절한 asyncIterator 용어를 사용하여 이 단계를 개선합니다.

  3. discovery process 중 오류가 발생할 때마다 다음 하위 단계를 실행합니다:
    참고

    마지막 오류가 유지됩니다. 구현은 오류가 보고되어야 한다고 판단하면 discovery process를 중지하기로 선택할 수 MAY 있습니다.

    1. Error 객체를 error로 둡니다. error.name"DiscoveryError"로 설정합니다.
    2. Protocol Bindings에서 제공한 오류 코드 또는 메시지가 있었다면, error.message를 그 값의 문자열로 설정합니다.
    3. discovery.errorerror로 설정합니다.
    4. 오류가 복구 불가능하고 기본 플랫폼에 의해 discovery가 중지되었거나, 구현이 discovery process를 중지하고 오류를 보고하기로 결정했다면, discovery.donetrue로 설정하고 이 단계를 종료합니다.

10.4 stop() 메서드

discovery process를 중지하거나 억제합니다. 모든 discovery 메서드와 엔드포인트에서 지원되는 것은 아닐 수 있지만, 이후의 discovery 결과 또는 오류는 모두 폐기되고 discovery는 완료된 것으로 표시됩니다. 이 메서드는 다음 단계를 실행해야 MUST 합니다:
  1. 보안상의 이유로 현재 스크립팅 컨텍스트에서 이 메서드 호출이 허용되지 않으면, SecurityErrorthrow하고 중지합니다.
  2. 기본 플랫폼에 discovery process를 중지하도록 요청합니다. 이것이 오류를 반환하거나, 예를 들어 discovery가 끝이 열린 multicast 요청에 기반한 경우처럼 불가능하다면, 구현은 이후 발견된 항목을 폐기해야 SHOULD 합니다.
  3. done 속성을 true로 설정합니다.

10.5 Discovery 예제

다음 예제는 로컬 하드웨어에 의해 노출된 ThingThingDescription 객체를 찾습니다. 실행 중인 WoT Runtime 인스턴스 수와 관계없이, Discovery 객체가 제공하는 asyncIterator를 사용하여 결과를 비동기적으로 순회하고, 얻은 ThingDescription 객체로 작업을 수행할 수 있습니다.

예제 9: Thing의 Thing Description 가져오기
let url = "https://mythings.com/thing1";
let td = await WOT.requestThingDescription(url);
console.log("Found Thing Description for " + td.title);

다음 예제는 TD Directory 서비스에 나열된 ThingThingDescription 객체를 찾습니다. 안전을 위해 timeout을 설정합니다.

예제 10: directory를 통해 Things discovery하기
let discovery = await WOT.exploreDirectory("http://directory.wotservice.org");
setTimeout( () => {
    discovery.stop();
    console.log("Discovery stopped after timeout.");
  },
  3000);
for await (const td of discovery) {
  console.log("Found Thing Description for " + td.title);
  let thing = new ConsumedThing(td);
  console.log("Thing name: " + thing.getThingDescription().title);
};
if (discovery.error) {
  console.log("Discovery stopped because of an error: " + error.message);
}

다음 예제는 WOT runtime에 프로비저닝된 어떤 방식으로든 수행되는 일반 discovery를 위한 것으로, 사용 가능한 경우 로컬 Things도 포함합니다.

예제 11: 네트워크에서 Things discovery하기
let discovery = await WOT.discover();
setTimeout( () => {
    discovery.stop();
    console.log("Stopped open-ended discovery");
  },
  10000);
for await (const td of discovery) {
  console.log("Found Thing Description for " + td.title);
};
if (discovery.error) {
  console.log("Discovery stopped because of an error: " + error.message);
}

11. 보안 및 프라이버시

다양한 상황에 맞게 조정할 수 있는 위협 모델을 포함하여 Web of Things의 보안 및 프라이버시 고려사항에 대한 자세한 논의는 정보성 문서 [WOT-SECURITY]에 제시되어 있습니다. 이 섹션에서는 스크립트와 WoT Scripting API에 직접 관련된 보안 및 프라이버시 위험과 가능한 완화책만 논의합니다.

WoT devices 및 services의 보안을 개선하기 위한 권장 best practices 세트는 [WOT-SECURITY]에 문서화되어 있습니다. 그 문서는 보안 조치가 발전함에 따라 업데이트될 수 있습니다. 이러한 관행을 따른다고 보안이 보장되는 것은 아니지만, 일반적으로 알려진 취약점을 피하는 데 도움이 될 수 있습니다.

WoT 보안 위험 및 가능한 완화책은 다음 그룹과 관련됩니다:

11.1 Scripting Runtime 보안 및 프라이버시 위험

이 섹션은 규범적이며 WoT Scripting Runtime과 관련된 구체적인 위험을 포함합니다.

11.1.1 손상된 입력 보안 및 프라이버시 위험

어떤 프로세스를 손상시키는 일반적인 방법은 노출된 인터페이스 중 하나를 통해 손상된 입력을 보내는 것입니다. 이는 script instance가 노출하는 WoT interface를 사용하여 수행될 수 있습니다.

완화책:
이 API의 구현자는 모든 script 입력에 대해 검증을 수행해야 SHOULD 합니다. 입력 검증에 더해, 입력 처리가 올바르게 수행되는지 확인하기 위해 fuzzing을 사용해야 합니다. 이러한 검증을 수행하기 위한 많은 도구와 기법이 존재합니다. 자세한 내용은 [WOT-SECURITY]에서 찾을 수 있습니다.

11.1.2 물리적 장치 직접 접근 보안 및 프라이버시 위험

script가 손상되었거나 오동작하는 경우, script가 직접 노출된 native device interface를 사용할 수 있다면 기본 물리적 장치 (및 잠재적으로 주변 환경)가 손상될 수 있습니다. 이러한 인터페이스에 입력에 대한 안전성 검사가 없다면, 기본 물리적 장치(또는 환경)를 안전하지 않은 상태(예: 장치가 과열되어 폭발)로 만들 수 있습니다.

완화책:
WoT Scripting Runtime은 native device interface를 script 개발자에게 직접 노출하는 것을 피해야 SHOULD 합니다. 대신 WoT Scripting Runtime은 native device interface에 접근하기 위한 hardware abstraction layer를 제공해야 합니다. 이러한 hardware abstraction layer는 장치(또는 환경)를 안전하지 않은 상태로 만들 수 있는 명령 실행을 거부해야 합니다. 또한 script가 손상된 경우 물리적 WoT device에 대한 피해를 줄이기 위해, 특정 script의 기능에 기반하여 노출되거나 접근 가능한 interface의 수를 최소화하는 것이 중요합니다.

11.1.3 프로비저닝 및 업데이트 보안 위험

WoT Scripting Runtime이 제조 이후의 script 프로비저닝이나 업데이트, WoT Scripting Runtime 또는 관련 데이터(보안 자격 증명 포함)를 지원한다면, 이는 주요 공격 벡터가 될 수 있습니다. 공격자는 업데이트 또는 프로비저닝 과정에서 위에서 설명한 요소 중 하나를 수정하려 하거나, 공격자의 코드와 데이터를 직접 프로비저닝하려 할 수 있습니다.

완화책:
제조 이후 script, WoT Scripting Runtime 또는 관련 데이터의 프로비저닝이나 업데이트는 안전한 방식으로 수행되어야 합니다. 안전한 업데이트와 제조 이후 프로비저닝을 위한 권장사항 세트는 [WOT-SECURITY]에서 찾을 수 있습니다.

11.1.4 보안 자격 증명 저장 보안 및 프라이버시 위험

일반적으로 WoT Scripting Runtime은 WoT network에서 작동하기 위해 WoT device에 프로비저닝된 보안 자격 증명을 저장해야 합니다. 공격자가 이러한 자격 증명의 기밀성 또는 무결성을 손상시킬 수 있다면, WoT assets에 접근하거나, WoT things 또는 devices를 가장하거나, Denial-Of-Service(DoS) 공격을 만들 수 있습니다.

완화책:
WoT Scripting Runtime은 프로비저닝된 보안 자격 증명을 안전하게 저장하여 그 무결성과 기밀성을 보장해야 합니다. 단일 WoT-enabled device에 둘 이상의 tenant가 있는 경우, WoT Scripting Runtime은 각 tenant의 프로비저닝된 보안 자격 증명의 격리를 보장해야 합니다. 또한 프로비저닝된 보안 자격 증명이 손상될 위험을 최소화하기 위해, WoT Scripting Runtime은 script가 프로비저닝된 보안 자격 증명을 질의할 수 있는 API를 노출하지 않아야 합니다.

11.2 Script 보안 및 프라이버시 위험

이 섹션은 비규범적입니다.

이 섹션은 script 개발자와 관련된 구체적인 위험을 설명합니다.

11.2.1 손상된 Script 입력 보안 및 프라이버시 위험

script instance는 TD가 정의한 데이터 형식 또는 애플리케이션이 정의한 데이터 형식을 수신할 수 있습니다. WoT Scripting Runtime은 TD가 정의한 모든 입력 필드에 대해 검증을 수행해야 SHOULD 하지만, script는 여전히 입력 데이터에 의해 악용될 수 있습니다.

완화책:
Script 개발자는 애플리케이션이 정의한 모든 script 입력에 대해 검증을 수행해야 합니다. 입력 검증에 더해, 입력 처리가 올바르게 수행되는지 확인하기 위해 fuzzing을 사용할 수 있습니다. 이러한 검증을 수행하기 위한 많은 도구와 기법이 존재합니다. 자세한 내용은 [WOT-SECURITY]에서 찾을 수 있습니다.

11.2.2 Denial Of Service 보안 위험

요청이 인증되기 전에 script가 수신한 요청에 대해 무거운 기능 처리를 수행하면, Denial-Of-Service(DOS) 공격에 큰 위험을 제공합니다.

완화책:
Scripts는 요청자의 성공적인 사전 인증 없이 무거운 기능 처리를 피해야 합니다. 권장되는 인증 메커니즘 세트는 [WOT-SECURITY]에서 찾을 수 있습니다.

A. API 설계 근거

API 근거는 보통 별도의 문서에 속하지만, WoT의 경우 context의 복잡성이 여기에 기본 근거를 포함하는 것을 정당화합니다.

A.1 WoT 애플리케이션 개발 접근 방식

WoT Interest Group과 Working Group은 모두 구현되고 테스트된 WoT 애플리케이션 개발을 위한 다양한 접근 방식을 탐구했습니다.

A.1.1 Scripting API 없음

WoT network interface만 사용하는 WoT 애플리케이션을 개발할 수 있습니다. 이는 일반적으로 클라이언트에게 RESTful API를 제공하고 지원되는 IoT 배포와 통신하는 IoT protocol plugins를 구현하는 WoT gateway에 의해 노출됩니다. 그러한 구현 중 하나는 Mozilla WebThings 플랫폼입니다.

A.1.2 단순 Scripting API

WoT Thing은 software object와 좋은 시너지를 보이므로, Thing은 software object로 표현될 수 있으며, Properties는 object properties로, Action은 methods로, Event는 events로 표현됩니다. 또한 metadata는 특수 properties에 저장됩니다. consuming 및 exposing은 remote Thing과 그 interactions를 직접 나타내는 software object를 생성하는 factory methods로 수행됩니다. 그러한 구현 중 하나는 Arena Web Hub 프로젝트입니다.

다음 예제에서 lock과의 interactions를 나타내는 Thing은 다음과 같습니다: status 속성과 open() 메서드는 객체에 직접 노출됩니다.

예제 12: 단순 API로 lock 열기
let lock = await WoT.consume(‘https://td.my.com/lock-00123’);
console.log(lock.status);
lock.open('withThisKey');

A.1.3 Web of Things (WoT) Thing Description 1.1 명세와 정렬된 이 API

Thing을 software object로 직접 매핑하는 방식에는 몇 가지 어려움이 있었기 때문에, 이 명세는 Thing metadata를 data property로, WoT interactions를 methods로 표현하기 위해 software object를 노출하는 다른 접근 방식을 취합니다. 한 구현은 이 문서에 명시된 API의 현재 참조 구현인 node-wot이며, 이는 Eclipse ThingWeb 프로젝트에 속합니다.

이제 같은 예제는 다음과 같이 보입니다: status 속성과 open() 메서드는 간접적으로 표현됩니다.

예제 13: lock 열기
let res = await fetch(‘https://td.my.com/lock-00123’);
let td = await res.json();
let lock = new ConsumedThing(td);
console.log(lock.readProperty(‘status’));
lock.invokeAction(‘open’, 'withThisKey');

결론적으로, WoT WG는 Web of Things (WoT) Thing Description 1.1 명세를 밀접하게 따르는 세 번째 옵션을 탐구하기로 결정했습니다. 이를 기반으로 단순 API도 구현할 수 있습니다. Scripting은 WoT의 선택적 모듈이므로, 이는 WoT network interface만 사용하는 애플리케이션을 위한 여지를 남깁니다. 따라서 위의 세 가지 접근 방식 모두 Web of Things (WoT) Thing Description 1.1 명세에서 지원됩니다.

또한 WoT network interface는 많은 언어와 runtime에서 구현될 수 있습니다. 이 API를 WoT를 위한 Scripting API를 설계할 때 고려해야 할 사항의 예로 간주하십시오.

A.2 TD 가져오기 및 검증

fetch(url) 메서드는 이 API의 이전 버전에 포함되어 있었습니다. 그러나 이제 URL이 주어진 TD를 가져오는 것은 Fetch API 또는 HTTP client library와 같은 외부 메서드로 수행되어야 하며, 이러한 메서드는 fetch 세부사항을 지정하기 위한 이미 표준화된 옵션을 제공합니다. 그 이유는 단순 fetch 작업(대부분의 사용 사례를 포함)은 이 API에서 수행될 수 있었지만, 다양한 fetch 옵션이 필요할 때 기존 작업을 중복하여 이 API에서 그러한 옵션을 다시 노출할 이유가 없었기 때문입니다.

TD 가져오기가 범위에서 제외되었고, TD 검증도 Web of Things (WoT) Thing Description 1.1 명세에서 외부적으로 정의되므로, 그것 역시 범위에서 제외됩니다. 이 명세는 Web of Things (WoT) Thing Description 1.1 명세에 따라 검증된 parsed JSON object로서의 TD를 기대합니다.

A.3 Factory와 constructors

Thing을 consume하고 expose하는 factory methods는 비동기적이며 입력 TD를 완전히 검증합니다. 또한, parsed되고 검증된 TD를 제공하여 ConsumedThingExposedThing도 구성할 수 있습니다. 그러면 platform 초기화는 WoT interactions 중 필요할 때 수행됩니다.

A.4 Observers

이전 초안은 Observer 구조를 사용했지만, 표준이 되지 않았기 때문에 embedded implementations에 충분히 가벼운 새로운 설계가 필요했습니다. 따라서 Property 변경 관찰과 WoT Event 처리는 callback registrations로 수행됩니다.

A.5 Events 사용하기

이 API는 결국 software events를 전혀 사용하지 않게 되었으며, 그 이유는 다음과 같습니다:
  • WoT Event 구독은 software events 처리와 다를 수 있습니다 (구독에 parameters가 필요하거나, security token 등이 관련될 수 있습니다).
  • 대부분의 구현은 Node.js용이며, browser 구현은 (native implementations에서 가능한 dependency management 문제 때문에) library가 될 가능성이 높아 Events 사용이 어려웠습니다.
  • Property 변경 관찰과 WoT Event 처리는 위의 해결책으로 수행됩니다.

A.6 Polymorphic functions

일반적인 polymorphic read() 함수 대신 readProperty(), readMultipleProperties() 등의 함수 이름을 사용하는 이유는, 현재 이름들이 Web of Things (WoT) Thing Description 1.1 명세의 Form 정의에서 가져온 "op" vocabulary에 정확히 매핑되기 때문입니다.

B. 변경사항

다음은 이 문서의 주요 변경사항 목록입니다. 이 명세의 주요 버전은 다음과 같습니다:

전체 변경사항 목록은 github change log를 참조하십시오. 또한 최근 닫힌 issues를 볼 수 있습니다.

C. 전체 Web IDL

WebIDLtypedef object ThingDescription;

[SecureContext, Exposed=(Window,Worker)]
namespace WOT {
  // methods defined in UA conformance classes
};

partial namespace WOT {
  Promise<ConsumedThing> consume(ThingDescription td);
};

typedef object ExposedThingInit;

partial namespace WOT {
  Promise<ExposedThing> produce(ExposedThingInit init);
};

partial namespace WOT {
  Promise<ThingDiscoveryProcess> discover(optional ThingFilter filter = {});
};

partial namespace WOT {
  Promise<ThingDiscoveryProcess> exploreDirectory(USVString url,
      optional ThingFilter filter = {});
};

partial namespace WOT {
  Promise<ThingDescription> requestThingDescription(USVString url);
};

typedef any DataSchemaValue;
typedef (ReadableStream or DataSchemaValue) InteractionInput;

[SecureContext, Exposed=(Window,Worker)]
interface InteractionOutput {
  readonly attribute ReadableStream? data;
  readonly attribute boolean dataUsed;
  readonly attribute Form? form;
  readonly attribute DataSchema? schema;
  Promise<ArrayBuffer> arrayBuffer();
  Promise<DataSchemaValue> value();
};

[SecureContext, Exposed=(Window,Worker)]
interface ConsumedThing {
  constructor(ThingDescription td);
  Promise<InteractionOutput> readProperty(DOMString propertyName,
                              optional InteractionOptions options = {});
  Promise<PropertyReadMap> readAllProperties(
                              optional InteractionOptions options = {});
  Promise<PropertyReadMap> readMultipleProperties(
                              sequence<DOMString> propertyNames,
                              optional InteractionOptions options = {});
  Promise<undefined> writeProperty(DOMString propertyName,
                              InteractionInput value,
                              optional InteractionOptions options = {});
  Promise<undefined> writeMultipleProperties(
                              PropertyWriteMap valueMap,
                              optional InteractionOptions options = {});
  /*Promise<undefined> writeAllProperties(
                              PropertyWriteMap valueMap,
                              optional InteractionOptions options = {});*/
  Promise<InteractionOutput> invokeAction(DOMString actionName,
                              optional InteractionInput params = {},
                              optional InteractionOptions options = {});
  Promise<Subscription> observeProperty(DOMString name,
                              InteractionListener listener,
                              optional ErrorListener onerror,
                              optional InteractionOptions options = {});
  Promise<Subscription> subscribeEvent(DOMString name,
                              InteractionListener listener,
                              optional ErrorListener onerror,
                              optional InteractionOptions options = {});
  ThingDescription getThingDescription();
};

dictionary InteractionOptions {
  unsigned long formIndex;
  object uriVariables;
  any data;
};

[SecureContext, Exposed=(Window,Worker)]
interface Subscription {
  readonly attribute boolean active;
  Promise<undefined> stop(optional InteractionOptions options = {});
};

[SecureContext, Exposed=(Window,Worker)]
interface PropertyReadMap {
  readonly maplike<DOMString, InteractionOutput>;
};

[SecureContext, Exposed=(Window,Worker)]
interface PropertyWriteMap {
  readonly maplike<DOMString, InteractionInput>;
};

callback InteractionListener = undefined(InteractionOutput data);
callback ErrorListener = undefined(Error error);

[SecureContext, Exposed=(Window,Worker)]
interface ExposedThing {
  ExposedThing setPropertyReadHandler(DOMString name,
          PropertyReadHandler handler);
  ExposedThing setPropertyWriteHandler(DOMString name,
          PropertyWriteHandler handler);
  ExposedThing setPropertyObserveHandler(DOMString name,
          PropertyReadHandler handler);
  ExposedThing setPropertyUnobserveHandler(DOMString name,
          PropertyReadHandler handler);
  Promise<undefined> emitPropertyChange(DOMString name,
          optional InteractionInput data);

  ExposedThing setActionHandler(DOMString name, ActionHandler action);

  ExposedThing setEventSubscribeHandler(DOMString name,
          EventSubscriptionHandler handler);
  ExposedThing setEventUnsubscribeHandler(DOMString name,
          EventSubscriptionHandler handler);
  Promise<undefined> emitEvent(DOMString name,
          optional InteractionInput data);

  Promise<undefined> expose();
  Promise<undefined> destroy();

  ThingDescription getThingDescription();
};

callback PropertyReadHandler = Promise<InteractionInput>(
        optional InteractionOptions options = {});

callback PropertyWriteHandler = Promise<undefined>(
        InteractionOutput value,
        optional InteractionOptions options = {});

callback ActionHandler = Promise<InteractionInput>(
        InteractionOutput params,
        optional InteractionOptions options = {});

callback EventSubscriptionHandler = Promise<undefined>(
        optional InteractionOptions options = {});

[SecureContext, Exposed=(Window,Worker)]
interface ThingDiscoveryProcess {
  constructor(optional ThingFilter filter = {});
  readonly attribute boolean done;
  readonly attribute Error? error;
  undefined stop();
  async iterable<ThingDescription>;
};

dictionary ThingFilter {
  object? fragment;
  
};

D. 감사의 말

이 명세를 개발한 이전 편집자 Johannes Hund(2017년 8월까지, 당시 Siemens AG 재직)와 Kazuaki Nimura(2018년 12월까지)에게 특별한 감사를 표합니다. 또한 편집자들은 Dave Raggett, Matthias Kovatsch, Michael Koster, Elena Reshetova, Michael McCool 및 다른 WoT WG 구성원들의 의견, 기여와 지침에 감사드립니다.

E. 참고문헌

E.1 규범 참고문헌

[ECMASCRIPT]
ECMAScript 언어 명세. Ecma International. URL: https://tc39.es/ecma262/multipage/
[fetch]
Fetch Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://fetch.spec.whatwg.org/
[html]
HTML Standard. Anne van Kesteren; Domenic Denicola; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[JSON-SCHEMA]
JSON Schema: JSON 문서를 설명하기 위한 Media Type. Austin Wright; Henry Andrews; Ben Hutton; Greg Dennis. Internet Engineering Task Force (IETF). 2020년 12월 8일. Internet-Draft. URL: https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema
[RFC2119]
RFC에서 요구 수준을 나타내기 위해 사용하는 핵심 단어. S. Bradner. IETF. 1997년 3월. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
RFC 2119 핵심 단어에서 대문자와 소문자의 모호성. B. Leiba. IETF. 2017년 5월. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[streams]
Streams Standard. Adam Rice; Domenic Denicola; Mattias Buelens; 吉野剛史 (Takeshi Yoshino). WHATWG. Living Standard. URL: https://streams.spec.whatwg.org/
[TYPESCRIPT]
TypeScript 언어 명세. Microsoft. 2012년 10월 1일. URL: https://www.typescriptlang.org/docs/handbook/intro.html
[url]
URL Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/
[WOT-ARCHITECTURE]
Web of Things (WoT) Architecture 1.1. W3C. 2023년 1월 19일. URL: https://www.w3.org/TR/2023/CR-wot-architecture11-20230119/
[WOT-PROTOCOL-BINDINGS]
Web of Things (WoT) Binding Templates. W3C. 2020년 1월 30일. URL: https://www.w3.org/TR/2020/NOTE-wot-binding-templates-20200130/
[WOT-SECURITY]
Web of Things (WoT) Security and Privacy Guidelines. W3C. 2019년 11월 6일. URL: https://www.w3.org/TR/2019/NOTE-wot-security-20191106/
[WOT-TD]
Web of Things (WoT) Thing Description 1.1. W3C. 2023년 1월 19일. URL: https://www.w3.org/TR/2023/CR-wot-thing-description11-20230119/

E.2 정보성 참고문헌

[WOT-DISCOVERY]
Web of Things (WoT) Discovery. W3C. 2023년 1월 19일. URL: https://www.w3.org/TR/2023/CR-wot-discovery-20230119/
[WOT-USE-CASES]
Web of Things (WoT): Use Cases and Requirements. W3C. 2022년 3월 7일. URL: https://www.w3.org/TR/2022/NOTE-wot-usecases-20220307/