TomFaulkner / SenseMe

Python Library for Haiku SenseMe app controlled fans/lights
GNU General Public License v3.0
21 stars 10 forks source link

Where to begin? Would like to get running in Flask. #18

Closed psmith3 closed 6 years ago

psmith3 commented 6 years ago

Is there any kind of guide to get this running in flask? I have 5 fans that I want to control and I would like to statically assign them. I know the mac addresses and names. I have a Synology NAS that I am running docker on and I have Flask already running in a container. I am launching flask_app.py, but when I laugh the webpage, I get a Internal Server Error message. I am sure it is not discovering my fans. Any pointers would be much appreciated.

TomFaulkner commented 6 years ago

I haven't really ran the flask app example in some time. Do you get a traceback? Can you paste that in here?

Have you tried the library using the example in the readme and see if the fans are discovered? What do output do you get if you do this from the repl?

from senseme import discover
devices = discover(devices_to_find=5)
repr(devices)

If you can provide that output it would be helpful in trying to figure out what is going wrong for you, and probably in seeing how well the discovery method works with multiple devices. I only have one to test on.

psmith3 commented 6 years ago

No, I haven’t tried anything yet only because I am not sure what other than the flash-app.py needs to be installed. I just need some basic instructions on what files go where and what to look for in the browser. Where is the index.html file in the example supposed to reside? Do I need to modify the contents of flash-app.py?

On Mar 6, 2018, at 8:40 AM, Tom Faulkner notifications@github.com wrote:

I haven't really ran the flask app example in some time. Do you get a traceback? Can you paste that in here?

Have you tried the library using the example in the readme and see if the fans are discovered? What do output do you get if you do this from the repl?

from senseme import discover devices = discover() repr(devices) If you can provide that output it would be helpful in trying to figure out what is going wrong for you, and probably in seeing how well the discovery method works with multiple devices. I only have one to test on.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

TomFaulkner commented 6 years ago

I should probably make that more clear in the code comments. I think you would copy the flask_app.py and html file to a directory of their own, with the library in a subdirectory of that then execute with flask.

So your directory structure would look like this.

fan
    /senseme
    flask_app.py
    index.html

And then from within the fan directory run python3 flask_app.py.

You will need flask installed in your virtual environment for this to not get an import error, but I think from the sound of things that you have that requirement met.

psmith3 commented 6 years ago

Tried that and that just give me "Internal Server Error” when I launch the webpage. If I call the URL with /index.html, I get

Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org http://nginx.org/. Commercial support is available at nginx.com http://nginx.com/.

Thank you for using nginx.

Any other ideas?

On Mar 6, 2018, at 8:56 AM, Tom Faulkner notifications@github.com wrote:

I should probably make that more clear in the code comments. I think you would copy the flask_app.py and html file to a directory of their own, with the library in a subdirectory of that then execute with flask.

So your directory structure would look like this.

fan /senseme flask_app.py index.html And then from within the fan directory run python3 flask_app.py.

You will need flask installed in your virtual environment for this to not get an import error, but I think from the sound of things that you have that requirement met.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/TomFaulkner/SenseMe/issues/18#issuecomment-370807702, or mute the thread https://github.com/notifications/unsubscribe-auth/ANtRK0i0z9_PMAzhyixFIyxG9T8bM64Fks5tbqOEgaJpZM4SeHWx.

TomFaulkner commented 6 years ago

I wouldn't worry about an nginx proxy yet, I would suggest trying to get it working prior to putting it behind a proxy, as the proxy will add to the complexity. What if you try to access it directly instead of through nginx?

psmith3 commented 6 years ago

I’m not sure why it’s going through this proxy. I have it running in a flask docker container. Let me see if I can get it running standalone outside of docker and see if I have the same issues. Thank you

On Mar 6, 2018, at 9:35 AM, Tom Faulkner notifications@github.com wrote:

I wouldn't worry about an nginx proxy yet, I would suggest trying to get it working prior to putting it behind a proxy, as the proxy will add to the complexity. What if you try to access it directly instead of through nginx?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

TomFaulkner commented 6 years ago

I gave the flask app example a try tonight. It worked as expected, aside from one small issue that I put a work around in for. I added instructions to the docstring of the flask_app.py file. By default flask runs on port 5000, so be sure you are including :5000 in the address.

psmith3 commented 6 years ago

Thank you. Will give it a try again.

TomFaulkner commented 6 years ago

You are running an old version of Python. I take advantage of fstrings which were introduced in Python 3.6.

Your NAS device appears to be running some kind of Linux, Try running python3 --version and see if it is 3.6.x. If so, run python3 -m venv venv (the last venv is a directory, this can be whatever you want. Then run source venv/bin/activate where the venv is whatever you used in the previous command. Then pip install flask and then try the flask command again.

If your python3 version isn't some version of 3.6 you would need to install Python 3.6. Read how to do this safely on Linux. Overwriting your system Python3 can be dangerous to the stability of the system if the system's own utilities are relying on something that was depracted or changed in 3.6.

psmith3 commented 6 years ago

I cannot find a way to upgrade my Synology NAS to 3.6.3. Python 3.5.1 is all that I can go up to natively. I was able to find a Docker version that I installed for Flask and has Python 3.6.3 on it. I just cannot get past errors when running flash-app.py.

I am running a docker container from [https://hub.docker.com/r/jazzdd/alpine-flask/].

I did have to rename your flash-app.py to app.py for this container to run it. Would renaming it matter?

When I try to go to the webpage, I get an internal server error and when I look at the Docker log for the container, I see

 --- no python application found, check your startup logs for errors ---                                                                   
[pid: 15|app: -1|req: -1/7] 172.17.0.1 () {40 vars in 636 bytes} [Fri Mar  9 23:16:09 2018] GET / => generated 21 bytes in 0 msecs (HTTP/1
.1 500) 2 headers in 83 bytes (0 switches on core 1)                                                                                      
172.17.0.1 - - [09/Mar/2018:23:16:09 +0000] "GET / HTTP/1.1" 500 32 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5
.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6"   

The startup log reveals the following:

Traceback (most recent call last): | stdout
File "./app.py", line 5, in <module> | stdout
from senseme import discover | stdout
File "./senseme/__init__.py", line 1, in <module> | stdout
from senseme.senseme import SenseMe, discover
File "./senseme/senseme.py", line 15, in <module>
  from .lib import MWT, BackgroundLoop
File "./senseme/lib/__init__.py", line 1, in <module>
from .background_monitor import BackgroundLoop
ModuleNotFoundError: No module named 'senseme.lib.background_monitor'
unable to load app 0 (mountpoint='') (callable not found or import error)
*** no app loaded. going in full dynamic mode ***
dropping root privileges after application loading
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI worker 1 (pid: 43, cores: 2)
spawned uWSGI worker 2 (pid: 44, cores: 2)
spawned uWSGI worker 3 (pid: 45, cores: 2)
spawned uWSGI worker 4 (pid: 47, cores: 2)
TomFaulkner commented 6 years ago

The flask app filename doesn't matter.

As to the issue you are having now, that one is my fault. It seems I forgot to add the background_monitor module I wrote for this library. I'm not at home now, but I should be able to get that uploaded in about ten hours or so. Sorry about that.

psmith3 commented 6 years ago

Thanks. Just let me know and I will try it again.

TomFaulkner commented 6 years ago

It is updated now. My gitignore was set to ignore directories named lib, which is what I chose to name my utility modules. Oops. It should work now. Please let me know if you have any more issues, and if you think anything should be added or changed.

The flask_app.py is meant as an example, it isn't complete, I know it lacks whoosh mode, and the example is only intended to operate on the first device it finds. Hopefully it is a start. If you decide to extend it I would love to see what you do with it.

psmith3 commented 6 years ago

Thanks,

I will give it a try tonight. LoL:-) I was hoping whoosh mode was there. Any chance you can tell me how to add it? I was planning on having an Insteon Keypadlinc with this mode programmed on one off the buttons. Let me get it working first.

On Mar 12, 2018, at 6:09 PM, Tom Faulkner notifications@github.com wrote:

It is updated now. My gitignore was set to ignore directories named lib, which is what I chose to name my utility modules. Oops. It should work now. Please let me know if you have any more issues, and if you think anything should be added or changed.

The flask_app.py is meant as an example, it isn't complete, I know it lacks whoosh mode, and the example is only intended to operate on the first device it finds. Hopefully it is a start. If you decide to extend it I would love to see what you do with it.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/TomFaulkner/SenseMe/issues/18#issuecomment-372492461, or mute the thread https://github.com/notifications/unsubscribe-auth/ANtRK33HFsSR117K9N6qW6EcqhCmd3HCks5tdwARgaJpZM4SeHWx.

TomFaulkner commented 6 years ago

It should be as simple as this, then adding the link to the HTML file in template.

@app.route("/whoosh/on")
def whoosh_on():
    fan.whoosh = True  # change this to fan.whoosh
    flask.flash('Turning Whoosh On')
    return flask.redirect(flask.url_for('index'))

Then do the opposite for /whoosh/off.

psmith3 commented 6 years ago

Well, maybe getting closer, but still not working. Here is my log now. I have 5 fans. Am I supposed to change fan = discover()[5]? I have not changed anything in the app.py until now. The log is in reverse...

date | stream | content
-- | -- | --
2018-03-13 03:32:46 | stdout | spawned uWSGI worker 4 (pid: 17, cores: 2)
2018-03-13 03:32:46 | stdout | spawned uWSGI worker 3 (pid: 15, cores: 2)
2018-03-13 03:32:46 | stdout | spawned uWSGI worker 2 (pid: 14, cores: 2)
2018-03-13 03:32:46 | stdout | spawned uWSGI worker 1 (pid: 9, cores: 2)
2018-03-13 03:32:46 | stdout | *** uWSGI is running in multiple interpreter mode ***
2018-03-13 03:32:46 | stdout | dropping root privileges after application loading
2018-03-13 03:32:46 | stdout | *** no app loaded. going in full dynamic mode ***
2018-03-13 03:32:46 | stdout | unable to load app 0 (mountpoint='') (callable not found or import error)
2018-03-13 03:32:46 | stdout | IndexError: list index out of range
2018-03-13 03:32:46 | stdout | fan = discover()[5]
2018-03-13 03:32:46 | stdout | File "./app.py", line 21, in <module>
2018-03-13 03:32:46 | stdout | Traceback (most recent call last):
2018-03-13 03:32:40 | stdout | *** Operational MODE: preforking+threaded ***
2018-03-13 03:32:40 | stdout | mapped 333504 bytes (325 KB) for 8 cores
2018-03-13 03:32:40 | stdout | your mercy for graceful operations on workers is 60 seconds
2018-03-13 03:32:40 | stdout | your server socket listen backlog is limited to 100 connections
2018-03-13 03:32:40 | stdout | python threads support enabled
2018-03-13 03:32:40 | stdout | dropping root privileges after plugin initialization
2018-03-13 03:32:40 | stdout | Python main interpreter initialized at 0x7f0d0259d1c0
2018-03-13 03:32:40 | stdout | Python version: 3.6.3 (default, Nov 21 2017, 14:55:19)  [GCC 6.4.0]
2018-03-13 03:32:40 | stdout | dropping root privileges after socket binding
2018-03-13 03:32:40 | stdout | uwsgi socket 0 bound to UNIX address /run/uwsgiApp.sock fd 3
2018-03-13 03:32:40 | stdout | thunder lock: disabled (you can enable it with --thunder-lock)
2018-03-13 03:32:40 | stdout | lock engine: pthread robust mutexes
2018-03-13 03:32:40 | stdout | detected max file descriptor number: 524288
2018-03-13 03:32:40 | stdout | your memory page size is 4096 bytes
2018-03-13 03:32:40 | stdout | your processes number limit is 63944
2018-03-13 03:32:40 | stdout | *** WARNING: you are running uWSGI without its master process manager ***
2018-03-13 03:32:40 | stdout | chdir() to /app
2018-03-13 03:32:40 | stdout | setuid() to 100
2018-03-13 03:32:40 | stdout | set additional group 82 (www-data)
2018-03-13 03:32:40 | stdout | setgid() to 101
2018-03-13 03:32:40 | stdout | dropping root privileges as early as possible
2018-03-13 03:32:40 | stdout | detected binary path: /usr/sbin/uwsgi
2018-03-13 03:32:40 | stdout | writing pidfile to /run/.pid
2018-03-13 03:32:40 | stdout | current working directory: /app
2018-03-13 03:32:40 | stdout | detected number of CPU cores: 4
2018-03-13 03:32:40 | stdout | pcre jit disabled
2018-03-13 03:32:40 | stdout | clock source: unix
2018-03-13 03:32:40 | stdout | machine: x86_64
2018-03-13 03:32:40 | stdout | nodename: flask-python3
2018-03-13 03:32:40 | stdout | os: Linux-3.10.102 #15254 SMP Fri Jan 26 06:43:30 CST 2018
2018-03-13 03:32:40 | stdout | compiled with version: 6.4.0 on 15 February 2018 14:37:19
2018-03-13 03:32:40 | stdout | *** Starting uWSGI 2.0.16 (64bit) on [Tue Mar 13 03:32:40 2018] ***
2018-03-13 03:32:40 | stdout | [uWSGI] getting INI configuration from /app.ini
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 8#8: start worker process 13
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 8#8: start worker process 12
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 8#8: start worker process 11
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 8#8: start worker process 10
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 8#8: start worker processes
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 7#7: getrlimit(RLIMIT_NOFILE): 524288:1048576
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 7#7: OS: Linux 3.10.102
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 7#7: nginx/1.12.2
2018-03-13 03:32:40 | stdout | 2018/03/13 03:32:40 [notice] 7#7: using the "epoll" event method
2018-03-13 03:32:40 | stdout | Running app in production mode!
TomFaulkner commented 6 years ago

Changing that to 5 would mean you want to use the sixth fan that is discovered, and since it didn't discover 6 fans, you received an IndexError. That number is the index of a list, with the first being 0. As an example I grabbed the first fan and worked with it as fan.

It would take some time to adapt the example to be easily usable for multiple devices. Assuming discovery works it is doable, but the problem would be that each function talks to fan, which with the example code is the first discovered device.

The path of least resistance, I would suspect, assuming you don't care about the webpage, would be to take a parameter for the index on the function decorator and pass that to the function and call the function on that fan index. (http://exploreflask.com/en/latest/views.html#built-in-converters)

@app.route("/<index>/whoosh/on")  # note the addition of <index> here
def whoosh_on(index):  # index added here as well
    fan[index].whoosh = True  # added of [index] here
    flask.flash('Turning Whoosh On')
    return flask.redirect(flask.url_for('index'))

And at the beginning of the file, you would remove the [0] from the discover line so fan would now be a list of fans.

If this works for you then I would suggest hard coding the fans, as discover is not likely to receive answers in the same order on each run. You might do that something like:

devices = [('192.168.1.50', 'Living Room Fan'), ('192.168.1.51', 'Living Room Fan 1')]
fan = []
for device in devices:
    fan.append(SenseMe(ip=device[0], name=device[1]))

# then you should be able to print them like this:
print(fan)

Of course, changing the name to fans would probably make more sense, but you'll have a lot of instances to find and replace that way.