Paraluna is a set of building blocks to implement home automation using FRP with typescript (and rxjs).
Since every input to your home-automation is typically an event, incoming events can be transformed and written back to devices (e.g. a switch "on" event needs to be written to a "light").
Given that everything is an event you can apply Event Sourcing to build up arbitrary complex "application" state with ease.
On top of that, the model to consume events and return "commands" (to be written to mqtt etc.) makes it possible to model home-automation as pure functions and thus even enable unit testing.
The idea to communicate with the outside is to think of "sources" as side-effects for input (of events) and "sinks" as side-effects for output (of "commands"). So sources are streams of events or rather values, like triggers or sensors which provide read-only values. Sinks on the other side allow writing values, e.g. a bulb that receives a brightness value.
This basic idea of "sinks" and "sources" is inspired by the cycle.js framework.
Building upon rxjs your automation "connects" to an Observable of events and returns observables of "commands" ({state: "ON"}
via mqtt topic livingroom/light
).
Given RXJS Operators it's easy to build complex automations (think of the typical reactive/rxjs examples how to implement autocomplete with debounce etc.).
- when modeling your home automation tasks as reactive streams, sinks & sources and event sourced fits your way of thinking
- when programming-capabilities of other platforms are not sufficient (e.g. home-assistant)
- when writing typescript/code is more convenient than writing yaml, using Node-RED
- you want to unit-test your automations
- you like rxjs and typescript
Paraluna works great with mqtt (e.g. zigbee2mqtt), but also supports home-assistant. Supporting other "systems" is just a matter of defining appropriate drivers (sources and sinks).
Typically, devices can be controlled directly via mqtt/zigbee2mqtt, but to reuse automation/scripts/scenes it makes sense to use home-assistant as proxy.
Also, home-assistant offers additional "sensors" (or entities to be precise in hass-speak) via it's integrations that are not available via mqtt, e.g. Weather Information, Device Information (from mobile phones), sunset etc.
- uses mqtt (and mqtt-async) directly.
-
advanced.last_seen
ISO_8601 not required directly, but used in type definitions -
device_options.retain
true simplifies usage, especially startup
-
simulated_brightness
for Ikea E1743 (TRADFRI ON/OFF switch)
- base topic "zigbee2mqtt" (
mqtt.base_topic
) - json message format (
exerimental.output
"json" or "attribute_and_json")
- uses hasso to access home-assistant
Run examples either via nodemon (during development to watch for changes) or via ts-node directly.
The examples expect the following environment variables to be set.
HASS_TOKEN
HASS_URL
MQTT_HOST
MQTT_USER
MQTT_PASSWORD
With direnv the proper values may be set into a local .envrc
file:
# copy the example file
$ cp .envrc.local{.sample,}
# set custom values
$ $EDITOR .envrc.local
# if not yet done, allow the .envrc file
$ direnv allow
$ cd examples
$ ts-node index.ts
To make nodemon/ts-node work with typescript project references and also watching changes in src, calling nodemon is a bit counter-intuitive. It needs to be called in project-root, but the examples
path needs to be omitted.
$ nodemon index.ts