Описание JavaSript API


Raspberri Pi with RaZberry extention board

Многие пытаются собрать "Умный дом" своими руками. При выборе системы стоит учитывать не только ассортимент и стоимость конечных устройств, но и возможности контроллера. Большинство контроллеров сразу готовы к работе "из коробки", но представляют ограниченные возможности. Однако нередко именно гибкость и возможность лёгкой интеграции является основополагающим критерием при выборе.

И вот, появился долгожданный "кубик Lego" для систем автоматизации на базе технологии Z-Wave, который обладает желаемой гибкостью и в то же время большим функционалом и низкой ценой.

Плата расширения RaZberry для Raspberry Pi, превращает самый популярный и дешёвый мини-компьютер в Z-Wave контроллер домашней автоматизации.

RaZberry — это решение три в одном:

  • плата с трансивером Z-Wave ZM3102
  • прошивка для ZM3102, расширяющая возможности стандартных прошивок для стиков
  • ПО Z-Way
RaZberry
Железо

В ОС Linux RaZberry виден как com-порт /dev/ttyAMA0. Плата по сути состоит из трансивера ZM3102, памяти EEPROM для хранения данных сети Z-Wave, PCBA антенны и коннектора ножек UART для GPIO Raspberry Pi. Реально используются только ножки Vcc, Gnd, TX и RX. Остальные ножки не используются для работы платы и лишь помогают её крепче зафиксировать. В теории эти ножки можно использовать для других нужд.

Прошивка

Плата предоставляет интерфейс полностью совместимый с Sigma Designs Serial API, что даёт возможность использовать не только прилагаемое ПО Z-Way, но и любое другое ПО для Z-Wave (Open Z-Wave, LinuxMCE, FHEM или самописное), а также облачный сервис Z-Cloud (см. инструкцию). Плата также предоставляет расширение протокола Sigma Designs Serial API от Z-Wave.Me, которое необходимо для работы ПО Z-Way.

Прошивка также позволяет обновлять себя по UART, что позволит не только исправить возможные баги в будущем, но и сменить версию прошивки с той, что установлена по умолчанию (SDK 4.54.01) на более старую 5.03, не имеющую полезных функций NWI, Explorer Frame и Random HomeId, но включающую функционал SUC/SIS. (Для внимательных: да, я не ошибся, 4 > 5 ;)

Программное обеспечение

Z-Way устанавливается поверх Raspbian wheezy путём запуска команды

wget ‐q ­‐O ­‐ http://razberry.z-­wave.me/install | sudo bash

Скрипт установки поставит Z-Way в /opt/z-way-server, пропишет в /etc/init.d/Z-Way скрипт для запуска, добавит его в авто запуск и настроит порт ttyAMA0. После перезагрузки (для применения настроек ядра для ttyAMA0 - по умолчанию Raspbian использует UART для консоли) сервер готов к работе. В браузере откройте http://IP_OF_RASPBERRY:8083 и начните строить сеть Z-Wave.

Рассмотрим подробней поставляемое в комплекте с RaZberry ПО Z-Way. Оно состоит из нескольких частей:

  • библиотека libzway — основа движка Z-Wave, предоставляющая C API для простой работы с Z-Wave (зависит от pthreads, zlib, openssl)
  • библиотека libzwayjs — биндинг между C и JavaScript движком Google V8. Предоставляет простое JS API для работы с сетью Z-Wave (зависит от libv8 и libzway)
  • библиотека libzwayhttp — HTTP сервис для обслуживания внешних клиентов, в том числе пользовательские интерфейсы. Предоставляет JSON API (зависит от libmicrohttpd, openssl, libzwayjs и libzway)
  • main.cpp — обвязка для запуска всех сервисов вместе, чтения конфигов и параметров командной строки

Такая структура позволяет использовать разные уровни интеграции с составными частями Z-Way. Для проектов, где важна производительность, доступен API уровня C (в комплекте есть заголовочные .h файлы для libzway), для проектов, где нужна оперативная разработка, проще использовать JS API. Важно, что оба интерфейса имеют сходную структуру, что позволяет просто портировать код с JavaScript на C после быстрого прототипирования.

Далее я опишу API уровня JavaScript и покажу, как создавать простые правила автоматизации. Интерфейс уровня C останется за рамками данной статьи. Стоит заметить, что синтаксис в движке JavaScript Z-Way на стороне сервера сделан по аналогии с тем, что было реализовано для Z-Cloud на стороне клиента — см. описание API Z-Cloud. Для дальнейшего чтива предполагается, что читатель уже знаком с особенностями протокола Z-Wave (см. подробное описание протокола Z-Wave).

Всё относящееся к Z-Wave в пространстве имён JavaScript находится в глобальном объекте zway, который предоставляет полное дерево данных и методов над устройствами Z-Wave.

Дочерний объект controller, содержит данные специфические для контроллера Z-Wave, а также список устройств devices. Каждое устройство содержит структуру данных (data), а также список каналов устройства (instances). Каждый канал имеет список Классов Команд Z-Wave, поддерживаемых устройством. На Классе Команд имеются методы для вызова функций (Set, Get, ...), а также дерево данных (data), где хранится вся информация о Классе.

Графически это можно иллюстрировать примерно так (картинка из документации):

Структура API асинхронна и делится на две части:

  • отправка команд и запросов
  • получение событий изменения состояний

Отправка команд осуществляется запросами вида zway.devices[2].instances[0].commandClasses.Basic.Set(0) (этот пример выключит свет) . Функция .Get() отправит запрос значения устройству. zway.devices[2].RequestNodeNeighbourUpdate() попросит устройство обновить соседей, а команда zway.AddNodeToNetwork(1) запустит процесс добавления нового устройства к сети. Более подробно все команды описаны в документации на странице документации проекта RaZberry. Каждому запросу можно добавить два дополнительных параметра: callback-функции для успешной и неудачной отправки.

Команды ставят в очередь на отправку соответствующие пакеты. Отправка осуществляется через Sigma Designs Serial API, реализованную на плате расширения RaZberry. Вся магия Z-Wave скрыта под этим уровнем абстракции: для устройств на батарейках эти пакеты помечаются как ожидающие пробуждения, для шифрования идёт предварительный обмен ключами, для экономии заряда батареек и времени в эфире некоторые пакеты стыкуются вместе и многое другое... Как и в любом протоколе, в Z-Wave много своих тараканов, которые все находятся под ковром API Z-Way и не вылезают оттуда.

Все полученные от устройств данные записываются в дерево data, относящееся к устройству или Классу Команд. Каждый элемент дерева data имеет атрибуты value (значение элемента), updateTime и invalidateTime (timestamp дат обновления и устаревания значения), name (имя). Структура дерева тоже подробно описана в документации. Для получения уведомлений об изменении значений дерева существует система подписок на события. Функция bind для каждого элемента дерева позволяет привязать callback функцию к изменению данных.

zway.devices[2].instances[0].commandClasses.Basic.data.level.bind(function (type[, arg]) {}, [arg, [watchChildren=false]]);

Опциональный аргумент arg, передаваемый bind будет передан в callback функцию и пригодится для передачи дополнительных параметров для разделения разных событий, имеющих общий callback-обработчик. Параметр watchChildren (true или false) позволяет отслеживать не только изменения данного элемента дерева, но и дочерних. Объект this в callback функции — это элемент дерева data, на котором запускается bind: this.value — значение, this.updateTime — дата последнего обовления и т.д. Аргумент type содержит битовую маску типа изменения (изменён, обновлён, удалён, устарел, ... - подробно все значения описаны в ZDefsPublic.h, который входит в комплект ПО).

Аналогично есть функция bind на глобальном объекте zway, которая позволяет отслеживать изменения в массивах devices, instances и commandClasses:

zway.bind(function(type, nodeId, instanceId, commandClassId) {}, [mask = 0xffff])

Аргумент type указывает на характер изменения (добавлено/удалено устройство/канал/класс команд или сохранён файл с деревом данных на диск (ZDDX-файл). Триплет nodeId, instanceId, commandClassId указывает на изенённый объект.

Отписаться от событий можно функцией unbind.

Для упрощения синтаксиса можно опускать слова commandClasses, instances[0] (именно нулевой канал!), .value (level == 0 аналогично level.value == 0, но при JSON-сериализации без .value будет сериализован весь элемент дерева с его атрибутами, а не только значение).

Стоит обратить внимание, что глобальный объект zway является нативным в v8, т.е. это не обычный JavaScript объект: у него можно лишь менять данные элементов дерева. Все остальные изменения в дереве игнорируются.

Приведём несколько примеров:

Запуск shell-команд при получении контроллером событий Basic Set от других устройств сети (например, выключатель ассоциирован с контроллером с каналом 1 при помощи MultiChannelAssociation)

var instanceId = 1;

var ctrlNodeId = zway.controller.data.nodeId.value; // Get controler Node Id
if (zway.devices[ctrlNodeId] && zway.devices[ctrlNodeId].instances[instanceId]) { // Check that instance object exists
	var basicCC = zway.devices[ctrlNodeId].instances[instanceId].Basic;
	if (basicCC) { // check that Basic exists
		basicCC.data.level.bind(function() { // bind to Basic level value
			system("echo " + this.value);
		});
	}
}

Автоматическое отключение диммера света через 10 секунд после включения (для реле используйте SwitchBinary вместо SwitchMultilevel). (Здесь предполагается, что устройство умеет отправлять отчёты и ассоциировано с контроллером)

var nodeId = 2;
var instanceId = 0;

var _tmr = null
zway.devices[nodeId].instances[instanceId].SwitchMultilevel.data.level.bind(function() {
	var node = args[0];
	var instance = args[1];
	if (_tmr) {
		clearTimeout(_tmr);
	}
	_tmr = null;
	if (this.value > 0) {
		_tmr = setTimeout(function () {
			zway.devices[nodeId].instances[instanceId].SwitchMultilevel.Set(0);
			_tmr = null;
		}, 10*1000);
	}
});

(тут стоит обратить внимание, что для запуска данного кода несколько раз нужно вводить массив _tmr для каждого устройства/канала)

Выполнение JavaScript кода

Куда вставлять этот JavaScript код? Есть несколько способов выполнить данный код:

  • Записать код в файл и положить его на SD-карту Raspberry Pi в папку /opt/z-way-server/[ваша папка]. Далее этот код можно исполнить из automation/main.js: executeFile(pathToJavaScriptFile).
  • Вы также можете прописать ваш собственный JavaScript файл в config.xml вместо родного файла RaZberry.
  • Отправить его по HTTP через Z-Way JSON API
Теперь опишем Z-Way HTTP/JSON API

Данный интерфейс взаимодействия с сервером Z-Way использует протокол HTTP и формат JSON для передачи данных. С его помощью не сложно создать собственный графический интерфейс для сервера автоматизации.

Команды кодируются прямо в URL запрос, а ответы приходят в привычном аяксерам JSON формате.

Запрос /ZWaveAPI/Data/<timestamp> предоставляет JSON структуру полного дерева обновлений с момента времени <timestamp>. Она имеет вид

{
	path1: object1,
	path2: object2,
	...
	updateTime: <timestamp>
}

Здесь pathN — это путь к изменившемуся элементу дерева, objectN — новое значение этого элемента. updateTime — время формирования обновлений (его нужно указать в следующем запросе, чтоб получить обновления)

Если запросить /ZWaveAPI/Data/0, то сервер вернёт полную структуру данных.

Запрос /ZWaveAPI/Run/<cmd> запускает JavaScript код zway.<cmd>. Этот запрос аналогичен более общему /JS/Run/ и создан для совместимости с API сервиса Z-Cloud.

Запрос /JS/Run/<cmd> выполняет <cmd> в v8 движке и возвращает в виде JSON результат выполнения этой команды.

Другие команды описаны в документации.

Несколько примеров:

  • http://localhost:8083/ZWaveAPI/Run/devices[2].SwitchMultilvel.data.level.value — возвращает последний полученный от устройства уровень яркости (стоит перед чтением послать SwitchMultilevel.Get())
  • http://localhost:8083/ZWaveAPI/Run/devices[3].instances[1].SensorMultilvel.data.level.value — возвращает последнее полученное от устройства значение датчика (стоит перед чтением послать SensorMultilevel.Get())
  • http://localhost:8083/ZWaveAPI/Run/devices[2].Basic.Set(0) — выключает устройство

Напомним, instances[0], commandClasses и value можно опустить.

! Не забывайте, что некоторые символы требуется явно кодировать перед отправкой (url encode): например, + { }.

Вместе с софтом поставляется базовый интерфейс для браузера. Он тоже использует Z-Way JSON API для взаимодействия с сервером. Его код удобно использовать для изучения работы API. Также может оказаться полезным расширение для jQuery для отслеживания обновлений дерева, полученных запросом /ZWaveAPI/Data/<timestamp>

Вот так выглядит взаимодействие разных слоёв системы (картинка из документации):

Через некоторое время планируется написание JS скриптов для автоматизации, предназначенных для простых пользователей (кривые заготовки уже лежат в папке automation). Их можно будет использовать совместно с собственными скриптами пользователя, т.е. собственные скрипты можно уже начинать писать и использовать в ваших проектах.

Где взять?

Raspberry Pi продаются на сайтах RS components или Farnell. Плата RaZberry и другое Z-Wave оборудование доступно в интернет-магазине Z-Wave.Me и других.

Пора автоматизировать!

Уверен, RaZberry станет удобным решением для построения бюджетных систем автоматизации на базе Z-Wave.