Skip to content

Commit 8c80b2f

Browse files
committed
update README to reflect changes
1 parent cb62bf9 commit 8c80b2f

File tree

2 files changed

+156
-71
lines changed

2 files changed

+156
-71
lines changed

README.md

Lines changed: 30 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,89 +6,48 @@
66

77
Enapter software development kit for Python.
88

9+
## Features
10+
11+
1. [Standalone
12+
Devices](https://v3.developers.enapter.com/docs/standalone/introduction)
13+
framework.
14+
2. [MQTT
15+
API](https://v3.developers.enapter.com/reference/device_integration/mqtt_api/)
16+
client.
17+
3. [HTTP API](https://v3.developers.enapter.com/reference/http/intro) client.
18+
919
## Installation
1020

11-
This project uses [semantic versioning](https://semver.org/).
21+
> [!IMPORTANT]
22+
> Make sure you are using Python 3.11+.
1223
13-
The API is still under development and may change at any time. It is
14-
recommended to pin the version during installation.
24+
> [!WARNING]
25+
> The API is still under development and may change at any time. It is
26+
> recommended to pin the version during installation.
1527
16-
Latest from PyPI:
28+
From PyPI:
1729

1830
```bash
19-
pip install enapter==0.11.3
31+
pip install enapter==0.12.0
2032
```
2133

2234
## Usage
2335

24-
Checkout [examples](examples).
25-
26-
## Implementing your own VUCM
27-
28-
### Device Telemetry and Properties
29-
30-
Every method of `enapter.vucm.Device` subclass decorated with
31-
`enapter.vucm.device_task` decorator is considered a _device task_. When such a
32-
device is started, all of its tasks are started as well. Device tasks are
33-
started in random order and are being executed concurrently in the background.
34-
If a device task returns or raises an exception, device routine is terminated.
35-
A typical use of the task is to run a periodic job to send device telemetry and
36-
properties.
37-
38-
In order to send telemetry and properties define two corresponding device
39-
tasks. It is advised (but is not obligatory) to send telemetry every **1
40-
second** and to send properties every **10 seconds**.
41-
42-
Examples:
43-
44-
- [wttr-in](examples/vucm/wttr-in)
45-
46-
### Device Command Handlers
47-
48-
Every method of `enapter.vucm.Device` subclass decorated with
49-
`enapter.vucm.device_command` is considered a _device command handler_. Device
50-
command handlers receive the same arguments as described in device Blueprint
51-
manifest and can optionally return a payload as `enapter.types.JSON`.
52-
53-
In order to handle device commands define corresponding device command
54-
handlers.
55-
56-
Examples:
36+
Check out examples:
5737

58-
- [zhimi-fan-za5](examples/vucm/zhimi-fan-za5)
38+
- [Standalone Devices](examples/standalone)
39+
- [MQTT API](examples/mqtt)
40+
- [HTTP API](examples/http)
5941

60-
### Device Alerts
42+
They provide a good overview of available features and should give you enough
43+
power to get started.
6144

62-
Device alerts are stored in `self.alerts`. It is a usual Python `set`, so you
63-
can add an alert using `alerts.add`, remove an alert `alerts.remove` and clear
64-
alerts using `alerts.clear`.
45+
> [!TIP]
46+
> Don't hesitate to peek into the source code - it is supposed to be easy to
47+
> follow.
6548
66-
Alerts are sent only as part of telemetry, so in order to report device alert,
67-
use `send_telemetry` with any payload.
49+
## Help
6850

69-
## Running your own VUCM via Docker
70-
71-
A simple Dockerfile can be:
72-
73-
```
74-
FROM python:3.10-alpine3.16
75-
76-
WORKDIR /app
77-
78-
RUN python -m venv .venv
79-
COPY requirements.txt requirements.txt
80-
RUN .venv/bin/pip install -r requirements.txt
81-
82-
COPY script.py script.py
83-
84-
CMD [".venv/bin/python", "script.py"]
85-
```
86-
87-
:information_source: If you are using [Enapter
88-
Gateway](https://handbook.enapter.com/software/gateway_software/) and running
89-
Linux, you should connect your containers to `host` network
90-
:information_source::
91-
92-
```bash
93-
docker run --network host ...
94-
```
51+
If you feel lost or confused, reach us in
52+
[Discord](https://discord.com/invite/TCaEZs3qpe) or just [file a
53+
bug](https://github.com/Enapter/python-sdk/issues/new). We'd be glad to help.

examples/standalone/README.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Standalone
2+
3+
## Basic Implementation
4+
5+
The most straightforward way to implement your own Standalone Device is this:
6+
7+
1. Subclass `enapter.standalone.Device`.
8+
2. Override `async def run(self) -> None` method to send telemetry and properties.
9+
3. Pass an instance of your device to `enapter.standalone.run`.
10+
11+
Here's a basic example:
12+
13+
```python
14+
# my_device.py
15+
16+
import asyncio
17+
import enapter
18+
19+
async def main():
20+
await enapter.standalone.run(MyDevice())
21+
22+
class MyDevice(enapter.standalone.Device):
23+
async def run(self):
24+
while True:
25+
await self.send_telemetry({})
26+
await self.send_properties({"model": "0.0.1"})
27+
await asyncio.sleep(10)
28+
29+
if __name__ == "__main__":
30+
asyncio.run(main())
31+
```
32+
33+
## Handling Commands
34+
35+
`enapter.standalone.Device` dispatches incoming command execution requests to
36+
corresponding methods on your subclass.
37+
38+
If this command is defined in the manifest:
39+
40+
```yaml
41+
configure_connection:
42+
display_name: Configure Connection
43+
group: connection
44+
arguments:
45+
ip_address:
46+
display_name: IP Address
47+
type: string
48+
required: true
49+
token:
50+
display_name: Bearer Authentication Token
51+
type: string
52+
required: false
53+
```
54+
55+
This is the signature you would create in your device class to implement a
56+
command handler:
57+
58+
```python
59+
async def cmd_configure_connection(ip_address: str, token: str | None = None): ...
60+
```
61+
62+
By default `cmd_` prefix is used to search the command handler.
63+
64+
## Synchronous Code
65+
66+
Blocking (CPU-bound) code should not be called directly. For example, if a
67+
function performs a CPU-intensive calculation for 1 second, all concurrent
68+
`asyncio` Tasks and IO operations would be delayed by 1 second.
69+
70+
Instead, use `asyncio.to_thread`:
71+
72+
```python
73+
await asyncio.to_thread(blocking_call())
74+
```
75+
76+
## Communication Config
77+
78+
> [!NOTE]
79+
> The following instruction works only for v3 sites. If you have a v1 site,
80+
> follow [this
81+
> tutorial](https://developers.enapter.com/docs/tutorial/software-ucms/standalone)
82+
> to generate your communication config.
83+
84+
Generate a communication config using [Enapter
85+
CLI](https://github.com/Enapter/enapter-cli):
86+
87+
```bash
88+
enapter3 device communication-config generate --device-id "$YOUR_DEVICE_ID" --protocol MQTTS | jq .config | base64 --wrap=0
89+
```
90+
91+
</details>
92+
93+
## Running
94+
95+
Now you can use the communication config to run your device:
96+
97+
```bash
98+
export ENAPTER_STANDALONE_COMMUNICATION_CONFIG="$YOUR_COMMUNICATION_CONFIG"
99+
python my_device.py
100+
```
101+
102+
## Running In Docker
103+
104+
Here's an example of a simple `Dockerfile`:
105+
106+
```Dockerfile
107+
FROM python:3.13-alpine
108+
109+
WORKDIR /app
110+
111+
RUN python -m venv .venv
112+
COPY requirements.txt requirements.txt
113+
RUN .venv/bin/pip install -r requirements.txt
114+
115+
COPY script.py script.py
116+
117+
CMD [".venv/bin/python", "script.py"]
118+
```
119+
120+
> [!WARNING]
121+
> If you are using Enapter Gateway and running Linux, you should connect your
122+
> containers to the `host` network to make mDNS resolution work:
123+
>
124+
> ```bash
125+
> docker run --network host ...
126+
> ```

0 commit comments

Comments
 (0)