Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

wirone

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

wirone

Wirone is a micro-framework for creating Yandex Smarthome Adapter API

  • 1.0.0-1
  • latest
  • npm
  • Socket score

Version published
Weekly downloads
5
increased by150%
Maintainers
1
Weekly downloads
 
Created
Source

wirone

Wirone - микро-фреймворк для работы с умным домом Яндекса и реализации Yandex Smarthome Adapter API. Основан на Express.js

Возможности

  • Встроенная реализация сервера OAuth авторизации

  • Поддержка query и action обработчиков для умений и встроенных датчиков устройства, что избавляет от надобности формировать JSON-объект с полной информацией об устройстве:

    const info = {
        name: "Чайник",
        type: "devices.types.cooking.kettle",
    
        capabilities: [
            OnOffCapability({
                // Функция, реализованная как Promise, которая возвращает текущее состояние умения OnOff
                onQuery: powerQuery,
                // Функция, реализованная как Promise, которая обрабатывает изменение состояния умения OnOff
                onAction: powerAction
            }),
            RangeCapability({
                parameters: {
                    instance: "temperature",
                    unit: "unit.temperature.celsius",
                    range: {
                        min: 60,
                        max: 100
                    }
                },
    
                // Функция, реализованная как Promise, которая возвращает текущее состояние умения Range (целевая температура воды)
                onQuery: temperatureQuery,
                // Функция, реализованная как Promise, которая обрабатывает изменение состояния умения Range
                onAction: temperatureAction
            })
        ]
    }
    
  • Описание устройств в виде независимых модулей:

    const OnOffCapability = require("wirone").capabilities.OnOff;
    
    const ledBulb = () => {
        const powerQuery = () => new Promise((resolve, reject) => {
            // Your awesome code
        });
    
        const powerAction = (state) => new Promise((resolve, reject) => {
            // Your awesome code
        });
    
        const info = {
            name: "Лампочка",
            type: "devices.types.light",
    
            capabilities: [
                OnOffCapability({
                    onQuery: powerQuery,
                    onAction: powerAction
                })
            ]
        }
    
        return Object.freeze({
            info
        });
    }
    
    module.exports = ledBulb();
    

Быстрый старт

Для демонстрации возможностей и примеров кода был создан репозиторий с шаблоном приложения.

Шаблон содержит базовую реализацию сервера, включая готовые обработчики OAuth авторизации, связь с базой данных MySQL, и описание трех устройств для умного дома.

Инициализация Wirone

Чтобы использовать Wirone в вашем приложении, вам необходимо установить Express.js, а также инициализировать Wirone с определенной конфигурацией.

Пример кода для инициализации Wirone:

const fs = require("fs");
const https = require("https");

const express = require("express");
const app = express();
const wirone = require("wirone");

// Объекты пользовательских устройств
const devices = {
    ledBulb: require("./src/devices/ledBulb.js"),
    switch: require("./src/devices/switch.js")
};

app.use(express.json());
app.use(express.urlencoded({extended: true}));

// Конфигурация Wirone
wirone.init(app, {
    // Обязательные параметры помечены символом *

    // debug - включает отладочные сообщения об OAuth авторизации, запросах к устройствам и т.д.
    // По умолчанию: false
    debug: true,

    // oauth* - конфигурация OAuth авторизации
    oauth: {
        // client* - идентификатор приложения (client identifier) для реализации связки аккаунтов через OAuth
        client: "wirone",

        // secret* - секрет приложения (client password) для реализации связки аккаунтов через OAuth
        secret: "your_secret_here",

        // lifetime - время жизни токена в секундах
        // По умолчанию: 3600
        lifetime: 3600,

        // authorization_page* - объект с описанием типа используемой страницы для авторизации пользователей
        authorization_page: {
            type: "static_page",
            path: path.join(__dirname + "/static/oauth/index.html")
        },

        // Обрабочики, реализующие логику авторизации*
        onAuthorize: oauth.generateCode,
        onGranted: oauth.saveAccessToken,
        onRefresh: oauth.refreshAccessToken,
        onVerify: oauth.verifyAccessToken
    }
});

// Обработчик для передачи информации об устройствах пользовател
wirone.query((userID) => new Promise((resolve, reject) => {
    // Данная реализация обработчика является не более, чем примером для простоты понимания работы
    if (userID == 1)
        // Возвращаем устройства для пользователя с ID 1
        resolve([
            devices.ledBulb,
            devices.switch
        ]);
    else
        reject("Access denied for user with ID '" + userID + "'");
}));

https.createServer({
    key: fs.readFileSync("./static/ssl/privkey.pem"),
    ca: fs.readFileSync("./static/ssl/chain.pem"),
    cert: fs.readFileSync("./static/ssl/cert.pem")
}, app).listen(443, "0.0.0.0", 10, () => {
    console.log("> Server ready");
});

Реализация обработчиков OAuth авторизации:

Для понимания работы обработчиков OAuth авторизации рекомендуется посмотреть их реализацию в репозитории с шаблоном приложения.

Описание пользовательских устройств

Устройства в wirone представляют собой независимые модули (функциональные объекты), которые, как правило, содержат объект с информацией об устройстве и описание обработчиков, для умений и встроенных датчиков.

Объект с информацией об устройстве описывается используя параметры из документации, а также используя объекты умений (capabilities) и встроенных датчиков (properties).

Рассмотрим пример описания умной лампочки, которая находится в локальной сети, и поддерживает управление через REST-подобное API:

// Файл ledBulb.js

const request = require("request");

const OnOffCapability = require("wirone").capabilities.OnOff;

const ledBulb = () => {
    // Обработчик текущего состояния (query) для умения On_off
    const powerQuery = () => new Promise((resolve, reject) => {
        // Запрос к устройству в локальной сети
        request({
            url: "http://192.168.0.110/state",
            method: "get"
        }, (error, response, body) => {
            /*
                Представим, что в ответ устройство возвращает JSON объект вида:
                
                {
                    "power": true,
                    "color": {
                        "r": 255,
                        "g": 42,
                        "b": 14
                    }
                }
                
                Объект содержит информацию о том, включено ли устройство, а также объект с интенсивностью свечения
                оттенков красного, зеленого и синего, от 0 до 255
            */
            
            // Если в процессе запроса произошла ошибка, необходимо вызвать reject с кодом ошибки из документации
            // Подробное описание кодов ошибок находится здесь: https://yandex.ru/dev/dialogs/smart-home/doc/concepts/response-codes.html
            if (error != null)
                return reject("INTERNAL_ERROR");

            // При успешном выполнении запроса записываем полученные данные в объект состояния, который формируется
            // в соответствии с используемым умением или встроенным датчком
      
            // Пример объекта для умения On_off: https://yandex.ru/dev/dialogs/smart-home/doc/concepts/on_off.html#state__parameters
            resolve({
                instance: "on",
                value: body.power
            });
        });
    });

    // Обработчик изменения состояния (action) для умения On_off
    const powerAction = (state) => new Promise((resolve, reject) => {
        // Параметр state содержит структуру, идентичную объекту state в примере запроса изменения
        // состояния умения On_off: https://yandex.ru/dev/dialogs/smart-home/doc/concepts/on_off.html#action__example
        
        let newPowerState = state.value;

        request({
            url: "http://192.168.0.110/state",
            method: "post",
            json: {
                // Устанавливаем новое состояние устройству в локальной сети, т.е. включаем лампочку
                power: newPowerState
            }
        }, (error, response, body) => {
            if (error != null)
                return reject("INTERNAL_ERROR");

            // В случае успеха вызывается resolve с передачей объекта, который описывает результат изменения состояния умения
            // Пример объекта для умения On_off: https://yandex.ru/dev/dialogs/smart-home/doc/concepts/on_off.html#action__parameters
            resolve({
                instance: "on",
                action_result: {
                    status: "DONE"
                }
            });
        });
    });

    // Объект с информацией об устройстве
    // Может включать в себя параметры, описанные в документации: https://yandex.ru/dev/dialogs/smart-home/doc/reference/get-devices.html#output-structure
    const info = {
        // Имя устройства
        name: "Лампочка",
        
        // Тип устройства
        type: "devices.types.light",

        // Массив с описанием умений устройства
        capabilities: [
            // Описание умения On_off
            
            // При описании умений и встроенных датчиков используется объект с параметрами, приведенный в документации,
            // например: https://yandex.ru/dev/dialogs/smart-home/doc/concepts/on_off.html#discovery__parameters
            OnOffCapability({
                // Установка обработчиков для умения
                onQuery: powerQuery,
                onAction: powerAction
            })
        ]
    }

    // Устройство обязательно должно возвращать объект с информацией о нем
    return Object.freeze({
        info
    });
}

module.exports = ledBulb();

Можно также добавить возможность управлять цветом нашей лампочки, используя умение Color_setting:

// Файл ledBulb.js

const request = require("request");

const OnOffCapability = require("wirone").capabilities.OnOff;
const ColorCapability = require("wirone").capabilities.ColorSetting;

const ledBulb = () => {
    const powerQuery = () => new Promise((resolve, reject) => {
        request({
            url: "http://192.168.0.110/state",
            method: "get"
        }, (error, response, body) => {
            if (error != null)
                return reject("INTERNAL_ERROR");

            resolve({
                instance: "on",
                value: body.power
            });
        });
    });

    const powerAction = (state) => new Promise((resolve, reject) => {
        let newPowerState = state.value;

        request({
            url: "http://192.168.0.110/state",
            method: "post",
            json: {
                power: newPowerState
            }
        }, (error, response, body) => {
            if (error != null)
                return reject("INTERNAL_ERROR");
                
            resolve({
                instance: "on",
                action_result: {
                    status: "DONE"
                }
            });
        });
    });
    
    // Обработчик текущего состояния (query) для умения Color_setting
    const colorQuery = () => new Promise((resolve, reject) => {
        request({
            url: "http://192.168.0.110/state",
            method: "get"
        }, (error, response, body) => {
            if (error != null)
                return reject("INTERNAL_ERROR");

            resolve({
                instance: "rgb",
                value: {
                    r: body.color.r,
                    g: body.color.g,
                    b: body.color.b
                }
            });
        });
    });
    
    // Обработчик изменения состояния (action) для умения Color_setting
    const colorAction = (state) => new Promise((resolve, reject) => {
        let newColorState = state.value;

        request({
            url: "http://192.168.0.110/state",
            method: "post",
            json: {
                color: newColorState
            }
        }, (error, response, body) => {
            if (error != null)
                return reject("INTERNAL_ERROR");
                
            resolve({
                instance: "rgb",
                action_result: {
                    status: "DONE"
                }
            });
        });
    });

    const info = {
        name: "Лампочка",
        type: "devices.types.light",

        capabilities: [
            OnOffCapability({
                onQuery: powerQuery,
                onAction: powerAction
            }),
            
            // Описание умения Color_setting
            ColorCapability({
                // Используем цветовую модель RGB
                parameters: {
                    color_model: "rgb"
                },

                onQuery: colorQuery,
                onAction: colorAction
            })
        ]
    }

    return Object.freeze({
        info
    });
}

module.exports = ledBulb();

Чтобы не дублировать отправку запросов для получения текущего состояния устройства, можно использовать обработчик globalQuery:

// Файл ledBulb.js

const request = require("request");

const OnOffCapability = require("wirone").capabilities.OnOff;
const ColorCapability = require("wirone").capabilities.ColorSetting;

const ledBulb = () => {
    // Функция globalQuery выполняется при запросе состояния устройства, и позволяет установить глобальное состояние,
    // данные из которого в дальнейшем можно использовать для присвоения значений умений и встроенных датчиков
    const globalQuery = () => new Promise((resolve, reject) => {
        request({
            url: "http://192.168.0.110/state",
            method: "get"
        }, (error, response, body) => {
            if (error != null)
                return reject("INTERNAL_ERROR");

            // Устанавливаем полученный от устройства JSON-объект как глобальное состояние
            resolve(body);
        });
    });

    const powerQuery = (globalState) => new Promise((resolve, reject) => {
        // Если была использована функция globalQuery, объект, который был передан в resolve, доступен через параметр globalState
        // в любом обработчике состояния
        
        resolve({
            instance: "on",
            // Установка значения для умения On_off из глобального состояния
            value: globalState.power
        });
    });

    const powerAction = (state) => new Promise((resolve, reject) => {
        let newPowerState = state.value;

        request({
            url: "http://192.168.0.110/state",
            method: "post",
            json: {
                power: newPowerState
            }
        }, (error, response, body) => {
            if (error != null)
                return reject("INTERNAL_ERROR");
                
            resolve({
                instance: "on",
                action_result: {
                    status: "DONE"
                }
            });
        });
    });
    
    const colorQuery = (globalState) => new Promise((resolve, reject) => {
        resolve({
            instance: "rgb",
            // Аналогично, устанавливаем значение цвета для умения Color_setting
            value: {
                r: globalState.color.r,
                g: globalState.color.g,
                b: globalState.color.b
            }
        });
    });
    
    const colorAction = (state) => new Promise((resolve, reject) => {
        let newColorState = state.value;

        request({
            url: "http://192.168.0.110/state",
            method: "post",
            json: {
                color: newColorState
            }
        }, (error, response, body) => {
            if (error != null)
                return reject("INTERNAL_ERROR");
                
            resolve({
                instance: "rgb",
                action_result: {
                    status: "DONE"
                }
            });
        });
    });

    const info = {
        name: "Лампочка",
        type: "devices.types.light",

        globalQuery: globalQuery,

        capabilities: [
            OnOffCapability({
                onQuery: powerQuery,
                onAction: powerAction
            }),
            
            ColorCapability({
                parameters: {
                    color_model: "rgb"
                },

                onQuery: colorQuery,
                onAction: colorAction
            })
        ]
    }

    return Object.freeze({
        info
    });
}

module.exports = ledBulb();

Другой пример описание устройства вы можете посмотреть в репозитории шаблона для Wirone.

Планы развития на будущее

Планируется реализация:

  • Инструментов для удобного взаимодействия с API сервиса уведомлений
  • Поддержки встроенных датчиков с типом Event, когда их функционал выйдет из стадии бета-тестирования

Keywords

FAQs

Package last updated on 10 Dec 2021

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc