BobaosKit - accessories¶
About¶
Inspired by Apple HomeKit I was searching for android solution but couldn’t find appropriate. To be able to work in LAN without cloud I started own project.
So, bobaoskit is a simple implementation of accessory system without any complication. So, there is no such things like pairing, authorization, crypto, binary protocol implemented. It works with simple Websocket protocol to implement network communications. Only five websocket requests are implemented and four broadcasted events.
Accessories only have id
, name
, type
, control
, status
fields.
Following example shows how switch accessory can be implemented. It has only state
both control and status field that reflects switch relay state.
const Bobaos = require("bobaos.sub");
const BobaosKit = require("bobaoskit.accessory");
// init bobaos and bobaoskit sdk
const bobaos = Bobaos();
const sdk = BobaosKit.Sdk();
const switchDatapointControl = 3;
const switchDatapointStatus = 7;
const switch1 = BobaosKit.Accessory({
id: "switch1",
name: "Simple Switch",
type: "switch",
control: ["state"],
status: ["state"],
sdk: sdk
});
switch1.on("ready", async _ => {
let value = (await bobaos.getValue(switchDatapointStatus)).value;
return switch1.updateStatusValue({field: "state", value: value});
});
const processOneControlAccValue = payload => {
let {field, value} = payload;
// send datapoint value to KNX bus
return bobaos.setValue({id: switchDatapointControl, value: value});
};
// on incoming event from bobaoskit.worker
// e.g. control request sent from mobile app
switch1.on("control accessory value", async (payload, cb) => {
if (Array.isArray(payload)) {
await Promise.all(payload.map(processOneControlAccValue));
return;
}
await processOneControlAccValue(payload);
});
const processOneDatapointValue = payload => {
let {id, value} = payload;
if (id === switchDatapointStatus) {
return switch1.updateStatusValue({field: "state", value: value});
}
};
// register listener for incoming value from KNX bus
// after switch command state of relay was changed
bobaos.on("datapoint value", payload => {
if (Array.isArray(payload)) {
return payload.forEach(processOneDatapointValue);
}
processOneDatapointValue(payload);
});
// on exit, unregister accessory
const inTheEnd = async _ => {
console.log("removing accessory");
await switch1.unregisterAccessory();
process.exit();
};
process.on('SIGINT', inTheEnd);
process.on('SIGTERM', inTheEnd);
How it works¶
Accessory backend service¶
First of all, there should be running instance of bobaoskit.worker
on pc.
bobaoskit.worker
script uses redis
and bee-queue
job manager to process incoming requests.
There is possible requests as clear/add/remove accessory, get accessory info, get/update status value, control accessory value. Events are broadcasted over Redis Pub/Sub channel defined in config.json
.
bobaoskit.worker
listens port defined in config.json
for WebSocket connections. Websocket API exposes request methods like get accessory info, get status/control accessory value
bobaoskit.worker
uses dnssd
module to advertise itself in local network. Advertised service name is defined in config.json
.
Accessory SDK¶
bobaoskit.accessory
nodejs module is used to register and manage accessories. So, use it to create accessory instance, receive control commands, control your devices and update accessory state.
Take a look at following code
const BobaosKit = require("bobaoskit.accessory");
// params:
// redis: "redis_url"/client object
// job_channel: "bobakit_job"
// broadcast_channel: "bobaoskit_bcast"
// by default redis is undefined
// channels are from comments above,
// same as default in config.json
const sdk = BobaosKit.Sdk();
// sdk param is not required but
// if you want to add multiple accessories
// it is better to create one sdk instance
// and use it for all accessories
const accessory = BobaosKit.Accessory({
id: "accessory",
name: "Simple Switch",
type: "switch",
control: ["state"],
status: ["state"],
sdk: sdk
});
Now, sdk has following methods that is used by accessory instance:
- ping()
- getGeneralInfo()
- clearAccessories()
- addAccessory(payload)
- removeAccessory(payload)
- getAccessoryInfo(payload)
- getStatusValue(payload)
- updateStatusValue(payload)
- controlAccessoryValue(payload)
Also, sdk emits broadcasted event
with (method, payload)
as a params.
BobaosKit.Accessory(..)
creates object that represents accessory. It uses sdk
to communicate via bee-queue
with bobaoskit worker and creates own bee-queue
instance to accept incoming requests. Exposed methods:
- getAccessoryInfo()
- getStatusValue(payload)
- updateStatusValue(payload)
- unregisterAccessory()
Accessory instance emits event control accessory value
event with (method, payload)
params when request to control accessory value is received. So, listen for this event and process it.
Client¶
Client application makes a dnssd discovery and after resolving host creates WebSocket connection to given port. Then get accessory info
with null
payload is sent and response contains list of all accessories.
To control accessory field/fields client sends control accessory value
request with payload {id: accId, control: {field: field1, value: value}/[{field: .. value: ..}]}/[{id: ...}, ...]
.
Installation¶
Currently, no npm package is published.
So, clone git repositories:
git clone https://github.com/bobaoskit/bobaoskit.worker
git clone https://github.com/bobaoskit/bobaoskit.accessory
Install dependencies for worker and run
cd bobaoskit.worker
npm install
./bin/bobaos-kit.js
Install dependencies for bobaoskit.accessory
cd bobaoskit.accessory
npm install
Now, take a look at bobaoskit.accessory/examples/
folder.
You may be able to run radio player accessory scripted in mpvsw.js
. Make sure you have mpv
and latest version of youtube-dl
installed.
Install dependencies for mpv client
npm install mympvspawn
npm install mympvclient
Now, start it with node
node ./examples/mpvsw.js
Mobile application¶
For mobile application flutter is used. It uses mdns plugin for dns service discovery, currently this plugin is not published, so there is git dependency in pubspec.yaml
.
Install flutter(I use v1.0.0), dart sdk(2.1.0), all sdk(Android/XCode) to compile app.
Clone repository
git clone https://github.com/shabunin/bobaflu
Install packages
flutter packages get
To run, debug and build use your favourite editor/ide, I prefer IntelliJ IDEA.
I run it only on Android device, so, it will be great if somebody takes responsibility to compile it for iOS.
Note: Android emulator won’t discover any service due to isolated emulator network. Still, you are able to make direct connection.