From 65c87b02e69d4755b5db4fdab8c357014e950f84 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 23 Mar 2022 22:38:36 -0700 Subject: [PATCH] alexa: doorbells --- plugins/alexa/src/main.ts | 43 +++++++++++++++++++++++++++-- plugins/alexa/src/types/camera.ts | 3 ++ plugins/alexa/src/types/common.ts | 17 ++++++++++-- plugins/alexa/src/types/doorbell.ts | 29 ++++++++++++++++--- plugins/alexa/src/types/index.ts | 1 + 5 files changed, 84 insertions(+), 9 deletions(-) diff --git a/plugins/alexa/src/main.ts b/plugins/alexa/src/main.ts index 30401845a..03789aa9c 100644 --- a/plugins/alexa/src/main.ts +++ b/plugins/alexa/src/main.ts @@ -2,10 +2,9 @@ import axios from 'axios'; import sdk, { HttpRequest, HttpRequestHandler, HttpResponse, MixinProvider, ScryptedDevice, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface } from '@scrypted/sdk'; import { StorageSettings } from '@scrypted/common/src/settings'; import { AutoenableMixinProvider } from '@scrypted/common/src/autoenable-mixin-provider'; -import crypto from 'crypto'; import { isSupported } from './types'; import { DiscoveryEndpoint, DiscoverEvent } from 'alexa-smarthome-ts'; -import { AlexaHandler, capabilityHandlers } from './types/common'; +import { AlexaHandler, capabilityHandlers, supportedTypes } from './types/common'; import { createMessageId } from './message'; const { systemManager, deviceManager } = sdk; @@ -35,6 +34,44 @@ class AlexaPlugin extends AutoenableMixinProvider implements HttpRequestHandler, this.handlers.set('Alexa.Discovery', this.alexaDiscovery); this.syncDevices(); + + systemManager.listen(async (eventSource, eventDetails, eventData) => { + if (!this.storageSettings.values.syncedDevices.includes(eventSource.id)) + return; + + const supportedType = supportedTypes.get(eventSource.type); + if (!supportedType) { + this.console.warn(`${eventSource.name} no longer supported type?`); + return; + } + + const report = await supportedType.reportState(eventSource, eventDetails, eventData); + + if (report?.type === 'event') { + const accessToken = await this.getAccessToken(); + const data = { + "context": {}, + "event": { + "header": { + "messageId": createMessageId(), + "namespace": report.namespace, + "name": report.name, + "payloadVersion": "3" + }, + "endpoint": { + "scope": { + "type": "BearerToken", + "token": accessToken, + }, + "endpointId": eventSource.id, + }, + payload: report.payload, + } + } + + await this.postEvent(accessToken, data); + } + }); } async getMixin(mixinDevice: any, mixinDeviceInterfaces: ScryptedInterface[], mixinDeviceState: { [key: string]: any; }): Promise { @@ -237,7 +274,7 @@ class AlexaPlugin extends AutoenableMixinProvider implements HttpRequestHandler, } async saveEndpoints(endpoints: DiscoveryEndpoint[]) { - const existingEndpoints: string[] = this.storageSettings.values.syncedDevices; + const existingEndpoints: string[] = this.storageSettings.values.syncedDevices; const newEndpoints = endpoints.map(endpoint => endpoint.endpointId); const deleted = new Set(existingEndpoints); diff --git a/plugins/alexa/src/types/camera.ts b/plugins/alexa/src/types/camera.ts index 333de7391..a4fe011eb 100644 --- a/plugins/alexa/src/types/camera.ts +++ b/plugins/alexa/src/types/camera.ts @@ -25,6 +25,9 @@ addSupportedType(ScryptedDeviceType.Camera, { }, ], } + }, + async reportState() { + return undefined; } }); diff --git a/plugins/alexa/src/types/common.ts b/plugins/alexa/src/types/common.ts index 5bfa77d81..fe219d6d4 100644 --- a/plugins/alexa/src/types/common.ts +++ b/plugins/alexa/src/types/common.ts @@ -1,14 +1,27 @@ -import { HttpRequest, HttpResponse, ScryptedDevice, ScryptedDeviceType } from "@scrypted/sdk"; +import { EventDetails, HttpRequest, HttpResponse, ScryptedDevice, ScryptedDeviceType } from "@scrypted/sdk"; import {DiscoveryEndpoint, Directive} from 'alexa-smarthome-ts'; export type AlexaHandler = (request: HttpRequest, response: HttpResponse, directive: Directive) => Promise export type AlexaCapabilityHandler = (request: HttpRequest, response: HttpResponse, directive: Directive, device: ScryptedDevice & T) => Promise -const supportedTypes = new Map(); +export const supportedTypes = new Map(); export const capabilityHandlers = new Map>(); +export interface EventReport { + type: 'event'; + payload: any; + namespace: string; + name: string; +} + +export interface StateReport { + type: 'state'; + payload: any; +} + export interface SupportedType { probe(device: ScryptedDevice): Partial>; + reportState(eventSource: ScryptedDevice, eventDetails: EventDetails, eventData: any): Promise; } export function addSupportedType(type: ScryptedDeviceType, supportedType: SupportedType) { diff --git a/plugins/alexa/src/types/doorbell.ts b/plugins/alexa/src/types/doorbell.ts index e4b292c76..44966bf35 100644 --- a/plugins/alexa/src/types/doorbell.ts +++ b/plugins/alexa/src/types/doorbell.ts @@ -1,13 +1,13 @@ -import sdk, { ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk"; -import { addSupportedType } from "./common"; +import sdk, { BinarySensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk"; +import { addSupportedType, EventReport } from "./common"; addSupportedType(ScryptedDeviceType.Doorbell, { probe(device) { - if (!device.interfaces.includes(ScryptedInterface.VideoCamera)) + if (!device.interfaces.includes(ScryptedInterface.VideoCamera) || !device.interfaces.includes(ScryptedInterface.BinarySensor)) return; return { - displayCategories: ['DOORBELL'], + displayCategories: ['CAMERA'], capabilities: [ { "type": "AlexaInterface", @@ -17,7 +17,28 @@ addSupportedType(ScryptedDeviceType.Doorbell, { "isFullDuplexAudioSupported": false, } }, + { + "type": "AlexaInterface", + "interface": "Alexa.DoorbellEventSource", + "version": "3", + "proactivelyReported": true + } ], } + }, + async reportState(eventSource: ScryptedDevice & BinarySensor, eventDetails, eventData): Promise { + if (!eventSource.binaryState) + return undefined; + return { + type: 'event', + namespace: 'Alexa.DoorbellEventSource', + name: 'DoorbellPress', + payload: { + "cause": { + "type": "PHYSICAL_INTERACTION" + }, + "timestamp": new Date().toISOString(), + } + }; } }); diff --git a/plugins/alexa/src/types/index.ts b/plugins/alexa/src/types/index.ts index 13f526aa1..3a9c82cea 100644 --- a/plugins/alexa/src/types/index.ts +++ b/plugins/alexa/src/types/index.ts @@ -1,4 +1,5 @@ import './camera'; +import './doorbell'; export { isSupported} from './common';