gluap / pyess

Python library for communication with LG ESS power converters
MIT License
31 stars 10 forks source link

Run pyess as daemon/service under Linux, comments #3

Closed fu-zhou closed 4 years ago

fu-zhou commented 4 years ago

I got my system up and running now, essmqtt is awesome, thanks for all the effort put in so far! I have a couple of requests/ comments meanwhile:

● essmqtt.service - ESS MQTT Communication
   Loaded: loaded (/etc/systemd/system/essmqtt.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Sat 2020-05-02 09:24:18 CEST; 3s ago
  Process: 20364 ExecStart=/home/user/essmqtt.sh (code=exited, status=1/FAILURE)
 Main PID: 20364 (code=exited, status=1/FAILURE)

May 02 09:24:18 iobroker-vm systemd[1]: Started ESS MQTT Communication.
May 02 09:24:18 iobroker-vm essmqtt.sh[20364]: Traceback (most recent call last):
May 02 09:24:18 iobroker-vm essmqtt.sh[20364]:   File "/home/user/.local/bin/essmqtt", line 5, in <module>
May 02 09:24:18 iobroker-vm essmqtt.sh[20364]:     from pyess.essmqtt import main
May 02 09:24:18 iobroker-vm essmqtt.sh[20364]: ModuleNotFoundError: No module named 'pyess'
May 02 09:24:18 iobroker-vm systemd[1]: essmqtt.service: Main process exited, code=exited, status=1/FAILURE
May 02 09:24:18 iobroker-vm systemd[1]: essmqtt.service: Failed with result 'exit-code'.

Thanks again for all the effort put in here!

gluap commented 4 years ago

Hi, 1). It seems that you have the script installed in your user home folder. Likely your environment variables differ between what is seen by a script when run from terminal and when run as a service. For this reason the system python does not realize that there are additional modules installed in your user home. To run it as a service I think the clean method would be doing the following: set up a python virtual environment for your systemd service. That way you ensure that running the script manually yields the same results as when run from systemd. Basically the workflow for preparing that venv (only to be done once) would be along the following lines:

# the following creates a sandboxed, local python installation in /opt/essmqtt
python3.8 -m venv /opt/essmqtt
# the following installs pyess in the sandbox
/opt/essmqtt/bin/pip install pyess

from then on you should be able to call /opt/essmqtt/bin/esmqtt from both your terminal and a systemd service. Read up on virtualenvs or venvs if you want to know more about this approach, they can be very powerful.

2.) The request here is that essmqtt somehow registers as an mqtt client -- could you point me towards specification on how this is usually done?

3.) The typos are actually what is sent by the LG box -- I agree they are typos but I wouldn't want to rename these to fix a typo made by LG.

4.) I don't know exactly what the status means, I assume that they somehow relate to the direction of the power flows. You may get clues by running the app in parallel, especially when the direction of power flows changes (for instance monitor it while switching on a load that changes your power balance such that you go from feeding the grid to not feeding it any more, or compare it in the evening before and after the last watts have stopped coming in during sunset).

fu-zhou commented 4 years ago

Thanks for the quick reply. I will try 1) as described by you. On 2) I have no clue about the protocol's details. What I see in iobroker's MQTT server adapter is: "List of connected clients: mosqsub|89309-iobroker-" when I run mosquitto_sub in parallel to essmqtt. If you give me a starting point, I'm more than happy to figure the details out, despite my limitiations when it comes to reverse engineering. 3) I fully agree. 4) Okay, I'll try to figure that out and keep you posted...

fu-zhou commented 4 years ago

In Ubuntu 18.04 Server I got essmqtt to run as service like:

# install venv:
sudo apt install python3.8-venv
# create virtual environment:
python3 -m venv essmqtt
# activate:
source essmqtt/bin/activate
# install pyess:
pip install pyess
# create service:
sudo nano /etc/systemd/system/essmqtt.service
# Content of service:
[Unit]
Description=ESS MQTT Communication

[Service]
ExecStart=/home/user/essmqtt/bin/essmqtt --mqtt_server=<MQTT SERVER IP> --ess_password <PW> --interval_seconds <POLL INTERVALL>

[Install]
WantedBy=default.target

Problem: the service does not run after startup/ reboot, it needs to be started with: systemctl start essmqtt.service

Having enabled the service (systemctl enable essmqtt.service), systemctl status essmqtt.service shows after startup:

● essmqtt.service - ESS MQTT Communication
   Loaded: loaded (/etc/systemd/system/essmqtt.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Tue 2020-05-05 22:59:05 CEST; 1min 9s ago
  Process: 625 ExecStart=/home/user/essmqtt/bin/essmqtt --mqtt_server=192.168.0.50 --ess_password XXX --interval_seconds 5 (code=exited, status=1/FAILUR
 Main PID: 625 (code=exited, status=1/FAILURE)

May 05 22:59:05 iobroker-vm essmqtt[625]:     sock.connect(sa)
May 05 22:59:05 iobroker-vm essmqtt[625]: ConnectionRefusedError: [Errno 111] Connection refused
May 05 22:59:05 iobroker-vm essmqtt[625]: /home/user/essmqtt/lib/python3.8/site-packages/pyess/aio_ess.py:202: RuntimeWarning: coroutine 'ClientSession.close' wa
May 05 22:59:05 iobroker-vm essmqtt[625]: ERROR:asyncio:Unclosed client session
May 05 22:59:05 iobroker-vm essmqtt[625]: client_session: <aiohttp.client.ClientSession object at 0x7fa2e96fab50>
May 05 22:59:05 iobroker-vm essmqtt[625]: ERROR:asyncio:Unclosed connector
May 05 22:59:05 iobroker-vm essmqtt[625]: connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x7fa2e9705820>, 14.149501901)]']
May 05 22:59:05 iobroker-vm essmqtt[625]: connector: <aiohttp.connector.TCPConnector object at 0x7fa2e96fad30>
May 05 22:59:05 iobroker-vm systemd[1]: essmqtt.service: Main process exited, code=exited, status=1/FAILURE
May 05 22:59:05 iobroker-vm systemd[1]: essmqtt.service: Failed with result 'exit-code'

Manual start as descibed above works. Can it have something to do with permissions/ rights? I also tried a waiting time in the service but no success.

gluap commented 4 years ago

Hi @fu-zhou this looks promising, earlier on your logs showed that the module wasn't even found. The error now seems to be that the service cannot connect. Is the current log from a start at boot?

I'm asking because I had the same problem once with a systemd service that was starting before network was started. But if you're manually starting it via systemctl start the above shouldn't happen. The issue now seems to be that for some reason the service cannot connect to the network.

I'll get back to you later after trying to replicate this on a debian machine.

fu-zhou commented 4 years ago

I know what it is: The MQTT Server Adpater of iobroker needs to run first and that takes a while... According to "htop" the process/ Command which needs to run is "io.mqtt.0"

The a.m. service won't run on your system either as - I guess - you won't have an MQTT Server running on that machine...

Putting

[Unit]
Description=ESS MQTT Communication
After=iobroker.service

Is certainly the first step, but we also have to tell the service to wait until io.mqtt.0 is running:

● iobroker.service - ioBroker Server
   Loaded: loaded (/lib/systemd/system/iobroker.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2020-05-06 19:19:08 CEST; 4min 19s ago
     Docs: http://iobroker.net
 Main PID: 655 (iobroker.js-con)
    Tasks: 209 (limit: 2290)
   CGroup: /system.slice/iobroker.service
           ├─ 655 iobroker.js-controller
           ├─ 992 io.admin.0
           ├─1027 io.s7.0
           ├─1182 io.web.0
           ├─1200 io.ping.0
           ├─1215 io.javascript.0
           ├─1230 io.fullcalendar.0
           ├─1247 io.text2command.0
           ├─1264 io.telegram.0
           ├─1279 io.sql.0
           ├─1384 io.motion.0
           ├─1399 io.samsung.0
           ├─1416 io.cloud.0
           ├─1432 io.socketio.0
           ├─1449 io.backitup.0
           ├─1465 io.web.1
           ├─1671 io.info.0
           ├─1690 io.worx.0
           └─2111 io.mqtt.0
gluap commented 4 years ago

@fu-zhou great to see that you solved it. Actually I ran it against my mqtt server so it was working with systemd on my machine. I'll see that I add an optional reconnect loop. Also makes sense in case the mqtt server has to be restarted, we don't want to crash in that case, either.

fu-zhou commented 4 years ago

Actually I didn't get it resolved yet... I still miss the piece to have the essmqtt.service wait for io.mqtt.0 to be started under iobroker.service. Reconnect loop sounds good!

gluap commented 4 years ago

@fu-zhou the easiest will be to just make systemd restart your service when it fails, something along these lines:

[Unit]
Description=ESS MQTT Communication

[Service]
ExecStart=/home/user/essmqtt/bin/essmqtt --mqtt_server=<MQTT SERVER IP> --ess_password <PW> --interval_seconds <POLL INTERVALL>
Restart=on-failure
RestartSec=10 

[Install]
WantedBy=default.target
gluap commented 4 years ago

I split off the "register as an mqtt client" point from the original issue into a separate issue - it is currently hard to implement because the mqtt library used for pyess does not support a "last will and testament" and as far as I could find out that is required to provide this functionality.

edit: I also split off the "what do statuses mean" question.

fu-zhou commented 4 years ago

Awesome, the service works now after boot-up with

Restart=on-failure
RestartSec=10 
gluap commented 4 years ago

Okay, I will close this issue then. Thank you @fu-zhou for your comments and cooperation in fixing this, I will add your systemd service configuration example to the documentation later on. I will think about adding an internal auto-reconnect but for the time being i think it is fine to have it handled by systemd, maybe even better than "hiding" it behind an internal retry.