Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored the code and added more documentation for API / MQTT #11

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/pre-commit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: pre-commit

on:
pull_request:
push:
branches: [master]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
- uses: actions/cache@v1
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- uses: pre-commit/[email protected]
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

*.pyc

.idea/*
venv/*
24 changes: 24 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v2.1.0
hooks:
- id: pyupgrade
args: [--py37-plus]
- repo: https://github.com/psf/black
rev: 19.10b0
hooks:
- id: black
args:
- --safe
- --quiet
- repo: https://gitlab.com/pycqa/flake8
rev: 3.7.9
hooks:
- id: flake8
#additional_dependencies:
# - flake8-docstrings==1.5.0
# - pydocstyle==5.0.2
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.21
hooks:
- id: isort
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM python:3.9-alpine

ENV LOG_LEVEL "INFO"
ENV API_SSL_CERT ""
ENV API_SSL_KEY ""
ENV API_PORT 4693
ENV API_KEY ""
ENV PIMA_LOGIN ""
ENV PIMA_ZONES 32
ENV PIMA_SERIAL_PORT ""
ENV PIMA_HOST ""
ENV PIMA_PORT ""
ENV MQTT_HOST ""
ENV MQTT_PORT ""
ENV MQTT_CLIENT_ID "pima-server"
ENV MQTT_USER ""
ENV MQTT_PASSWORD ""
ENV MQTT_TOPIC "pima-server"
ENV API_MODE "Docker"

RUN apk update && \
apk upgrade && \
apk add --no-cache gcc libressl-dev musl-dev libffi-dev nano && \
pip install flask pyopenssl crcmod pyserial paho-mqtt

COPY . /app/

EXPOSE 4693

ENTRYPOINT ["python3", "/app/entrypoint.py"]
194 changes: 106 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PIMA Alarms
# PIMA2MQTT

This program implements an interface for negotiation with [PIMA Hunter Pro alarms](https://www.pima-alarms.com/our-products/hunter-pro-series/).
It was built based on PIMA™'s General Specification for Home Automation &
Expand All @@ -10,10 +10,10 @@ This program was built with no affiliation of PIMA Electronic Systems Ltd.
1. PIMA Hunter Pro alarm™, with 32, 96 or 144 zones.
1. PIMA Home Automation kit™ (`SA-232`, `LCL-11A` and Serial-to-USB cable), or `net4pro` ethernet connection.
Diagram by PIMA™ ©:
![Diagram by PIMA™ ©](home_automation_kit.png)
![Diagram by PIMA™ ©](docs/home_automation_kit.png)
- According to various users, the alarm can be alternatively connected using a `PL2303TA` USB-to-TTL cable, like [this one](https://www.aliexpress.com/item/32345829369.html).
- Yet another option is to connect directly to Raspberry pi, as specified here:
![Diagram by @maorcc](rpi_connection.png)
![Diagram by @maorcc](docs/rpi_connection.png)
1. Raspberry Pi or similar, connected to the alarm through the Home Automation kit.
- Tested on [Raspbian](https://www.raspberrypi.org/downloads/raspbian/). Other operating systems
may use different path structure for the serial ports.
Expand Down Expand Up @@ -42,91 +42,109 @@ This program was built with no affiliation of PIMA Electronic Systems Ltd.
- `ENTR`
- `END` to exit

## Setup
1. Create an SSL certificate, if you wish to access the server through HTTPS:
```bash
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365
```
1. Install additional Python libraries:
```bash
pip3 install crcmod paho-mqtt pyserial
```
1. Download [pima.py](pima.py) and [pima_server.py](pima_server.py), and put them in the same directory.
1. Set run permissions to [pima_server.py](pima_server.py):
```bash
chmod a+x pima_server.py
```
## Run for testing
1. Test out that you can run the server, e.g.:
```bash
./pima_server.py --ssl_cert cert.pem --ssl_key key.pem --port 7777 --key my_random_key --login 000000 --mqtt_host localhost
```
Parameters:
- `--ssl_cert` - Path to the SSL certificate file. If not set, will run a non-encrypted web server.
- `--ssl_key` - Path to the SSL private key file. If not set, will get the key from the certificate file.
- `--port` or `-p` - Port for the web server.
- `--key` or `-k` - An arbitrary string key to authenticate the server calls.
Consider generating a random key using `uuid -v4`.
- `--login` or `-l` - The technician login code to the alarm.
- `--zones` or `-z` - Number of zones supported by the alarm, one of 32, 96 or 144. Default is 32.
- `--serialport` - Serial port, e.g. `/dev/serial0`. Needed if connected directly through GPIO serial.
- `--pima_host` - Pima alarm hostname or IP address. Must be set if connected by ethernet.
- `--pima_port` - Pima alarm port. Must be set if connected by ethernet.
- `--mqtt_host` - The MQTT broker hostname or IP address. Must be set to enable MQTT.
- `--mqtt_port` - The MQTT broker port. Default is 1883.
- `--mqtt_client_id` - The MQTT client ID. If not set, a random client ID will be generated.
- `--mqtt_user` - <user:password> for the MQTT channel. If not set, no authentication is used.
- `--mqtt_topic` - The MQTT root topic. Default is "pima_alarm". The server will listen on topic
<{mqtt_topic}/command> and publish to <{mqtt_topic}/status>.
- `--log_level` - The minimal log level to send to syslog. Default is WARNING.
1. Access e.g. using curl:
```bash
curl -ik 'http://localhost:7777/pima?key=my_random_key&command=status'
curl -ik 'http://localhost:7777/pima?key=my_random_key&command=arm&mode=home1&partitions=1'
```
CGI Arguments:
- `key` - The key specified on the web server startup.
- `command` - Either `status` or `arm`.
When `arm` is specified:
- `mode` - Either `full_arm`, `home1`, `home2` or `disarm`.
- `partitions` Comma separated list of partitions. Default is `1`.
## Run as a service
1. Create a dedicated directory for the script files, and move the files to it.
Pass the ownership to root. e.g.:
```bash
sudo mkdir /usr/lib/pima
sudo mv pima_server.py pima.py key.pem cert.pem /usr/lib/pima
sudo chown root:root /usr/lib/pima/*
sudo pip3 install crcmod paho-mqtt pyserial
```
1. Create a service configuration file (as root), e.g. `/lib/systemd/system/pima.service`:
```INI
[Unit]
Description=PIMA alarm server
After=network.target

[Service]
ExecStart=/usr/bin/python3 -u pima_server.py --ssl_cert cert.pem --ssl_key key.pem --port 7777 --key my_random_key --login 000000 --mqtt_host localhost
WorkingDirectory=/usr/lib/pima
StandardOutput=inherit
StandardError=inherit
Restart=always

[Install]
WantedBy=multi-user.target
```
1. Link to it from `/etc/systemd/system/`:
```bash
sudo ln -s /lib/systemd/system/pima.service /etc/systemd/system/multi-user.target.wants/pima.service
```
1. Enable and start the new service:
```bash
sudo systemctl enable pima.service
sudo systemctl start pima.service
```
1. If you use [MQTT](http://en.wikipedia.org/wiki/Mqtt) for [HomeAssistant](https://www.home-assistant.io/) or
[openHAB](https://www.openhab.org/), the broker should now provide the updated status of the alarm, and accepts commands.
Basic yaml config files for HomeAssistant are available under the [hass](hass/) directory.
## How to run

- [Command Line](docs/command_line.md)
- [Docker](docs/docker.md)
- [Home Assistant](docs/homeassistant.md)

## MQTT

#### Publishes

##### Status: pima_alarm/status
```json
{
"partitions": {
"1": "home1"
},
"open zones": [],
"alarmed zone": [],
"bypassed zones": [],
"failed zones": [],
"failures": []
}
```

##### Last Will and Testament: pima_alarm/LWT
- online
- offline

#### Subscribes

##### pima_alarm/arm
Each of the following commands can receive which partitions to execute the command,
By default `partitions: [ "1" ]`


Arm
```json
{
"mode": "full_arm"
}
```

Arm Home1
```json
{
"mode": "home1"
}
```

Arm Home2
```json
{
"mode": "home2"
}
```

Disarm
```json
{
"mode": "disarm"
}
```

## Web Server endpoints

Each request to the server must include in the URL (query string) the api_key, e.g. ```/pima/status?api_key=test```

#### GET /pima/status
Returns status

##### Http Codes

Code | Reason | Description
--- | --- | --- |
200 | OK | Set arm state changed successfully
401 | Unauthorized request | api_key doesn't match to the defined key


#### POST /pima/arm
##### Body
Name | Type | Required | Default | Description
--- | --- | --- | --- | --- |
mode | str | True | None | Which command to run, supported options - arm, disarm, home1, home2
partitions | array of int | False | `[ "1" ]` | Which partition to use, accept array of int

Returns status

##### Http Codes

Code | Reason | Description
--- | --- | --- |
200 | OK | Set arm state changed successfully
400 | Invalid request data | Empty payload sent within the request
401 | Unauthorized request | api_key doesn't match to the defined key
403 | More details below | The request contained valid data and was understood by the server, but the server is refusing action, more details available in the error message and logs
501 | Invalid arm mode, must be one of `full_arm`, `home1`, `home2`, `disarm` | received mode is not supported

##### Status Code 403 available errors
Message | Description
| --- | --- |
No Server | Server is unreachable, check if serial port / IP:Port are correct
Invalid arm mode [mode] | provided mode is not supported

## Next steps
1. [Groovy](http://groovy-lang.org/) [Device Type Handlers](https://docs.smartthings.com/en/latest/device-type-developers-guide/) for [SmartThings](https://www.smartthings.com/) integration.
1. Support further functionality, e.g. change user codes.
Loading