Python library and CLI to control Prana recuperators via BLE connection (https://prana.org.ua/)


Keywords
python, prana, recuperators, iot, automation, cli, prana-recuperators
License
GPL-3.0
Install
pip install prana-rc==0.4.4

Documentation

Prana device picture

Prana RC


Python library and CLI to manage Prana recuperators (https://prana.org.ua/) via BLE interface. It provides access to the device API and provides functionality similar to the functionality of the official mobile application with some limitations (see limitation section below).

ATTENTION: The manufacturer doesn't provide any technical documentation describing the protocol and also officially doesn't provide an ability to interact with the devices programmatically. This library is based on reverse engineering of the closed proprietary protocol. Use it at your own risk.

Features:

  • Read current prana state
  • Control everything which could be managed via an official app with a few exceptions (see limitations section)
  • Discover not connected devices around
  • Client-server architecture (allows distributed setup)
  • CLI interface available for quick tinkering

Installation & Usage

Server component

Prerequisites

For Linux hosts:

  • Make sure you have Bluez component installed. E.g. on debian based systems you can use sudo apt-get install bluez

Installation

The easiest way is to use pip. To install the recent version with HTTP API support run:

pip install prana-rc[server-tornado]

If you prefer dockerized setup you could run it like this:

docker run --volume /run/dbus/system_bus_socket:/run/dbus/system_bus_socket -p 8881:8881 --restart=unless-stopped corvis/prana-rc:latest

By default it will run http-server command, however you could run any cli commend by addign extra arguments to the end. For example to run discover you could use:

docker run --rm --volume /run/dbus/system_bus_socket:/run/dbus/system_bus_socket corvis/prana-rc:latest discover

NOTE: It is highly recommended to use the fixed version instead of latest to avoid unintended upgrades.

API

API is based on JSON RPC 2.0 standard. The server exposes one single endpoint for handling rpc requests, any other requests will be declined.

According to json rpc specification, the request must be a POST HTTP request with Content-Type set to application/json and the body containing json of the following structure:

{
  "jsonrpc": "2.0", 
  "method": "METHOD_NAME", 
  "id": 1,
  "params": [] 
}

Where params might be omitted if the method doesn't require any arguments. Params also could be an object if you prefer keyword arguments instead of positional args. id is a unique identifier of the request (relevant for async APIs), it should be generated by the client and it will be returned back with the response. It is important to include id and jsonrpc fields into the request otherwise.

The table below describes the exposed API methods:

Discover

Discovers available Prana devices nearby.

Important: It will discover only those devices which are not connected to any client (either mobile app, prana_rc or any other).

Method name: prana.discover

Params
Name Type Required Description
timeout int no Time in seconds to wait for the device's announcement.
Default: 4s.

Example

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "prana.discover",
  "params": {}
}

Shell command

curl \
  -X POST \
  -H "Accept: application/json" \
  http://YOUR_IP:8881/ \
  -d '{ "jsonrpc": "2.0", "id": 1, "method": "prana.discover" }'
Result
{
   "result":[
      {
         "address":"00:A0:50:99:52:D2",
         "bt_device_name":"PRNAQaqBEDROOM",
         "name":"BEDROOM",
         "rssi":-87
      }
   ],
   "id":1,
   "jsonrpc":"2.0"
}
Get State

Method name: prana.get_state

Params
Name Type Required Description
address string yes Mac address of the device to communicate with. Could be obtained from prana.discover query result
timeout int no Time in seconds to wait for successful command execution.
Default: 5s.
attempts int no Number of connection attempts to make before the failure
Default: 5.

Example

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "prana.get_state",
  "params": {"address": "XX:XX:XX:XX:XX:XX"}
}

Shell command

curl \
  -X POST \
  -H "Accept: application/json" \
  http://YOUR_IP:8881/ \
  -d '{ "jsonrpc": "2.0", "id": 1, "method": "prana.get_state", "params": {"address": "XX:XX:XX:XX:XX:XX"} }'
Result

Returns PranaState object. For baseline prana series (e.g.model 150, 200G) with no any sensors on board:

{
   "result":{
      "speed_locked":4,
      "speed_in":4,
      "speed_out":4,
      "night_mode":false,
      "auto_mode":false,
      "flows_locked":true,
      "is_on":false,
      "mini_heating_enabled":false,
      "winter_mode_enabled":false,
      "is_input_fan_on":false,
      "is_output_fan_on":false,
      "brightness": 6,
      "sensors": null,
      "timestamp":"2020-11-18T23:14:45.313515"
   },
   "id":1,
   "jsonrpc":"2.0"
}

For models with embedded sensors (e.g. Eco Life, Eco energy series):

{
   "result":{
      "speed_locked":4,
      "speed_in":4,
      "speed_out":4,
      "night_mode":false,
      "auto_mode":false,
      "flows_locked":true,
      "is_on":false,
      "mini_heating_enabled":false,
      "winter_mode_enabled":false,
      "is_input_fan_on":false,
      "is_output_fan_on":false,
      "sensors": {
        "temperature_in": 12.3,
        "temperature_out": 9.6,
        "humidity": 41,
        "pressure": 1010,
        "voc": 632.5,
        "co2": 651.1 
      },
      "timestamp":"2020-11-18T23:14:45.313515"
   },
   "id":1,
   "jsonrpc":"2.0"
}
Set State

Method name: prana.set_state

Params
Name Type Required Description
address string yes Mac address of the device to communicate with. Could be obtained from prana.discover query result
state object yes State to set for the device. The structure is listed below
timeout int no Time in seconds to wait for successful command execution.
Default: 4s.
attempts int no Number of connection attempts to make before the failure
Default: 5.
Set State Object Structure

The following properties could be set via Set State

Name Type Required Description
speed string no Either string representing integer (0-10) or one of "low", "l", "high", "h", "off", "stop", "2", "3", "4", "5", "6", "7", "8", "9"
mode string no One of normal, night, high. It doesn't seem to have any practical
winter_mode bool no Enable\disable winter mode (anti-frost)
heating bool no Enable\disable heating
brightness int no Set brightness. Should be an integer in a range of 1-6. See alsobrightness_pct
brightness_pct int no Set brightness in percent (0-100). Note: it's impossible to turn off screen so 0 will mean brightness level 1

Note: You should specify either brightness or brightness_pct but not both at the same time.

Example

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "prana.set_state",
  "params": {"address": "XX:XX:XX:XX:XX:XX", "state": {"speed": 1}}
}

Shell command

curl \
  -X POST \
  -H "Accept: application/json" \
  http://YOUR_IP:8881/ \
  -d '{ "jsonrpc": "2.0", "id": 1, "method": "prana.set_state", "params": {"address": "XX:XX:XX:XX:XX:XX", "state": {"speed": 1}} }'
Result

Returns the PranaState object. See Get State method for more details on the object structure.

{
   "result":{
        "speed_locked": 1,
        "speed_in": 1,
        "speed_out": 1,
        "brightness": 6,
        "night_mode": true,
        "auto_mode": false,
        "flows_locked": true,
        "is_on": true,
        "mini_heating_enabled": false,
        "winter_mode_enabled": false,
        "is_input_fan_on": true,
        "is_output_fan_on": true,
        "sensors": null,
        "timestamp": "2021-04-06T20:39:15.516925"
   },
   "id":1,
   "jsonrpc":"2.0"
}

Client

You could use any HTTP client to query the server. The underlying is JSON-RPCv2. The interface is defined here.

If you are looking for programmatic access from the python code you might consider the python API client. It has minimal dependencies and could be installed from the pip.

AIOHTTP Client

If your project is based on asyncio the recommended way is to use aiohttp based client:

​ pip install prana-rc.client[aiohttp] ​

Here is the basic usage example:

​python TBD ​

Architecture

The library could be used either directly in a python project or it could act as a server which maintains bluetooth connection with Prana device and exposes HTTP interface to the clients. See diagram below:

This approach is recommended as it brings some significant benefits over the embedding prana_rc into its own codebase:

  • Stability. Prana RC uses bluetooth library which relies on low-level OS APIs. Theoretically, it might be a stability risk for your application. In case you use the prana server in case of a crash, it will not affect your application. If you use the Dockerized version it is easy to configure automatic restart.
  • Coverage. Bluetooth works stable on a relatively small distance (5-10m with no obstacles) this means your server should be located close to the device. Often it is hard\impossible as you have a number of prana devices in different locations so the solution would be to set up a few intermediate nodes (e.g. some microcomputers like Raspberry Pi) with server components and connect them to the central control unit via API.
  • Easy integration. There is no lock on particular technology so the library could be used in conjunction with any tech stack.

Hardware

The device running this software must be equipped with a bluetooth 4.0+ module so it supports BLE. This module relies on Bleak library so there is a chance it might work on Win, Mac, and Linux. However, it was tested only on:

  • Linux (x64): Fedora, Debian
  • Raspbery PI: Raspbian
  • Mac: Catalina

Most likely it is compatible with Prana 150,200G, and Eco Energy series (with a limited feature set). But the confirmed list of models which were verified to work fine is below:

  • Prana 150
  • Prana 160 (italian market)

Please, create a ticket if you tested it with another device model so we could extend the list.

Limitations

  • Using device password is not supported
  • Reading information form built-in sensors (for Eco Energy series) is limited. At the moment VOC and CO2 sensors are not supported.
  • Changing brightness is not supported

Credits

  • Dmitry Berezovsky, author
  • Bleak, bluetooth client library
  • Contributors:

Disclaimer

This module is licensed under GPL v3. This means you are free to use in non-commercial projects.

The GPL license clearly explains that there is no warranty for this free software. Please see the included LICENSE file for details.