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

Request: New MQTT option #31

Closed
Duzeper opened this issue Nov 9, 2020 · 62 comments
Closed

Request: New MQTT option #31

Duzeper opened this issue Nov 9, 2020 · 62 comments

Comments

@Duzeper
Copy link

Duzeper commented Nov 9, 2020

I am using PictureFrame2020. Is there any chance of adding an MQTT option to toggle display/remove filenames interactively.
Generally the photos I am displaying are travel photos and there are times when I think, 'I can't remember exactly where that is', but if I could briefly bring up the filename then I could look it up.
Normally I don't want to display filenames so I have parameter -s set to '0.0'

@paddywwoof
Copy link
Member

@Duzeper this is something that's cropped up a few times. Wolfgang asked about a couple of things that could be switched using MQTT (possibly with voice activation) and I need to sort out all the other good ideas and put something in the docs if I don't add the actual code.

@Duzeper
Copy link
Author

Duzeper commented Nov 14, 2020 via email

@paddywwoof
Copy link
Member

Martin, I haven't use MQTT on my setup for a while (the 'free' testing servers keep deciding they can't afford to keep being free) I will install an MQTT server on my RPi and check it out and see if I can reproduce you issue. Thanks for reporting the problem.
Paddy

@Duzeper
Copy link
Author

Duzeper commented Nov 14, 2020 via email

@Duzeper
Copy link
Author

Duzeper commented Nov 16, 2020 via email

@paddywwoof
Copy link
Member

Martin, I'm working my way through the MQTT stuff now. What I did was (all via ssh to the RPi which is running at command prompt with no desktop):

  1. install mostquitto-clients sudo apt install -y mosquitto mosquitto-clients
  2. install paho-mqtt sudo pip3 install paho-mqtt NB I installed it for --user without sudo at first but running xinit has to be done as root using sudo so python couldn't find the module!!!
  3. in PictureFrame2020config.py
parse.add_argument("-m", "--use_mqtt",      default=True)
parse.add_argument(      "--mqtt_server",   default="localhost")
parse.add_argument(      "--mqtt_port",     default=1883, type=int)
parse.add_argument(      "--mqtt_login",    default="")
parse.add_argument(      "--mqtt_password", default="")
  1. at the command line sudo xinit /usr/bin/python3 /home/pi/pi3d_demos/PictureFrame2020.py to start PictureFrame running
  2. at the command line in a different terminal mosquitto_pub -h localhost -t frame/paused -m "" toggles the display paused or not
  3. mosquitto_pub -h localhost -t frame/quit -m "" stops the program immediately and Ctrl-C in the ssh terminal
    I haven't altered PictureFrame from the version on pi3d_demos develop branch (it already has frame/paused and frame/quit functionality). Let me know if you did the same things and got a different result.

@Duzeper
Copy link
Author

Duzeper commented Nov 22, 2020

OK, to recap, I had done 1 & 2, and with parameter -m set to default=False, then the Picture Frame would run normally and display photos one after the other.
However, when I set the parameter to True, then the frame is permanently paused. I confirmed this by issuing the command at 5, and the frame then unpauses. Command 5 then toggles pause/unpause as expected.
So to clarify. When 'use_mqtt' is set to true, the frame starts paused!

Command 6 (quit) works fine as well
I also added a 'back' command (mosquitto_pub -h localhost -t frame/back -m "" ) which also works fine.
So command line instructions work fine.

However, when it comes to putting this into Android MQTT Dash, it gets a bit more complicated:-
'Quit' (app.publish("frame/quit", "quit", false, O);) works fine.
'Pause' (app.publish("frame/paused", "pause", false, O);) doesn't work at all
'Back' (app.publish("frame/back", "back", false, O);) sometimes goes back 1 image, sometimes 2 and sometimes to the first image in the folder.
I wonder if trying a different MQTT app would be worth a try.
That, however, would not solve the issue of the frame being paused at startup if MQTT is enabled!

PS I could not use command 4 (xinit.....) as it gave an error:-

pi@raspberrypi:~ $ sudo xinit /usr/bin/python3 /home/pi/pi3d_demos/PictureFrame2020.py

(EE)
Fatal server error:
(EE) Server is already active for display 0
If this server is no longer running, remove /tmp/.X0-lock
and start again.
(EE)
(EE)
Please consult the The X.Org Foundation support
at http://wiki.x.org
for help.
(EE)
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 7 requests (7 known processed) with 0 events remaining.

I can start the frame with 'python3 /home/pi/pi3d_demos/PictureFrame2020.py' so I assume that is not an issue.

@paddywwoof
Copy link
Member

Hi OK I will check the paused at start issue (I didn't notice that but it might have been - the raspberry pi is in a different room from where my laptop is (nice warm kitchen!)) and set up MQTT dash and play with that. The fact that your last line works means that you must have the desktop running which will stop xinit working. i.e. you need to change your boot so you go to the command line. Even if you are logging in as a different session, the fact that there is already an X session using the display will give you that error.

@paddywwoof
Copy link
Member

OK. the MQTT dash seems to work pretty well. I set the Address and Port to 192.168.0.190 and 1883 and in the pause option Topic (sub) is frame/paused I don't know what the other defaults are but Enable publishing is ticked and Update metric on publish immediately is ticked.

One thing I have found with MQTT is that it will 'remember' the status from the last time it was run, so if it closed down with paused True maybe it starts off paused. But actually, your problems sound more involved than that so probably not the culpret.

I did, however notice that the, frame/delete option in PictureFrame2020.py has a bug. It checks for and copies to ~/DeletedPictures however when it's started with sudo xinit ... that creates a directory /root/DeletedPictures which is not intended and I will fix.

@Duzeper
Copy link
Author

Duzeper commented Nov 22, 2020

I have downloaded an Android app called MQTT Dashboard and set up all three commands (Quit, Pause, Back) and all of them work perfectly, although the pause at start issue is still there.
Bearing in mind my almost total lack of linux knowledge, I wondered if, when starting the frame, as a workaround at least, would it be feasible to send two commands one after the other ie. Command 1 to start the frame followed by Command 2 to run the pause/unpause command? Is that what the && command could do?
Also, and this may actually be another new MQTT request, given that the pause topic is a toggle, is it possible for the frame to send back the pause status to the android client? If I set the time between images to, say 15 min, it could be some time before I realise that it is still paused!

@paddywwoof
Copy link
Member

I suppose the PictureFrame program could send an MQTT message to switch paused off when it starts, it does seem a bit odd though. It's probably a good idea to make the code read the message payload as well as the topic. At the moment it just toggles the paused status but it would be easy to allow a payload "off", "on" or "" where the last toggles but the other two set it to a value. Or you could have two buttons: a pause on and a pause off.

Now I've done a few tries I can't get it to start with the pictureframe paused. So it's the opposite way round from you!

@Duzeper
Copy link
Author

Duzeper commented Nov 23, 2020

I have assumed that the meat of PictureFrame2020.py, where the images are displayed and changed, is from line 349,
'while DISPLAY.loop_running():'

So to confirm the 'paused' status, I inserted 'print(paused) immediately before that line. When I run the program it does display 'True'
I then replaced this line with 'paused = False' and the program now runs properly.
I note that Paused is set to 'False' at line 36 but I cannot see anywhere where that could be changed to 'True'.

Can you think of any unintended consequences of this solution?

Re. displaying the paused status, would it be possible to display the word Paused on screen in a similar way to the filename display (but only while 'paused = True')
I haven't quite (or rather, anywhere near) got my head around that part of the code yet. Any advice would be very much appreciated.

@paddywwoof
Copy link
Member

Hi, I can't see any where the variable paused can be set to True apart from by the MQTT server which must remember its status from a previous run. Setting paused to False just before the loop starts might not work all the time because it will depend on how quickly the MQTT server sends the message (i.e. it operates asynchronously, possibly over the internet). Maybe it would be a good idea to send MQTT messages from program at startup as mentioned before.

Showing the paused status with the text/date message is a good idea. I will add that and push the modified code to the develop branch as soon as I can.

@Duzeper
Copy link
Author

Duzeper commented Nov 23, 2020

You were half right. It wasn't the MQTT Dash that was remembering the status, It was Mosquitto. I uninstalled then reinstalled it and the paused issue is resolved. Clearly though, it was MQTT Dash that somehow caused the issue in the first place.
I have given up with MQTT Dash (I will stick with MQTT Dashboard) I deleted all the settings that I had created, deleted the appdata and cache from android, then uninstalled the app. I then tried reinstalling it, and all the settings were back as they were. The app developer's answer to why that was happening was 'Have no idea, sorry'

@paddywwoof
Copy link
Member

paddywwoof commented Nov 25, 2020

I've put a modified version of PictureFrame2020 on the develop branch. It has a few changes:

  • 'frame/next' shows the next image
  • pausing can be toggled by sending 'frame/paused' with no payload. Or it can be set by sending a payload "on","true","yes" and released with "off","false","no" (case insensitive) 'PAUSED' is appended to the text (if it is). At the moment tThe SHOW_TEXT_TM is set to 1/3 of the overall slide time but I might make it settable using a payload value.can be set with payload as time in seconds (if it's bigger than 2).
  • 'frame/text_on', 'frame/date_on', 'frame/date_text_on', 'frame/text_off' show or hide file name and date
  • deleted files are put explicitly into '/home/pi/DeletedPictures' which might need to be modified if that's not where you want them

In defense of MQTT Dash I think their toggle system works on the basis that payload is set to a value and held on the server. So rather than PictureFrame toggling the python value of paused it should have been looking at the payload sent by MQTT and setting it to that. There are options when setting up the behavior of the dashboard buttons that indicate that's the way they expect it to be used.

@Duzeper
Copy link
Author

Duzeper commented Nov 25, 2020

Not strictly relevant to this thread but, can you explain why, if I start the Picture Frame from the terminal with
'python3 /home/pi/pi3d_demos/pictureFrame2020.py', although it doesn't seem to affect the operation of the frame, I see 'glGetError 0x500' in the terminal?

@Duzeper
Copy link
Author

Duzeper commented Nov 25, 2020

Sorry, but I'm not fully au fait with github. I have found the files in the develop branch. To use these new files, is there a download option, or do I just copy and paste the code?

@paddywwoof
Copy link
Member

It's frustrating not being able to find where that glGetError message comes from but I don't think it does any harm. I will have yet another search for it.

For just a one-off use such as this it's probably easier to just copy and paste. If you want to swap from master to develop and submit pull requests etc then you need to clone the repository. The advantage of the latter is that you can go back to a working version very easily if the new changes break something on your setup... but you can achieve the same thing by saving the old version as PictureFrame2020backup.py.

@Duzeper
Copy link
Author

Duzeper commented Nov 26, 2020

Just had a chance to try the new version. However, I get this error at start up:-
pi@raspberrypi:~ $ python3 /home/pi/pi3d_demos/PictureFrame2020.py
Traceback (most recent call last):
File "/home/pi/pi3d_demos/PictureFrame2020.py", line 25, in
locale.setlocale(locale.LC_TIME, config.LOCALE)
File "/usr/lib/python3.7/locale.py", line 604, in setlocale
return _setlocale(category, locale)
locale.Error: unsupported locale setting

@paddywwoof
Copy link
Member

Hi, just checking that you did use the revised PictureFrame2020config.py file as well?

@Duzeper
Copy link
Author

Duzeper commented Nov 26, 2020

Yes

@paddywwoof
Copy link
Member

That's odd as, presumably, your RPi is essentially set up the same way as mine. Could you open a python3 terminal and enter

>>> import locale
>>> locale.LC_TIME
>>> locale.setlocale(locale.LC_TIME, "en_US.utf8")

and see what you get. If that runs without an error maybe put line24 print(config.LOCALE)
If you do get the same error try

>>> locale.locale_alias

and see if en_US is in there.
Finally if you want a different locale you could put the code for that in your config file.
Thanks for testing this out.
Paddy

@Duzeper
Copy link
Author

Duzeper commented Nov 26, 2020

OK
pi@raspberrypi:~ $ python3
Python 3.7.3 (default, Jul 25 2020, 13:03:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

import locale
locale.LC_TIME
2
locale.setlocale(locale.LC_TIME, "en_US.utf8")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.7/locale.py", line 604, in setlocale
return _setlocale(category, locale)
locale.Error: unsupported locale setting

Re. locale.locale_alias, is there a way to present the output in columns?

@paddywwoof
Copy link
Member

>>> for k,v in locale.locale_alias.items():
...   print(k, v)

@Duzeper
Copy link
Author

Duzeper commented Nov 26, 2020

I couldn't get that to work, however I have searched the text of ' locale.locale_alias' output, and, although there are various 'en_US.ISO*****' entries, there is no 'en_US.UTF8'. However there is 'en_US.UTF-8'. Can I assume that 'UTF8' should read 'UTF-8?
Given that I am not in the US, however, is there any reason not to use 'en_GB.ISO8859-1'?

@Duzeper
Copy link
Author

Duzeper commented Nov 26, 2020

OK, didn't realise I needed to hit return twice to get the column display.
Anyway, looking again at the output I see that there are the following:-
en_gb en_GB.ISO8859-1
en_uk en_GB.ISO8859-1
en_us en_US.ISO8859-1

Also several variations of 'en' (eng, english), but none are UTF-8
I have tried replacing 'en_US.utf8' in the config file and your terminal commands with all of these (eg 'en_US.ISO8859-1') and I get the same error each time.

@paddywwoof
Copy link
Member

OK after a quick google! I think the locales must be something set up when you first got raspbian going - they're tied up with how things are displayed on the desktop and what keys on the keyboard are where. At the command prompt I typed
sudo dpkg-reconfigure locales
then went down the list till I got to en_GB.UTF-8 and pressed space to put an asterix in the box. I then tabbed to exit (and on the next page I chose that as my default but that might not be necessary). Then exited. After than "en_GB.UTF-8" worked with python locale.setlocale()

I will have to add something to the docs and, at the very least, put this command inside a try/except block.

Thanks again for your help

@Duzeper
Copy link
Author

Duzeper commented Nov 27, 2020

That seemed to work. I say 'seemed' only because, although the frame did, this time, start up OK it couldn't find any images. It turned out that the external drive was corrupted and then, coincidentally, there was a catastrophic crash, because the OS card was also corrupted and refused to reboot.
Fortunately I had an OS backup card, but it is not completely up to date. So, although I am not starting completely from square 1 it is not far off.
Don't you just love computers!

@Duzeper
Copy link
Author

Duzeper commented Dec 1, 2020

OK, back working.
I have been running through the MQTT options.
Success with:
/time_delay
/fade_time
/quit
/pause
/back
/next

Now, I apologize in advance if I am missing something obvious.

  1. Although I have /paused working with a multi-option/radio button control, looking at your code, it looks as though your intention was to append 'PAUSED' to displayed date/filename text. However, this doesn't happen. Actually, I wonder how it could. Although the text times out even if the image is paused, surely it can't be re-displayed until the next image, which will only appear if the frame is un-paused?

I had hoped that, to display the fact that the frame was actually paused, the word 'PAUSED' would appear on screen regardless of the show_text setting, and then disappear when restarted.

  1. That brings me to the show-text MQTT settings. I see that there are 4 topics:
    /text_on
    /date_on
    /date_text_on
    /text_off

Firstly, what are the approprite payloads for these? If they are 'true' or 'false' (or equivalents), then isn't /date_text_on superfluous? Couldn't you just set the other 2 to 'true'?

Secondly, wouldn't it be easier to have just 1 /display_text topic with 4 possible options and then use a radio button control like the /paused command?

@paddywwoof
Copy link
Member

paddywwoof commented Dec 1, 2020

Hi, sorry not to keep you in t he loop. I've had other discussions with Wolfgang about options and several things have changed.

Most importantly I've been persuaded that having an optional location lookup would be a nice feature. i.e. if your photos are location tagged then there is a config option to enable the lookup of info from an online service to find the text description matching the lat and lon. (the text is saved so the lookup only happens the first time).

This means the --show_text config option can include 'name', 'date' and 'location' i.e. name_date or 'date location' and the MQTT messages have become /text_on /date_on /location_on and /text_off the first three toggling name, date and location the last turning them all off.

However (re your last point) in order to control the length of time the text is shown for the above three options the payload is used as time in seconds. If it's empty then the time of the text is set to 1/3 of the total slide time.

Also, just to make you even more bamboozled, I have made the MQTT option for frame/paused take a payload so you can specify a state by passing a payload (on, true, yes)->paused (off, false, no)->un-paused (case insensitive) This means you can have two buttons on your mqtt dashboard one to always pause and one to always start it running.

I will look at the logic to make PAUSED show even when there isn't any other text - I think I did look before but can't remember what the issue was.

Sorry to mess you around. Wolfgang is testing the location feature and will find any other bugs in all this stuff hopefully, then I will write something up and get it into the master branch. Thanks for your help testing as well

Paddy

@paddywwoof
Copy link
Member

Hi, I have just pushed v2.40 of pi3d and pi3d_demos to github and pypi.python.org so you should be able to use the pip3 installed version.

Those uncaught characters (returned by the geo location but not in the CODEPOINTS given to the Font) are now skipped as well as fixing the fact that a shorter description sometimes left a previous longer description showing and it now searches through for printable text rather than just using parts 2,3 and last from the address.

I'm not sure why PAUSED doesn't show. It's supposed to so probably not a major fix.

It's certainly possible to publish MQTT topics (it already publishes /frame/paused -> false on startup) but it would be better to rearrange the MQTT section as an instance of a class and put all the code in an external file. It's not a completely trivial change as there are loads of global variables being changed by the MQTT functions (yuck). Probably the easiest solution short term would be put a new function in the MQTT section that is triggered by a message it's subscribed to, that then sends whatever MQTT messages you want.

@Duzeper
Copy link
Author

Duzeper commented Dec 3, 2020

Sorry, but I have just downloaded the latest PictureFrame2020.py and PictureFrame2020geo.py, and I still get the KeyError: 'ð' with those Iceland photos.
Also, PAUSED is still not showing unless text, date or location are displayed.

@paddywwoof
Copy link
Member

paddywwoof commented Dec 3, 2020

Hi, the fix for the KeyError is in the code for pi3d/pi3d/util/TextBlock.py so you need to move to the latest version of pi3d for that:
$ sudo pip3 install pi3d --upgrade
I've fixed the PAUSED not showing, you can either download PictureFrame2020.py again or put this tiny patch yourself (line 442)

      # set the file name as the description
      if config.SHOW_TEXT_TM > 0.0 or paused:
        txt = ""

There is still the issue that if you set --show_text_tm to 0.0 the paused will show but only for one frame at zero opacity! And using MQTT frame/text_off will set text_tm to zero. Now that I have changed the text display to a binary mask system I should switch the checking to use that instead, I will let you know when I've done that - probably later today. EDIT Made the changes now.

In the mean time thanks for the feedback

Paddy

@sapnho
Copy link

sapnho commented Dec 3, 2020

Thanks for all your development work, Paddy. I have just published an article explaining this new geo feature based on Pi3D: https://www.thedigitalpictureframe.com/how-to-display-the-place-where-your-photo-was-taken-with-the-raspberry-pi-digital-picture-frame-geolocation-feature/ and will update it should the code change in the coming days.

@Duzeper
Copy link
Author

Duzeper commented Dec 3, 2020

Just tried the latest files:
PictureFrame2020.py
PictureFrame2020config.py
PictureFrame2020geo.py
(from develop branch)
Also upgraded pi3d

and I get this error:

pi@raspberrypi:~ $ python3 /home/pi/pi3d_demos/PictureFrame2020.py
==DSC_0690.JPG
Traceback (most recent call last):
File "/home/pi/pi3d_demos/PictureFrame2020.py", line 458, in
textblock.set_text(text_format=txt, wrap=config.TEXT_WIDTH)
TypeError: set_text() got an unexpected keyword argument 'wrap'

File name is different every time (because shuffle is set to True?)

@paddywwoof
Copy link
Member

paddywwoof commented Dec 4, 2020

Ah sorry about that. Still in a state of flux on the develop branch. If you use develop pi3d_demos you need to have develop branch pi3d too. Basically if you:

$ cd ~/pi3d_demos
$ git checkout master

All the changes apart from long line wrapping are in the master branch of pi3d_demos so that's your easiest option.

On the other hand if you want to use the latest changes in the pi3d_demos develop branch (I've implemented line wrapping in the pi3d.TextBlock class so long descriptions go onto two lines) Then you will need to have the pi3d repo cloned and

$ cd ~/pi3d
$ git checkout master

But in order to stop this cloned repository being picked up in future I would advise you to rename it after you've stopped experimenting i.e. $ mv ~/pi3d ~/pi3d_repo

@Duzeper
Copy link
Author

Duzeper commented Dec 4, 2020

OK, thanks. The reason I went to the develop branch was that they were the most recently updated.
If it's only line wrapping then perhaps I should go back to the master branch.

Has, not showing PAUSED show_text is turned off been fixed in the master branch?

@Duzeper
Copy link
Author

Duzeper commented Dec 4, 2020

I have just reverted to the (current) master branch files, but I still don't see 'PAUSED' when name,date,location off.

Also, can you explain the reason for sometimes seeing 'NO IMAGES SELECTED'? It does appear to happen if I try testing MQTT commands shortly after the frame has started, but it has happened at other times when sending, I think, a subdirectory change. Sorry I can't be more specific, but it does seem to be intermittent (I know, the worst problem to diagnose!), but does appear to be MQTT related. If I just leave the frame to get on with just displaying photos (which, of course, is what it's there for) it is working OK

@paddywwoof
Copy link
Member

Hi there are a couple of issues (I've subsequently patched and now in the develop branch, unfortunately) that Wolfgang pointed out. The 'no images' issue is due to the MQTT happening synchronously with the display loop i.e. if the current picture number changed while a picture was being loaded. The PAUSED seems to work reliably even when no other text is on, but it only remains visible for the standard show_text_tm. I have added an MQTT frame/text_refresh that will just restart the current slide and make the text re-show.

There might be some additional issue with the subdirectory command. I've had a couple of 'no images' message when I wouldn't have expected them, but not reproducible and hasn't happened for the last day. So maybe it's fixed itself (hmm!)

I will test the code on different computers and raspberry pis today and, hopefully, push out the new master version of pi3d_demos and pi3d (v2.41) tonight.

Anyway, thanks for carrying on testing.

Paddy

@Duzeper
Copy link
Author

Duzeper commented Dec 4, 2020

Thanks. So if it's a 'timing' issue, that would certainly explain why I couldn't guarantee to reproduce the error.
Many years ago, before I retired, I did a lot of programming in Microsoft VBA and, more than once, came across bugs that subsequently proved impossible to reproduce!
Going back to the PAUSED issue. I cannot see PAUSED at all if the other text is off. Clearly it is there in the text because if I turn the text back on whilst paused, then it displays PAUSED.
Re. it only displaying for the standard show_text_tm, that actually only occurred to me today as an issue, so I wondered if it would be possible, instead of appending 'PAUSED' to the 'show_text' string, could you not generate and display a separate 'PAUSED' string which displays as for long as the frame is paused?

@paddywwoof
Copy link
Member

Yes, computer systems generally have moved on from the idea of a single process jogging along on its own. MQTT can change variables used in the rest of the program at any time so really there should be a comprehensive mutex system... However the whole program has evolved from a rather simple demo so is rather is much more complicated than it should be! This is why I really enjoy using Rust.

I'm not sure what the best approach is for the PAUSED message. The NO IMAGES SELECTED system has it's own section (look around line 520) that doesn't reduce the alpha value of the text and logically pausing might do something similar. However I think a lot of people will want to pause the slideshow at a picture they particularly like - and not want any message showing at the bottom. The current system (I've just pushed it to pi3d_demos, pi3d master and pypi.python.org as I type this) might be a reasonable compromise (without adding a series of additional config and MQTT variables) i.e. the paused message shows when you pause it then fades but you can have an MQTT topic to refresh the status if you want to check it out.

@Duzeper
Copy link
Author

Duzeper commented Dec 5, 2020

Sorry, but I've just tried the current files from the master branch and whenever I hit any of the MQTT text display commands, the frame stops with this error:

pi@raspberrypi:~ $ python3 /home/pi/pi3d_demos/PictureFrame2020.py
0
glGetError 0x500
Traceback (most recent call last):
File "/home/pi/pi3d_demos/PictureFrame2020.py", line 463, in
textblock.set_text(text_format=txt, wrap=config.TEXT_WIDTH)
TypeError: set_text() got an unexpected keyword argument 'wrap'

Also sometimes when I hit pause (strangely not always)

Re. your comments on the timing of MQTT cammands I have made sure that the time_delay is sufficiently long to be sure of not sending the command at the same time as a frame change.

@paddywwoof
Copy link
Member

Looks like you're still running with the old version of pi3d. I would try:

$ sudo pip3 install pi3d --upgrade
$ mv ~/pi3d ~/pi3d_repo # to 'hide' it from being added to sys.path by demo.py

@sapnho
Copy link

sapnho commented Dec 5, 2020

I did a fresh install to test the new Pi3D version as well as an upgrade on another Pi and my frame has been running without a glitch for 12h now.

@Duzeper
Copy link
Author

Duzeper commented Dec 5, 2020

Sorry. I should read things more carefully. Didn't see pi3d (v2.41) in your earlier post

I've now done the upgrade. However:
$ mv ~/pi3d ~/pi3d_repo
mv: cannot stat '/home/pi/pi3d': No such file or directory

@paddywwoof
Copy link
Member

paddywwoof commented Dec 5, 2020

That renaming was just belt and braces. If you had cloned pi3d locally it would have been picked up and caused problems. If you hadn't the the worst that could happen was a no such directory message.

Is it working OK now?

@Duzeper
Copy link
Author

Duzeper commented Dec 5, 2020

Has been working OK since my last post.
Many thanks for all your help. I just need to find a new 'lockdown' project now.

@Duzeper
Copy link
Author

Duzeper commented Dec 7, 2020

Sorry, me again.
Still working OK, but I have another question.
Since I don't see the point of leaving the frame running overnight, I have configured a 'frameOn' and a frame'Off' timer/service. The 'frameOn' service powers on the display and starts the frame with:
ExecStart=sudo vcgencmd display_power 1
ExecStart=/usr/bin/python3 /home/pi/pi3d_demos/PictureFrame2020.py

The 'frameOff' service powers off the display and stops the frame with:
ExecStart=sudo vcgencmd display_power 0
ExecStart=sudo systemctl stop frameOn.service

This all works perfectly well. I have set the default subdirectory as "" and I then choose the subdirectory I wish to play over a period of days with an MQTT command.
I wonder however, since that would need to be reset every morning, is there any way, when the frame restarts, for it to start in the same subdirectory as when it stopped (say, pass a subdirectory string to the frame at startup)?

@paddywwoof
Copy link
Member

Yes, you can pass any of the config values on start i.e.

sudo xinit /usr/bin/python3 /home/pi/pi3d_demos/PictureFrame2020.py --subdirectory=holiday_in_cambodia

But I'm not sure how you could automate that. It would be nice if the position in the sequence was remembered and the shuffle delayed until all had been shown but if the program is stopped the whole of the file list would have to be written to disk (one of the examples on Wolfgang's blog shows how somebody did exactly that I think)

It would be easy enough to save the subdirectory value to a tiny txt, json or pickle file whenever an MQTT message arrived to change it. Then the program could check that when it started, but you might need some complicated code to cope with going back to no subdirectory selected...

@Duzeper
Copy link
Author

Duzeper commented Dec 9, 2020

OK, I have inserted these lines (in PictureFrame2020.py) after line 316, to create a text file containing the latest subdirectory MQTT message.

subdirfile = open("usbdrive/subdir.txt", "w")
subdirfile.write(subdirectory)
subdirfile.close()

Also, I have added a subdirectory MQTT command with no payload. this, effectively, sends an empty string, thus resetting the subdirectory parameter to "".

Then, before I next start PictureFrame2020.py, I redirect the contents of subdir.txt, if it exists, or an empty string if it doesn't yet exist or has been deleted, to a variable which is then passed to PictureFrame2020.py:

With some assistance from the Raspberrypi.org forum I have created a file:

vcgencmd display_power 1
test -f usbdrive/subdir.txt && subdirvar=$(cat usbdrive/subdir.txt) || subdirvar=""
/usr/bin/python3 /home/pi/pi3d_demos/PictureFrame2020.py --subdirectory=$subdirvar

which is then called from my frameOn.service

This all does what I wanted.

One more question though. The line:

ExecStart=sudo systemctl stop frameOn.service

no longer works in frameOff.service. I know that I could use 'pkill' to terminate PictureFrame2020.py, but is that the best way to do it?

Sorry if this is a bit rambling but it' getting late!

@paddywwoof
Copy link
Member

Well done for getting it working, however the mechanisms of running and stopping sytemd services is pretty confusing.
My instinct would be not use bash but rather have a simple python script (to be honest I can't really think of very many situations where bash would be preferable)

import subprocess
try:
    with open("usbdrive/subdir.txt", "r") as f:
        subdir = "--subdirectory={}".format(f.read())
except:
    subdir = ""
subprocess.run(["/usr/bin/python3", "/home/pi/pi3d_demos/PictureFrame2020.py", subdir])

and make the FrameOn service start this instead

or (my favourite option I think) effectively add the code to read usbdrive/subdir.txt at the bottom of PictureFrame2020config.py and just change the SUBDIRECTORY 'constant' if a) nothing has been put on the command line and b) there is something in usbdrive/subdir.txt

Paddy

PS I think it's good practice to always use the with open(..) as f: form for reading and writing files nowadays.

@Duzeper
Copy link
Author

Duzeper commented Dec 9, 2020

Thanks, I'll give that a go.
However, I'm still not clear on the best way to stop the frame.

@paddywwoof
Copy link
Member

paddywwoof commented Dec 9, 2020

Did it work OK prior to making frameOn.service start the picture frame via a bash script? (i.e. did frameOff stop python when it stopped the frameOn.service) My thinking is that you can revert to the setup that worked by altering PictureFrame2020config a little bit. You could put the snippet of code into its own file and include it to simplify the process when there are new releases of pi3d_demos.

if SUBDIRECTORY == "":
    try:
        with open("usbdrive/subdir.txt", "r") as f:
            SUBDIRECTORY = f.read()
    except:
        pass

The only issue then is differentiating between "" as the default for no command line argument entered and "" entered to set the subdirectory to nothing. I suppose that would happen with your MQTT command.

@Duzeper
Copy link
Author

Duzeper commented Dec 10, 2020

Sorry, but I'm not entirely sure I understand the last post.
I have tried saving:

import subprocess
try:
with open("usbdrive/subdir.txt", "r") as f:
subdir = "--subdirectory={}".format(f.read())
except:
subdir = ""
subprocess.run(["/usr/bin/python3", "/home/pi/pi3d_demos/PictureFrame2020.py", subdir])

as piccy.py, and then calling it from frameOn.service:
ExecStart=/usr/bin/python3 /home/pi/piccy.py

Instead of running pictureFrame2020.py
Although I can successfully run piccy.py from the command prompt, if I start frameOn.service, after a slight pause, the command prompt reappears, and although there are no errors, nothing else happens.

Yes, stop frameOn.service did work until I tried to make it call the external file

@paddywwoof
Copy link
Member

paddywwoof commented Dec 10, 2020

Hi, Oh well, maybe there are some extra settings in systemd to allow spawned process to run other processes (though what the difference is between bash and python I don't know). What I meant by my preferred solution was to reduce the number of variations from the working system:

keep frameOn.service and frameOff.service exactly as they were previous to any changes.
Keep the lines in PictureFrame2020.py that write the subdirectory to usbdrive/subdir.txt but otherwise unchanged
At the end of PictureFrame2020config.py add some lines to check usbdrive/subdir.txt something like this

...
DISPLAY_Y = args.display_y
DISPLAY_W = args.display_w
DISPLAY_H = args.display_h


CODEPOINTS = '1234567890AÄÀBCÇDÈÉÊEFGHIÍJKLMNÑOÓÖPQRSTUÚÙÜVWXYZ., _-/abcdefghijklmnñopqrstuvwxyzáéèêàçíóúäöüß' # limit to 49 ie 7x7 grid_size

if SUBDIRECTORY == "":
    try:
        with open("usbdrive/subdir.txt", "r") as f:
            SUBDIRECTORY = f.read()
    except:
        pass

@Duzeper
Copy link
Author

Duzeper commented Dec 10, 2020

Now I am really confused.
I have, as before, inserted these lines in PictureFrame2020.py:

     elif message.topic == "frame/subdirectory":
        subdirectory = msg
        
        subdirvar = open("usbdrive/subdir.txt", "w") #Write subdirectory file
        subdirvar.write(subdirectory)
        subdirvar.close()
 
        reselect = True

and these at the end of PictureFrame2020config.py:

        CODEPOINTS = '1234567890AÄÀBCÇDÈÉÊEFGHIÍJKLMNÑOÓÖPQRSTUÚÙÜVWXYZ., _-/abcdefghijklmnñopqrstuvwxyzáéèêàçíóúäöüß' # limit to 49 ie 7x7 grid_size

if SUBDIRECTORY == "":  #Check subdirectory
   try:
        with open("usbdrive/subdir.txt", "r") as f:
            SUBDIRECTORY = f.read()
   except:
        pass

When I run the ammended frame, the MQTT commands are working - except /subdirectory. When I send a /subdirectory command, all the other commands stop working. When I stop and check subdir.txt, it has not been changed.

OK, it is the 3 file writing lines that are causing the MQTT issue. If I take them out then MQTT works properly, including /subdirectory.
I am confused by this as they are the same lines I originally inserted which I am pretty sure worked OK a few daya ago

The additional code at the end of the config file is not working however.
At startup, even though 'usbdrive/subdir.txt' does exist (and is not empty) the frame is not starting with that subdirectory, it is starting as if subdirectory="".

@paddywwoof
Copy link
Member

paddywwoof commented Dec 10, 2020

I can't see anything obviously wrong with your writing to file (apart from not using the with ... as ...: construction which would, by the way, prevent errors stopping the rest of the MQTT working) I notice that your file path is relative to wherever you start the program running. What happens if you make that an absolute path /home/pi/usbdrive/subdir.txt or wherever it is (or is mapped to)

Are you running the code on boot, in which case it might be hard to get the error output - but you could do it by putting your code inside a try block like this

    try:
        with open("usbdrive/subdir.txt", "w") as subdirvar:#Write subdirectory file
            subdirvar.write(subdirectory)
    except Exception as e:
        with open("error_msg.txt", "w") as f:
            f.write(e)

PS you can define code using backticks (more explicit than just indenting) - I've edited your post so you can see how

@Duzeper
Copy link
Author

Duzeper commented Dec 10, 2020

Many thanks. You were right again. It was the path. When I changed it to the absolute path (in both files) it worked fine.
I also replaced my file writing lines with your suggested try block and that works as well. I take it that previously it was throwing an error in the background which 'crashed' the MQTT try block.
All I need to do now. is confirm that the timers are performing as expected and, you will be pleased to know, I'm good to go.

Once again thanks a million for all your help.

@paddywwoof
Copy link
Member

Sounds good and hopefully there's info in this thread that might help future bug/solution searchers.

The advantage of using the 'new' style:

with open("file.txt", "w") as f:
   f.read() # etc etc 

is that it 'automatically' has a try/catch built in (so not only does it close the file automatically but it catches errors and still closes the file. So you only need the try/catch as in my example in order to get the error message and write it to a debug log file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants