├── .gitignore
├── error.png
├── screenshot.jpg
├── bin
└── rsvg-convert
├── lib
├── librsvg-2.so.2
└── libcroco-0.6.so.3
├── sync2kindle.sh
├── menu.json
├── weatherstation.conf
├── config.py
├── config.xml
├── create-png.sh
├── wifi.sh
├── README.md
├── weather2svg.py
├── kindle-weather.sh
└── weather-preprocess.svg
/.gitignore:
--------------------------------------------------------------------------------
1 | config.py
2 | weather-script-output.svg
3 | out.png
4 | __pycache__
5 |
--------------------------------------------------------------------------------
/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzzw/kindle-weatherstation/HEAD/error.png
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzzw/kindle-weatherstation/HEAD/screenshot.jpg
--------------------------------------------------------------------------------
/bin/rsvg-convert:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzzw/kindle-weatherstation/HEAD/bin/rsvg-convert
--------------------------------------------------------------------------------
/lib/librsvg-2.so.2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzzw/kindle-weatherstation/HEAD/lib/librsvg-2.so.2
--------------------------------------------------------------------------------
/sync2kindle.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | rsync -zrvh --update * root@kindle:/mnt/us/extensions/weatherstation
3 |
--------------------------------------------------------------------------------
/lib/libcroco-0.6.so.3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattzzw/kindle-weatherstation/HEAD/lib/libcroco-0.6.so.3
--------------------------------------------------------------------------------
/menu.json:
--------------------------------------------------------------------------------
1 | {
2 | "items": [
3 | {
4 | "name": "Weatherstation",
5 | "action": "setsid ./kindle-weather.sh &"
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/weatherstation.conf:
--------------------------------------------------------------------------------
1 | start on started cmd
2 | stop on stopping cmd
3 |
4 | export LANG LC_ALL
5 |
6 | chdir /mnt/us/extensions/weatherstation
7 | exec ./kindle-weather.sh
8 |
9 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | ### API URL, don't touch
2 | api_url = 'https://api.openweathermap.org/data/3.0/onecall?'
3 |
4 | ### Change your key
5 | api_key = '00000000000000000000000000000000'
6 |
7 | ### Adjust your lat/lon, units and language
8 | ### --> https://openweathermap.org/api/one-call-api
9 | api_param = 'lat=53.5&lon=10&units=metric&lang=de'
10 |
--------------------------------------------------------------------------------
/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Weatherstation
5 | 1.0
6 | mattzz
7 | KAUL-SELF-MENU-ADD-ONS
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/create-png.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | PWD=$(pwd)
4 | export LD_LIBRARY_PATH=$PWD/lib
5 |
6 | $PWD/weather2svg.py
7 | if [ $? -eq 0 ]
8 | then
9 | $PWD/bin/rsvg-convert --background-color=white -o kindle-weather.png weather-script-output.svg
10 | # $PWD/bin/pngcrush -q -c 0 out.png kindle-weather.png
11 | else
12 | cp error.png kindle-weather.png
13 | fi
14 |
--------------------------------------------------------------------------------
/wifi.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | id="`wpa_cli add_network | sed -n '2p'`"
3 | echo $id
4 |
5 | exec="`wpa_cli << EOF
6 | set_network $id ssid \"Your SSID\"
7 | set_network $id psk \"Your PW\"
8 | set_network $id key_mgmt WPA-PSK
9 | set_network $id group CCMP TKIP
10 | set_network $id proto RSN WPA
11 | set_network $id pairwise CCMP TKIP
12 | select_network $id
13 | enable_network $id
14 | quit
15 | EOF
16 | `"
17 | echo $exec
18 | udhcpc -i wlan0
19 |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kindle Weatherstation
2 |
3 | This is a serverless implementation of a weatherstation for the Kindle (PW2)
4 | that is also optimized for battery runtime (~ a month).
5 |
6 | The Kindle fetches weather data from Openweathermap.org every 60 minutes, creates a SVG file
7 | based on a template, converts the SVG to PNG, displays the newly generated PNG and
8 | goes to sleep (STR/suspend to RAM) for the remaining time.
9 |
10 | 
11 |
12 | ## What's what
13 | * `weather2svg.py`: Queries weather data and assembles a SVG file based on template
14 | * `create-png.sh`: Gets data, converts svg file to png and compresses png file
15 | * `kindle-weather.sh`: Main loop, gets and displays data, suspend to RAM and wakeup
16 | * `weather-preprocess.svg`: SVG template
17 | * `config.xml`: KUAL config file
18 | * `menu.json`: KUAL config file
19 | * `sync2kindle.sh`: rsyncs all files to Kindle, helps during development/debugging
20 | * `wifi.sh`: Wifi helper script
21 | * `weatherstation.conf`: Upstart script
22 |
23 | [rsvg-convert](https://github.com/ImageMagick/librsvg) and [pngcrush](https://pmt.sourceforge.io/pngcrush/) binaries and libs are included.
24 |
25 | ## Kindle preparation:
26 | * Hack the kindle (doh!) --> [Mobileread Forum Thread](https://www.mobileread.com/forums/showthread.php?t=320564)
27 | * Install KUAL
28 | * Add USBnetworking and python (via MPRI)
29 |
30 | ## Installation:
31 | * Adjust key and location in `config.py`
32 | * Adjust SSID and passphrase in `wifi.sh`
33 | * Create directory /mnt/us/extensions/weatherstation
34 | * Copy everything to the newly created directory (or use sync2kindle.sh)
35 |
36 | ## Upstart script
37 | Optionally, you can use a startup script to start the weatherstation automatically when the Kindle comes up.
38 |
39 | * `$ mntroot rw`
40 | * `$ cp /mnt/us/extensions/weatherstation/weatherstation.conf /etc/upstart`
41 | * `$ mntroot ro`
42 | * `$ start weatherstation`
43 |
44 | ## Stopping weatherstation:
45 | * Either press button, quickly login and do a `killall kindle-weather.sh` (or if you are using the upstart script `stop weatherstation`).
46 | * Or force reboot kindle by holding powerbutton ~10 seconds
47 |
48 | ## Credits:
49 | * Building on ideas and code from
50 | * https://mpetroff.net/2012/09/kindle-weather-display/ and
51 | * https://github.com/nicoh88/kindle-kt3_weatherdisplay_battery-optimized
52 |
--------------------------------------------------------------------------------
/weather2svg.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import requests
4 | from datetime import datetime
5 | import json
6 | import codecs
7 | import subprocess
8 | import config
9 |
10 |
11 | api_url = config.api_url + config.api_param + '&appid=' + config.api_key
12 |
13 | try:
14 | r = requests.get(api_url)
15 | r.raise_for_status()
16 | except requests.exceptions.HTTPError as errh:
17 | print ("Http Error: ",errh)
18 | exit(-1)
19 | except requests.exceptions.RequestException as e:
20 | print ("Problem getting data: " + str(e))
21 | exit(-1)
22 |
23 | # read the data from the URL and print it
24 | weather = r.json()
25 |
26 | # process SVG
27 | output = codecs.open('weather-preprocess.svg', 'r', encoding='utf-8').read()
28 |
29 | output = output.replace('#NOW', datetime.fromtimestamp(weather['current']['dt']).strftime('%d %b %Y, %H:%M:%S'))
30 |
31 | # current weather
32 | output = output.replace('#IC00',weather['current']['weather'][0]['icon'])
33 | output = output.replace('#TN','{:.0f}'.format(weather['current']['temp']))
34 | output = output.replace('#HI00','{:.0f}'.format(weather['daily'][0]['temp']['max']))
35 | output = output.replace('#LO00','{:.0f}'.format(weather['daily'][0]['temp']['min']))
36 | output = output.replace('#SUMNOW', weather['current']['weather'][0]['description'])
37 | # output = output.replace('#SUMHR', weather['hourly'][0]['weather'][0]['description'])
38 | output = output.replace('#DP0', '{:.0f}'.format(weather['daily'][0]['pop'] * 100))
39 | # rain and snow keys are not always present.
40 | precip = 0
41 | if 'rain' in weather['daily'][0]:
42 | precip = weather['daily'][0]['rain']
43 | if 'snow' in weather['daily'][0]:
44 | precip += weather['daily'][0]['snow']
45 | output = output.replace('#DM0', '{:.2f}'.format(precip))
46 | output = output.replace('#DBP', '{:.0f}'.format(weather['daily'][0]['pressure']))
47 | output = output.replace('#DHU', '{:.0f}'.format(weather['daily'][0]['humidity']))
48 |
49 | output = output.replace('#SR', datetime.fromtimestamp(weather['daily'][0]['sunrise']).strftime('%H:%M'))
50 | output = output.replace('#SS', datetime.fromtimestamp(weather['daily'][0]['sunset']).strftime('%H:%M'))
51 |
52 | # battery
53 | # depending on board type
54 | # could also be e.g. /sys/devices/system/yoshi_battery/yoshi_battery0/battery_capacity
55 | proc_out = subprocess.Popen("gasgauge-info -s".split(),
56 | stdout=subprocess.PIPE,
57 | stderr=subprocess.STDOUT)
58 | battery_capacity,stderr = proc_out.communicate()
59 | output = output.replace('#BAT', battery_capacity.decode("utf-8"))
60 |
61 | # next 12 hours
62 | for i in range(1, 13):
63 | istr = "{:02d}".format(i)
64 | output = output.replace('#IC'+istr, weather['hourly'][i]['weather'][0]['icon'])
65 | output = output.replace('#TM'+istr, str(datetime.fromtimestamp(weather['hourly'][i]['dt']).strftime('%H:%M')))
66 | output = output.replace('#TE'+istr, '{:.0f}'.format(weather['hourly'][i]['temp']))
67 | output = output.replace('#PP'+istr, '{:.0f}'.format(weather['hourly'][i]['pop'] * 100))
68 | precip = 0
69 | if 'rain' in weather['hourly'][i]:
70 | precip = weather['hourly'][i]['rain']['1h']
71 | if 'snow' in weather['hourly'][i]:
72 | precip += weather['hourly'][i]['snow']['1h']
73 | output = output.replace('#PA'+istr, '{:.2f}'.format(precip))
74 |
75 | #next 7 days
76 | for i in range (1, 8):
77 | istr = str(i)
78 | output = output.replace('#DA'+istr, str(datetime.fromtimestamp(weather['daily'][i]['dt']).strftime('%a %d.%-m.')))
79 | output = output.replace('#DI'+istr, weather['daily'][i]['weather'][0]['icon'])
80 | output = output.replace('#DH'+istr, '{:.0f}'.format(weather['daily'][i]['temp']['max']))
81 | output = output.replace('#DL'+istr, '{:.0f}'.format(weather['daily'][i]['temp']['min']))
82 | output = output.replace('#DP'+istr, '{:.0f}'.format(weather['daily'][i]['pop'] * 100))
83 | precip = 0
84 | if 'rain' in weather['daily'][i]:
85 | precip = weather['daily'][i]['rain']
86 | if 'snow' in weather['daily'][i]:
87 | precip += weather['daily'][i]['snow']
88 | output = output.replace('#DM'+istr, '{:.2f}'.format(precip))
89 |
90 | #output = output.replace('#SUMDAILY', weather['daily']['summary'])
91 |
92 | codecs.open('weather-script-output.svg', 'w', encoding='utf-8').write(output)
93 |
--------------------------------------------------------------------------------
/kindle-weather.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | PWD=$(pwd)
4 | LOG="/mnt/us/weatherstation.log"
5 | SLEEP_MINUTES=60
6 | FBINK="fbink -q"
7 | FONT="regular=/usr/java/lib/fonts/Palatino-Regular.ttf"
8 |
9 | ### uncomment/adjust according to your hardware
10 | #PW3
11 | #FBROTATE="/sys/devices/platform/imx_epdc_fb/graphics/fb0/rotate"
12 | #BACKLIGHT="/sys/devices/platform/imx-i2c.0/i2c-0/0-003c/max77696-bl.0/backlight/max77696-bl/brightness"
13 |
14 | #PW2
15 | FBROTATE="/sys/devices/platform/mxc_epdc_fb/graphics/fb0/rotate"
16 | BACKLIGHT="/sys/devices/system/fl_tps6116x/fl_tps6116x0/fl_intensity"
17 |
18 | wait_wlan_connected() {
19 | return `lipc-get-prop com.lab126.wifid cmState | grep CONNECTED | wc -l`
20 | }
21 |
22 | wait_wlan_ready() {
23 | return `lipc-get-prop com.lab126.wifid cmState | grep -e READY -e PENDING -e CONNECTED | wc -l`
24 | }
25 |
26 | ### Dim Backlight
27 | echo -n 0 > $BACKLIGHT
28 |
29 | ### Prepare Kindle, shutdown framework etc.
30 | echo "------------------------------------------------------------------------" >> $LOG
31 | echo "`date '+%Y-%m-%d_%H:%M:%S'`: Starting up, killing framework et. al." >> $LOG
32 |
33 | ### stop processes that we don't need
34 | stop lab126_gui
35 | ### give an update to the outside world...
36 | echo 0 > $FBROTATE
37 | $FBINK -w -c -f -m -t $FONT,size=20,top=410,bottom=0,left=0,right=0 "Starting weatherstation..." > /dev/null 2>&1
38 | #echo 3 > $FBROTATE
39 | sleep 1
40 | ### keep stopping stuff
41 | stop otaupd
42 | stop phd
43 | stop tmd
44 | stop x
45 | stop todo
46 | stop mcsd
47 | stop archive
48 | stop dynconfig
49 | stop dpmd
50 | stop appmgrd
51 | stop stackdumpd
52 | sleep 2
53 |
54 | # At this point we should be left with a more or less Amazon-free environment
55 | # I leave
56 | # - powerd & deviced
57 | # - lipc-daemon
58 | # - rcm
59 | # running.
60 |
61 | ### If we have a wan module installed...
62 | #if [ -f /usr/sbin/wancontrol ]
63 | #then
64 | # wancontrol wanoffkill
65 | #fi
66 |
67 | ### Disable Screensaver
68 | lipc-set-prop com.lab126.powerd preventScreenSaver 1
69 |
70 | echo "`date '+%Y-%m-%d_%H:%M:%S'`: Entering main loop..." >> $LOG
71 |
72 | while true; do
73 |
74 | NOW=$(date +%s)
75 |
76 | let SLEEP_SECONDS=60*SLEEP_MINUTES
77 | let WAKEUP_TIME=$NOW+SLEEP_SECONDS
78 | echo `date '+%Y-%m-%d_%H:%M:%S'`: Wake-up time set for `date -d @${WAKEUP_TIME}` >> $LOG
79 |
80 | ### Dim Backlight
81 | echo -n 0 > $BACKLIGHT
82 | ### Force landscape mode
83 | echo 0 > $FBROTATE
84 |
85 | ### Disable CPU Powersave
86 | echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
87 |
88 | lipc-set-prop com.lab126.cmd wirelessEnable 1
89 | ### Wait for wifi interface to come up
90 | echo `date '+%Y-%m-%d_%H:%M:%S'`: Waiting for wifi interface to come up... >> $LOG
91 | while wait_wlan_ready; do
92 | sleep 1
93 | done
94 |
95 | ### Wifi interface is up, connect to access point.
96 | ./wifi.sh
97 |
98 | ### Wait for WIFI connection
99 | TRYCNT=0
100 | NOWIFI=0
101 | echo `date '+%Y-%m-%d_%H:%M:%S'`: Waiting for wifi interface to become ready... >> $LOG
102 | while wait_wlan_connected; do
103 | if [ ${TRYCNT} -gt 30 ]; then
104 | ### waited long enough
105 | echo "`date '+%Y-%m-%d_%H:%M:%S'`: No Wifi... ($TRYCNT)" >> $LOG
106 | NOWIFI=1
107 | eips -f -g $PWD/error.png
108 | break
109 | fi
110 | sleep 1
111 | let TRYCNT=$TRYCNT+1
112 | done
113 |
114 | echo `date '+%Y-%m-%d_%H:%M:%S'`: WIFI connected! >> $LOG
115 |
116 | ### Get Weatherdata
117 | echo `date '+%Y-%m-%d_%H:%M:%S'`: Getting weatherdata >> $LOG
118 | rm -f $PWD/kindle-weather.png
119 |
120 | if $PWD/create-png.sh; then
121 | #eips -f -g $PWD/kindle-weather.png
122 | $FBINK -c -f -i kindle-weather.png -g w=-1,h=-1
123 | else
124 | echo `date '+%Y-%m-%d_%H:%M:%S'`: Something went wrong getting weatherdata >> $LOG
125 | eips -f -g $PWD/error.png
126 | sleep 60
127 | fi
128 |
129 | #echo `date -d @${WAKEUP_TIME}` | xargs -0 eips
130 | BAT=$(gasgauge-info -s | sed s/%//)
131 | # echo $BAT | xargs -0 eips -f 1 1
132 | echo `date '+%Y-%m-%d_%H:%M:%S'`: Battery level: $BAT >> $LOG
133 |
134 | ### Enable powersave
135 | echo powersave > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
136 |
137 | ### flight mode on...
138 | lipc-set-prop com.lab126.cmd wirelessEnable 0
139 |
140 | ### 3 secs necessary on Kindle PW (EY21) to avoid random hangs
141 | sleep 3
142 |
143 | ### set wake up time to one hour
144 | rtcwake -d /dev/rtc1 -m no -s $SLEEP_SECONDS
145 | ### Go into Suspend to Memory (STR)
146 | echo `date '+%Y-%m-%d_%H:%M:%S'`: Sleeping now... >> $LOG
147 | echo "mem" > /sys/power/state
148 | done
149 |
--------------------------------------------------------------------------------
/weather-preprocess.svg:
--------------------------------------------------------------------------------
1 |
2 |
304 |
--------------------------------------------------------------------------------