浏览代码

Add systemd service, sdnotify and local config

Thomas Dietrich 8 年之前
父节点
当前提交
0914cf8b44
共有 5 个文件被更改,包括 167 次插入17 次删除
  1. 109 0
      .gitignore
  2. 25 14
      README.md
  3. 16 3
      mqtt-flora.py
  4. 1 0
      requirements.txt
  5. 16 0
      template.service

+ 109 - 0
.gitignore

@@ -0,0 +1,109 @@
+# Project specific
+
+config.local.ini
+
+
+# Created by https://www.gitignore.io/api/python
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# dotenv
+.env
+
+# virtualenv
+.venv
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# End of https://www.gitignore.io/api/python
+

+ 25 - 14
README.md

@@ -16,8 +16,10 @@ The program can be executed for a single run or in **daemon mode** to run contin
 * JSON encoded
 * JSON encoded
 * MQTT authentication support
 * MQTT authentication support
 * Daemon mode (default)
 * Daemon mode (default)
+* Systemd service file included, sd\_notify messages generated
 * MQTT-less mode, printing data directly to stdout/file
 * MQTT-less mode, printing data directly to stdout/file
 * Reliable and inituitive
 * Reliable and inituitive
+* Tested on Raspberry Pi
 
 
 ![Promotional image](https://xiaomi-mi.com/uploads/ck/xiaomi-flower-monitor-001.jpg)
 ![Promotional image](https://xiaomi-mi.com/uploads/ck/xiaomi-flower-monitor-001.jpg)
 
 
@@ -35,25 +37,27 @@ The Mi Flora sensor offers the following plant and soil readings:
 
 
 ### Installation
 ### Installation
 
 
-Shown for a modern Debian system:
+On a modern Linux system just a few steps are needed.
+The following example shows the installation under Debian/Raspbian:
 
 
 ```shell
 ```shell
+sudo apt install git python3 python3-pip bluetooth
+
 git clone https://github.com/ThomDietrich/miflora-mqtt-daemon.git /opt/miflora-mqtt-daemon
 git clone https://github.com/ThomDietrich/miflora-mqtt-daemon.git /opt/miflora-mqtt-daemon
 cd /opt/miflora-mqtt-daemon
 cd /opt/miflora-mqtt-daemon
 
 
-apt install python3 python3-pip bluetooth libbluetooth-dev libboost-python-dev libglib2.0-dev
-pip3 install -r requirements.txt
+sudo pip3 install -r requirements.txt
 ```
 ```
 
 
 ### Configuration
 ### Configuration
 
 
-To match personal needs all operation details can be configured using the file [`config.ini`](config.ini).
+To match personal needs, all operation details can be configured using the file [`config.ini`](config.ini).
 
 
 You need to add at least one sensor to the configuration.
 You need to add at least one sensor to the configuration.
 Scan for available Miflora sensors in your proximity with the command:
 Scan for available Miflora sensors in your proximity with the command:
 
 
 ```shell
 ```shell
-hcitool lescan
+sudo hcitool lescan
 ```
 ```
 
 
 ### Execution
 ### Execution
@@ -71,26 +75,33 @@ The extensive output can be reduced to error messages:
 python3 mqtt-flora.py > /dev/null
 python3 mqtt-flora.py > /dev/null
 ```
 ```
 
 
-You probably want to execute the program **continuously in the background**.
-This can either be done by using the internal daemon or cron.
+#### Continous Daemon/Service
+
+You most probably want to execute the program **continuously in the background**.
+This can be done either by using the internal daemon or cron.
 
 
-**Attention:** Daemon mode can be enabled (default) and disabled in the config file.
+**Attention:** Daemon mode must be enabled in the configuration file (default).
 
 
-1. Send the program into the background with some simple command line foo:
+1. Systemd service - on systemd powered systems the recommended option
    
    
    ```shell
    ```shell
-   python3 /path/to/mqtt-flora.py &
+   sudo ln -s /opt/miflora-mqtt-daemon/template.service /etc/systemd/system/miflora.service
+
+   sudo systemctl daemon-reload
+
+   sudo systemctl start miflora.service
+   sudo systemctl status miflora.service
+
+   sudo systemctl enable miflora.service
    ```
    ```
-   
-   *Hint:* Bring back to foreground with `fg`.
 
 
-2. Screen Shell - Run the program inside a [screen shell](https://www.howtoforge.com/linux_screen):
+1. Screen Shell - Run the program inside a [screen shell](https://www.howtoforge.com/linux_screen):
    
    
    ```shell
    ```shell
    screen -S mqtt-flora -d -m python3 /path/to/mqtt-flora.py
    screen -S mqtt-flora -d -m python3 /path/to/mqtt-flora.py
    ```
    ```
 
 
-3. Cron job - Add a new con job, e.g., `/etc/cron.d/miflora`, execute every 5 minutes
+1. Cron job - Add a new con job, e.g., `/etc/cron.d/miflora`, execute every 5 minutes
    
    
    ```shell
    ```shell
    */5 * * * * root python3 /path/to/mqtt-flora.py > /dev/null
    */5 * * * * root python3 /path/to/mqtt-flora.py > /dev/null

+ 16 - 3
mqtt-flora.py

@@ -7,6 +7,7 @@ from time import sleep, localtime, strftime
 from configparser import ConfigParser
 from configparser import ConfigParser
 from miflora.miflora_poller import MiFloraPoller, MI_BATTERY, MI_CONDUCTIVITY, MI_LIGHT, MI_MOISTURE, MI_TEMPERATURE
 from miflora.miflora_poller import MiFloraPoller, MI_BATTERY, MI_CONDUCTIVITY, MI_LIGHT, MI_MOISTURE, MI_TEMPERATURE
 import paho.mqtt.client as mqtt
 import paho.mqtt.client as mqtt
+import sdnotify
 
 
 parameters = [MI_BATTERY, MI_CONDUCTIVITY, MI_LIGHT, MI_MOISTURE, MI_TEMPERATURE]
 parameters = [MI_BATTERY, MI_CONDUCTIVITY, MI_LIGHT, MI_MOISTURE, MI_TEMPERATURE]
 
 
@@ -15,10 +16,14 @@ print('Xiaomi Mi Flora Plant Sensor MQTT Client/Daemon')
 print('Source: https://github.com/ThomDietrich/miflora-mqtt-daemon')
 print('Source: https://github.com/ThomDietrich/miflora-mqtt-daemon')
 print()
 print()
 
 
-# Eclipse Paho callbacks http://www.eclipse.org/paho/clients/python/docs/#callbacks
+# Systemd Service Notifications - https://github.com/bb4242/sdnotify
+sd_notifier = sdnotify.SystemdNotifier()
+
+# Eclipse Paho callbacks - http://www.eclipse.org/paho/clients/python/docs/#callbacks
 def on_connect(client, userdata, flags, rc):
 def on_connect(client, userdata, flags, rc):
     if rc == 0:
     if rc == 0:
         print('Connected.\n')
         print('Connected.\n')
+        sd_notifier.notify('STATUS=MQTT connection established')
     else:
     else:
         print('Connection error with result code {} - {}'.format(str(rc), mqtt.connack_string(rc)), file=sys.stderr)
         print('Connection error with result code {} - {}'.format(str(rc), mqtt.connack_string(rc)), file=sys.stderr)
         #kill main thread
         #kill main thread
@@ -30,7 +35,7 @@ def on_publish(client, userdata, mid):
 # Load configuration file
 # Load configuration file
 config = ConfigParser(delimiters=('=', ))
 config = ConfigParser(delimiters=('=', ))
 config.optionxform = str
 config.optionxform = str
-config.read(os.path.join(sys.path[0], 'config.ini'))
+config.read([os.path.join(sys.path[0], 'config.ini'), os.path.join(sys.path[0], 'config.local.ini')])
 
 
 reporting_mode = config['General'].get('reporting_method', 'mqtt-json')
 reporting_mode = config['General'].get('reporting_method', 'mqtt-json')
 daemon_enabled = config['Daemon'].getboolean('enabled', True)
 daemon_enabled = config['Daemon'].getboolean('enabled', True)
@@ -47,6 +52,7 @@ if not config['Sensors']:
     print('Error. Please add at least one sensor to the configuration file "config.ini".', file=sys.stderr)
     print('Error. Please add at least one sensor to the configuration file "config.ini".', file=sys.stderr)
     print('Scan for available Miflora sensors with "sudo hcitool lescan".', file=sys.stderr)
     print('Scan for available Miflora sensors with "sudo hcitool lescan".', file=sys.stderr)
     sys.exit(1)
     sys.exit(1)
+sd_notifier.notify('STATUS=Configuration accepted')
 
 
 # MQTT connection
 # MQTT connection
 if reporting_mode == 'mqtt-json':
 if reporting_mode == 'mqtt-json':
@@ -67,6 +73,8 @@ if reporting_mode == 'mqtt-json':
         mqtt_client.loop_start()
         mqtt_client.loop_start()
         sleep(1) # some slack to establish the connection
         sleep(1) # some slack to establish the connection
 
 
+sd_notifier.notify('READY=1')
+
 # Initialize Mi Flora sensors
 # Initialize Mi Flora sensors
 flores = dict()
 flores = dict()
 for [name, mac] in config['Sensors'].items():
 for [name, mac] in config['Sensors'].items():
@@ -80,6 +88,8 @@ for [name, mac] in config['Sensors'].items():
     print()
     print()
     flores[name] = flora_poller
     flores[name] = flora_poller
 
 
+sd_notifier.notify('STATUS=Initialization complete, starting MQTT publish loop')
+
 # Sensor data retrieval and publication
 # Sensor data retrieval and publication
 while True:
 while True:
     for [flora_name, flora_poller] in flores.items():
     for [flora_name, flora_poller] in flores.items():
@@ -101,11 +111,14 @@ while True:
         else:
         else:
             raise NameError('Unexpected reporting_mode.')
             raise NameError('Unexpected reporting_mode.')
 
 
+    sd_notifier.notify('STATUS={} - Status messages published'.format(strftime('%Y-%m-%d %H:%M:%S', localtime())))
+
     if daemon_enabled:
     if daemon_enabled:
         print('Sleeping ({} seconds) ...'.format(sleep_period))
         print('Sleeping ({} seconds) ...'.format(sleep_period))
         sleep(sleep_period)
         sleep(sleep_period)
         print()
         print()
     else:
     else:
-        mqtt_client.disconnect() if reporting_mode == 'mqtt-json'
+        if reporting_mode == 'mqtt-json':
+            mqtt_client.disconnect()
         break
         break
 
 

+ 1 - 0
requirements.txt

@@ -1,3 +1,4 @@
 miflora==0.1.15
 miflora==0.1.15
 paho-mqtt==1.2
 paho-mqtt==1.2
 wheel==0.24.0
 wheel==0.24.0
+sdnotify==0.3.1

+ 16 - 0
template.service

@@ -0,0 +1,16 @@
+[Unit]
+Description=Xiaomi Mi Flora Plant Sensor MQTT Client/Daemon
+Documentation=https://github.com/ThomDietrich/miflora-mqtt-daemon
+After=syslog.target mosquitto.service bluetooth.service
+
+[Service]
+Type=notify
+User=daemon
+Group=daemon
+WorkingDirectory=/opt/miflora-mqtt-daemon/
+ExecStart=/opt/miflora-mqtt-daemon/mqtt-flora.py
+StandardOutput=null
+Environment=PYTHONUNBUFFERED=true
+
+[Install]
+WantedBy=multi-user.target