mendhak / waveshare-epaper-display

At-a-glance dashboard for Raspberry Pi with a Waveshare ePaper 7.5 Inch HAT. Date/Time, Weather, Alerts, Google/Outlook Calendar
https://code.mendhak.com/raspberrypi-epaper-dashboard/
438 stars 65 forks source link

Adding an extra weather descriptor #5

Closed feh123 closed 3 years ago

feh123 commented 3 years ago

Hi @mendhak Apologies for more requests for help! I have installed inkscape on my PC and have made more space on the screen for my diary entries using it to edit the screen space. I can see how you have used nodes to add the weather icon and high/low temperature. I am tempted to add another weather icon - the descriptor for the icon itself. I can see all the categories are there but adding the icon/text has proved difficult. I am still trying to understand the extra lines of code I need. Do you have any lines I could use? Many thanks.

mendhak commented 3 years ago

Do you mean, below the 'cloud' icon you want to show the word 'cloudy'?

An easy way to do this would be to add another text node rather than icon. Just like CAL_ONE this could be WEATHER_DESC.

You then have to find the right position for it - just below the weather icon but above the high/low temperature.

Then in the Python script where the weather is being obtained, replace WEATHER_DESC with whatever text you want. That will probably need another 'mapping' like the one here but instead of mapping to an icon name, map it to a descriptive phrase.

feh123 commented 3 years ago

Hi @mendhak Yes- some of the icons are quite complicated so the text would be good. Thanks for the info - I will try it out and let you know. Thanks.

feh123 commented 3 years ago

Hi @mendhak So I can see CAL_ONE as part of a text block in screen-template.svg and I could duplicate with WEATHER_DESC and give it some suitable x, y position. But in my screen-output-get.py there is no CAL_ONE or WEATHER_DESC to duplicate with mapping code. Icon_one are present but I do not understand the coding. Any thoughts appreciated.

mendhak commented 3 years ago

You need to do the work in screen-weather-get.py: https://github.com/mendhak/waveshare-epaper-display/blob/master/screen-weather-get.py

First add a mapping similar to this one. Make a copy of it but remove the right hand side values. Call it description_dict.

description_dict=icon_dict={
    'cloudy':'Really Cloudy', 
    'clear':'Really Bright',

}

Add a description for each icon in that list.

Then do the replacement right after this

output = output.replace('WEATHER_DESC, description_dict[icon_one])

That should be enough to get you started.

If you run into problems then post your full screen weather get py, and any errors.

feh123 commented 3 years ago

Hi @mendhak thanks a lot - I will try this tomorrow.

feh123 commented 3 years ago

Hi @mendhak the new screen-weather-get.py has just run - no new errors in the run.log. I have also added WEATHER_DESC to screen-template.svg in inkscape and moved it to the right position. Using nano I copied the DAY_ONE entry in template.svg, pasted it and edited it. I do hit the issue of what text#### number to use. How do I decide what to use? Thanks!

mendhak commented 3 years ago

Those text#### are just IDs, give it any unique number you want. They only matter if you were writing code that searched for elements in the SVG by ID, but you'll see that the code is just doing text replacements, so those IDs aren't super important.

mendhak commented 3 years ago

Or give it some number, example text8415. Just make sure there isn't another text8415 in the file.

feh123 commented 3 years ago

Hi @mendhak thanks - I did just guess a number to finish the editing - I was expecting you to say it would some specific number. So I was lucky - and it has just worked. Thanks a lot for helping again!

feh123 commented 3 years ago

Hi @mendhak Sorry about this but I have been addling more weather descriptors and I have hit a problem. The program was working and I saw the weather icon and the weather descriptot. But in adding more everything else works but the weather icon has now gone. I think I have corrupted something! Here is my run.log: ound cached calendar response
Tue Dec 15 Blue Bin Collection
Tue Dec 15 Green Bin Collection
Thu Dec 17 Lucca Casa Mina - 17 - 28 Dec
Thu Dec 17 WC5W7C BA0604 to Pisa from T3
Fri Dec 18 09:00 Tesco Delivery
Fri Dec 18 15:00 Gym
Sun Dec 20 Buckie House 20 - 27 Dec - cancelled


| _| _ __ | |
|
| \ \/ / ' \ / | '| |
| | > <| |) | () | | | |
|____//_\ .
/ __/|| _|
|
|

(inkscape:27358): WARNING : 08:30:50.256: Malformed URI
DPI: 150
Background RRGGBBAA: ffffff00
Area 0:0:800:480 exported to 640 x 384 pixels (76.8 dpi)
Bitmap saved as: screen-output.png
INFO:root:epd_screen-output.py
INFO:root:init and Clear
DEBUG:root:e-Paper busy
DEBUG:root:e-Paper busy
INFO:root:1.read png file on window
DEBUG:PIL.PngImagePlugin:STREAM 'IHDR' 16 13
DEBUG:PIL.PngImagePlugin:STREAM 'gAMA' 41 4
DEBUG:PIL.PngImagePlugin:STREAM 'cHRM' 57 32
DEBUG:PIL.PngImagePlugin:STREAM 'bKGD' 101 2
DEBUG:PIL.PngImagePlugin:'bKGD' 101 2 (unknown)
DEBUG:PIL.PngImagePlugin:STREAM 'pHYs' 115 9
DEBUG:PIL.PngImagePlugin:STREAM 'tIME' 136 7
DEBUG:PIL.PngImagePlugin:'tIME' 136 7 (unknown)
DEBUG:PIL.PngImagePlugin:STREAM 'IDAT' 155 26489
DEBUG:PIL.PngImagePlugin:STREAM 'tEXt' 26656 37
DEBUG:PIL.PngImagePlugin:STREAM 'tEXt' 26705 37
DEBUG:PIL.PngImagePlugin:STREAM 'tEXt' 26754 25
DEBUG:root:Horizontal
DEBUG:root:e-Paper busy

And here is my screen-weather-get.py:

#!/usr/bin/python

import json
import requests
from xml.dom import minidom
import datetime
import codecs
import os.path
import time
import sys
import os
import html
import pytz
from astral import LocationInfo
from astral.sun import sun

climacell_apikey=os.getenv("CLIMACELL_APIKEY","")

if climacell_apikey=="":
    print("CLIMACELL_APIKEY is missing")
    sys.exit(1)

town_lat=53.081
town_long=-1.0143

template = 'screen-template.svg'

#Map Climacell icons to local icons
#Reference: https://developer.climacell.co/v3/reference#data-layers-core

icon_dict={
    'freezing_rain_heavy':'freezing_rain', 
    'freezing_rain':'freezing_rain', 
    'freezing_rain_light': 'freezing_rain' ,
    'freezing_drizzle': 'freezing_rain',
    'ice_pellets_heavy': 'ice_pellets',
    'ice_pellets': 'ice_pellets',
    'ice_pellets_light': 'rain_icepellets_mix',
    'snow_heavy': 'snow',
    'snow': 'snow',
    'snow_light': 'rain_snow_mix',
    'flurries': 'blizzard',
    'tstorm': 'thundershower_rain',
    'rain_heavy': 'rain_day',
    'rain': 'rain_day',
    'rain_light': 'rain_day',
    'drizzle': 'rain_day',
    'fog_light': 'scattered_clouds_fog',
    'fog': 'foggy',
    'cloudy': 'few_clouds',
    'mostly_cloudy':'mostly_cloudy',
    'partly_cloudy': 'few_clouds',
    'mostly_clear': 'clear_sky_day', 
    'clear': 'clear_sky_day'
}

dt = datetime.datetime.now(pytz.utc)
city = LocationInfo(town_lat,town_long)
s = sun(city.observer, date=dt)
if dt > s["sunset"] or dt < s["sunrise"]:
    icon_dict["clear"]="Clear_Night"
    icon_dict["partly_cloudy"]="Partly_Cloudy_Night"
    icon_dict["cloudy"]="Cloudy_Night"
    icon_dict["rain"]="Rain_Night"
    icon_dict["rain_light"]="Rain_Light_Night"
    icon_dict["rain_heavy"]="Rain_Heavy_Night"
    icon_dict["drizzle"]="Rain_Drizzle_Night"

description_dict=icon_dict={
    'cloudy':'Really_Cloudy',
    'clear':'Really_Bright',
    'freezing_rain_heavy':'Freezing_Rain_Heavy',
    'freezing_rain':'Freezing_Rain',
    'freezing_rain_light':'Freezing_Rain_Light',
    'freezing_drizzle':'Freezing_Rain_Drizzle',
    'ice_pellets_heavy':'Ice_Pellets_Heavy',
    'ice_pellets':'Ice_Pellets',
    'ice_pellets_light':'Rain_Ice_Pellets_Mix',
    'snow_heavy':'Snow_Heavy',
    'snow':'Snow',
    'snow_light':'Rain_Snow_Mix',
    'flurries':'Blizzard',
    'tstorm':'Thunderstorm_Rain',
    'rain_heavy':'Rain_Heavy',
    'rain':'Rain',
    'rain_light':'Rain Light',
    'drizzle':'Rain_Drizzle',
    'fog_light':'Scattered_Clouds_Fog',
    'fog':'Foggy',
    'cloudy':'Few_Clouds',
    'mostly_cloudy':'Mostly_Cloudy',
    'partly_cloudy':'Partly_Cloudy',
    'mostly_clear':'Mostly_Clear_Sky', 
    'clear':'Clear_Sky'
}

weather_json=''
stale=True

if(os.path.isfile(os.getcwd() + "/apiresponse.json")):
    #Read the contents anyway
    with open(os.getcwd() + "/apiresponse.json", 'r') as content_file:
        weather_json = content_file.read()
    stale=time.time() - os.path.getmtime(os.getcwd() + "/apiresponse.json") > (1*60*60)

#If old file or file doesn't exist, time to download it
if(stale):
    try:
        print("Old file, attempting re-download")
        url = "https://api.climacell.co/v3/weather/forecast/daily?lat={}&lon={}&unit_system=si&start_time=now&fields=temp%2Cweather_code&apikey={}".format(town_lat, town_long, climacell_apikey)
        weather_json = requests.get(url).text
        with open(os.getcwd() + "/apiresponse.json", "w") as text_file:
            text_file.write(weather_json)
    except:
        print("Failed to get new API response, will use older response")
        with open(os.getcwd() + "/apiresponse.json", 'r') as content_file:
            weather_json = content_file.read()

weather_data = json.loads(weather_json)

#icon_one = weatherData['daily']['data'][0]['icon']
icon_one = weather_data[0]['weather_code']['value']
high_one = round(weather_data[0]['temp'][1]['max']['value'])
low_one = round(weather_data[0]['temp'][0]['min']['value'])
day_one = datetime.datetime.now().strftime('%a %b %d')
latest_alert=""

# if 'alerts' in weatherData:
#     latest_alert = html.escape(weatherData['alerts'][0]['title'])

print(icon_one , high_one, low_one, day_one)

# Process the SVG
output = codecs.open(template , 'r', encoding='utf-8').read()
output = output.replace('ICON_ONE',icon_dict[icon_one])
output = output.replace('WEATHER_DESC',description_dict[icon_one])
output = output.replace('HIGH_ONE',str(high_one))
output = output.replace('LOW_ONE',str(low_one)+"°C")
output = output.replace('DAY_ONE',day_one)

output = output.replace('TIME_NOW',datetime.datetime.now().strftime("%H:%M"))

output = output.replace('ALERT_MESSAGE', latest_alert)

codecs.open('screen-output-weather.svg', 'w', encoding='utf-8').write(output)

I may have corrupted an .svg file?

I will keep checking too but any help you can give much appreciated!

feh123 commented 3 years ago

Hi @mendhak I wonder if I have accidentially overwritten the text in screen-template.svg that controls icon_one (which I guess is the weather icon). I have references to day_one, high/low temp and weather_desc but nothing to indicate icon_one - not even a position as far as I can see. Does that make sense?

feh123 commented 3 years ago

Sorry I do have a reference to it (g3127 and xlink:href="ICON_ONE") but no position. In moving my weather descriptor text around could I have invalidated the icon_one placement on the screen?

mendhak commented 3 years ago

Yeah it's probably the svg file rather than your code, since you show the script is running to completion.

How are you editing the svg file? If it's some gui tool, does it have older versions? Or have you made backup copies? Whenever you're about to edit the svg, to experiment new, it's worth making a backup of if you're familiar with git, doing a commit after each new feature.

mendhak commented 3 years ago

Might it be easier if you start from the base svg again? You'll have to add your nodes again but your code shouldn't need to change

feh123 commented 3 years ago

Hi @mendhak thanks for the ideas. I do try to remember to copy the file to a directory on my PC as a backup before doing anything. But sadly I altered things too quickly and did not have a backup! I have tried adding back bits of the base screen-template.svg I downloaded from waveshare-epaper-display-master. I edit via inkscape - I have always used the latest version. I use remote terminal (a microsoft app on my pc) to sudo nano the svg file too. But mainly it's inkscape.

Looks like I have to try the file! I will let you know how it goes. Thanks

feh123 commented 3 years ago

Hi @mendhak After backing up everything I added back the screen-template.svg from the waveshare-epaper-master I downloaded as a zip file to my PC. It did not restore the weather icon - everything else was there and correct - two diary entries, PiHole, Ad Block etc. Just an empty space for the icon. I paste run.log. I notice the inkscape error re malformed URI - I looked this up and it seemed to be a xlink font request.

\ \ / / | || | __ () / |
\ \ /\ / / \/ ` | | ' \ / \ '| | | ' | | / _ \ \ V V / / (| | || | | | / | | | | | | | () | _/_/ _|_,|_|| ||\|| ||| ||_| ___/

Old file, attempting re-download cloudy 10 8 Tue Dec 15


/ _| | | _ | | () / _|
| | /
| |/ _ \ '_ \ / _ |/ _` | '
| | | ' | | / _ \ | || (_| | | / | | | (| | (| | | | | | | | | () | ___,||\|| ||_,|_,|| ||| ||_| ___/

Found cached calendar response Pickle is stale, calling the Calendar API Tue Dec 15 Blue Bin Collection Tue Dec 15 Green Bin Collection Thu Dec 17 Lucca Casa Mina - 17 - 28 Dec Thu Dec 17 WC5W7C BA0604 to Pisa from T3 Fri Dec 18 09:00 Tesco Delivery Fri Dec 18 15:00 Gym Sun Dec 20 Buckie House 20 - 27 Dec - cancelled


| _| _ __ | | | | \ \/ / ' \ / | '| | | | > <| |) | () | | | | |____//_\ ./ __/|| _| ||
Failed to get connection (inkscape:1688): CRITICAL : 12:01:06.415: dbus_g_proxy_new_for_name: assertion 'connection != NULL' failed

(inkscape:1688): CRITICAL : 12:01:06.415: dbus_g_proxy_call: assertion 'DBUS_IS_G_PROXY (proxy)' failed

(inkscape:1688): CRITICAL : 12:01:06.416: dbus_g_connection_register_g_object: assertion 'connection != NULL' failed

(inkscape:1688): WARNING : 12:01:07.697: Malformed URI DPI: 150 Background RRGGBBAA: ffffff00 Area 0:0:800:480 exported to 640 x 384 pixels (76.8 dpi) Bitmap saved as: screen-output.png INFO:root:epd_screen-output.py INFO:root:init and Clear DEBUG:root:e-Paper busy DEBUG:root:e-Paper busy INFO:root:1.read png file on window DEBUG:PIL.PngImagePlugin:STREAM 'IHDR' 16 13 DEBUG:PIL.PngImagePlugin:STREAM 'gAMA' 41 4 DEBUG:PIL.PngImagePlugin:STREAM 'cHRM' 57 32 DEBUG:PIL.PngImagePlugin:STREAM 'bKGD' 101 6 DEBUG:PIL.PngImagePlugin:'bKGD' 101 6 (unknown) DEBUG:PIL.PngImagePlugin:STREAM 'pHYs' 119 9 DEBUG:PIL.PngImagePlugin:STREAM 'tIME' 140 7 DEBUG:PIL.PngImagePlugin:'tIME' 140 7 (unknown) DEBUG:PIL.PngImagePlugin:STREAM 'IDAT' 159 32768 DEBUG:PIL.PngImagePlugin:STREAM 'tEXt' 35454 37 DEBUG:PIL.PngImagePlugin:STREAM 'tEXt' 35503 37 DEBUG:PIL.PngImagePlugin:STREAM 'tEXt' 35552 25 DEBUG:root:Horizontal DEBUG:root:e-Paper busy

mendhak commented 3 years ago

I can't tell much from the run log, ultimately the problem is between that Python script and the SVG.

OK try this then. What you did with the SVG (restoring from master zip) do the same with the screen weather get as well. Something at some point is messing up. Restore the screen weather get, test that's working.

Then slowly add your changes back in one by one.

feh123 commented 3 years ago

Hi @mendhak thanks for this. I have tried systematically going through files and comparing them originals, I have found format issues and corrected them but it has made no difference. It is so odd that for nearly two days it was working fine. I struggle to think what I could have done. But I could easily be missing it - I know punctuation/indenting is so important. The good news is that the weather desciptor text is working so in fact the I get all the data I want even now - so its a great project! I will try going back and checking both original files. I have tried to understand the coding going on and I think I see how it works (posssibly!) but there are three things I cannot figure out. Where does the software keep the weather cons? I did think I might have deleted this! How is the position of icon_one controlled - could I have moved it to the wrong position? And coding the weather_desc and high-low temp - both use the same code in the .svg but icon_one uses a very different code - why is that? All the info seems to come via the apiresponse.json. This is the frst time I have seen inkscape used in this way - it's very interesting! Thanks!

mendhak commented 3 years ago

Right so:

Where does the software keep the weather cons?

It's in the SVG itself. Open up the SVG as text, and search for mostly_cloudy. You will see this:

<path
       id="mostly_cloudy"
       d="

All that stuff after it is basically the drawing of the icon. You'll see a whole bunch of icons right next to it. They have the name, that matches up with your Python dictionaries.

How is the position of icon_one controlled - could I have moved it to the wrong position?

The weather icon is here:

<g
     transform="scale(1.2) translate(75 215)"
     id="g3127">
    <use
       xlink:href="#ICON_ONE"
       id="use3129" />
  </g>

The positioning is the scale + translate attributes you can see there. If you've changed any of that then yeah it'll have moved somewhere else. The Python code replaces that #ICON_ONE with something like #mostly_cloudy.

And coding the weather_desc and high-low temp - both use the same code in the .svg but icon_one uses a very different code - why is that? All the info seems to come via the apiresponse.json.

I didn't fully understand this q, but I'll explain what I can. The icon_one is just a reference to a built-in icon in the SVG, it's done so that it can scale to whatever size you need. But most of the other replacements look the same, it's just a text being replaced. It's just the weather icon which is very different as it's a reference to another icon, which is that #ICON_ONE

feh123 commented 3 years ago

Hi @mendhak Thanks for all the answers! I have an update on my status - adding back template.svg and weather-get.py from the master files worked and the logo re-appeared - brilliant! To save time I then just added back my working .svg and all my new nodes remained as well as the logo - even better. You answers about my .svg file said it was okay - I checked all the values and they were the same as the master .svg. In my weather-get.py I added back my lat, long - this worked too (my lat, lon is close to the numbers already there). Is adding my lat, lon what I should do or does the program somehow select them?

I then added the code defining description_dict=icon_dict={...} - this loses the weather icon. I have tried adding this just after the icon definitions and after the set night block - in both places the icon is still missing. The WEATHER_DESC line is present still - nothing as changed - just the icon is not present. Is there a fix? Just to say my RPi inkscape version is 0.92.4 but my .svg is using 1.0.1 - should that be a problem? Thanks for all your help.

mendhak commented 3 years ago

Can you post your SVG and PY here, I think you can just attach it. I can try running it myself and see what's wrong; I should be able to find some time today/tomorrow. You said that when you start adding your description_dict, that's when stuff goes wrong, so I'll just look at that area.

Is adding my lat, lon what I should do or does the program somehow select them?

Yeah put your lat long there, and it gets used for the Climacell API call and also gets used in the astral library to figure out when sunset happens in your area to turn some of the icons 'dark'.

feh123 commented 3 years ago

Hi @mendhak thanks for offering to check them. But I cannot get copy/paste or drag/drop to work. I can give you access to them via google drive: REMOVED

They are the two files currently working - so all my calendar nodes are there including the new WEATHER_DESC which naturally contains no data. Icon_one is working fine as is high/low temp. Let me know when you have them and I can deactivate the link. Thanks again.

mendhak commented 3 years ago

Downloaded, please deactivate the link

I've also edited your comment to remove the link but please deactivate it on G Drive

feh123 commented 3 years ago

Okay, thanks. Good luck with the files!

mendhak commented 3 years ago

I didn't touch the .SVG. I edited the screen-weather-get.py.

Right after icon_dict {} block, I have added this:

descriptions_dict={
    'freezing_rain_heavy':'miserable out there',
    'freezing_rain':'miserable out there',
    'freezing_rain_light': 'miserable out there' ,
    'freezing_drizzle': 'miserable out there',
    'ice_pellets_heavy': 'miserable out there',
    'ice_pellets': 'miserable out there',
    'ice_pellets_light': 'miserable out there',
    'snow_heavy': 'miserable out there',
    'snow': 'miserable out there',
    'snow_light': 'miserable out there',
    'flurries': 'miserable out there',
    'tstorm': 'miserable out there',
    'rain_heavy': 'miserable out there',
    'rain': 'miserable out there',
    'rain_light': 'miserable out there',
    'drizzle': 'miserable out there',
    'fog_light': 'miserable out there',
    'fog': 'miserable out there',
    'cloudy': 'miserable out there',
    'mostly_cloudy':'miserable out there',
    'partly_cloudy': 'miserable out there',
    'mostly_clear': 'miserable out there',
    'clear': 'miserable out there'
}

Then right near the end of the file, in the Process the SVG section, I've added the WEATHER_DESC line.

# Process the SVG
output = codecs.open(template , 'r', encoding='utf-8').read()
output = output.replace('ICON_ONE',icon_dict[icon_one])
output = output.replace('WEATHER_DESC',descriptions_dict[icon_one])
output = output.replace('HIGH_ONE',str(high_one))
output = output.replace('LOW_ONE',str(low_one)+"°C")
output = output.replace('DAY_ONE',day_one)

Result: image

feh123 commented 3 years ago

Hi @mendhak Okay. Brilliant - I will try that now! Thanks.

feh123 commented 3 years ago

Hi @mendhak Sorry - the weather desriptor is back but the icon has gone. Maybe I have miss typed it - can you send me the file? I will re-check it again later. I can set up another google drive link if you need. Thanks.

mendhak commented 3 years ago

Python script attached.

screen-weather-get.py.feh123.txt

mendhak commented 3 years ago

I haven't modified the SVG that you sent, just used it as-is. So whatever you need to narrow down should be in the Python script

feh123 commented 3 years ago

Hi @mendhak Thanks a lot. I will be very careful with this and let you know tomorrow!

feh123 commented 3 years ago

Hi @mendhak Success!! End the end it was quick to add it and check. Thanks so much. I now just have to edit the miserable out there text - I did not want to touch anything before running it! What was I doing wrong? Thanks again

mendhak commented 3 years ago

Yay glad to hear.

Also I just spotted the problem. It was copy paste error in my original advice.

description_dict=icon_dict={

A double variable assignment. So I think it was actually overwriting the icon dictionary itself.

The correct way to make a new dictionary was

descriptions_dict={
feh123 commented 3 years ago

Hi @mendhak Well I would never have spotted that - so thanks again for checking it. I will make sure I backup all this now - as I do like to keep adjusting things. But it does look great!