diff --git a/config.example.toml b/config.example.toml index b8e4662..7bc8f06 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,6 +1,4 @@ [display] -width = 1200 -height = 825 rss_url = "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml" location = [40.75, -73.99] timezone = "America/New_York" diff --git a/inkplate_dashboard/chrome.py b/inkplate_dashboard/chrome.py index 37554ae..78c6291 100644 --- a/inkplate_dashboard/chrome.py +++ b/inkplate_dashboard/chrome.py @@ -35,7 +35,8 @@ def screenshot_display(display: DisplayConfiguration) -> bytes: "--force-device-scale-factor=1", "--disable-lcd-text", # B&W display f"--screenshot={screenshot_path}", - f"--window-size={display.width},{display.height}", + "--window-size=825,1200", + "--virtual-time-budget=10000", "--timeout=10000", # load fonts "http://127.0.0.1:8000/live/html", ], @@ -60,6 +61,7 @@ def screenshot_display(display: DisplayConfiguration) -> bytes: palette_img.putpalette(palette * 32) img = Image.open(screenshot_path).convert("RGB") img = img.quantize(kmeans=0, palette=palette_img).convert("L") + img = img.rotate(90, expand=1) output = io.BytesIO() img.save(output, "png") diff --git a/inkplate_dashboard/config.py b/inkplate_dashboard/config.py index aa9ff4f..dcbbeb8 100644 --- a/inkplate_dashboard/config.py +++ b/inkplate_dashboard/config.py @@ -11,8 +11,6 @@ class UnitEnum(str, Enum): class DisplayConfiguration(BaseModel): - width: int - height: int rss_url: HttpUrl location: tuple[float, float] locale: str diff --git a/inkplate_dashboard/sources/weather.py b/inkplate_dashboard/sources/weather.py index 04dbfc5..a88e088 100644 --- a/inkplate_dashboard/sources/weather.py +++ b/inkplate_dashboard/sources/weather.py @@ -29,11 +29,12 @@ def get_weather(display: DisplayConfiguration) -> list[WeatherObservation]: # Fetch JSON request = requests.get( MET_API_URL, - headers={"user-agent": "inkplate-dashboard meda.ugo@gmail.com"}, + headers={"user-agent": "github.com/ugomeda/inkplate-dashboard"}, params={ "lat": f"{display.location[0]:.2f}", "lon": f"{display.location[1]:.2f}", }, + timeout=3, ) request.raise_for_status() data = request.json() diff --git a/inkplate_dashboard/statics/fonts/roboto-latin-300-normal.woff b/inkplate_dashboard/statics/fonts/roboto-latin-300-normal.woff deleted file mode 100644 index e58cf24..0000000 Binary files a/inkplate_dashboard/statics/fonts/roboto-latin-300-normal.woff and /dev/null differ diff --git a/inkplate_dashboard/statics/fonts/roboto-latin-300-normal.woff2 b/inkplate_dashboard/statics/fonts/roboto-latin-300-normal.woff2 deleted file mode 100644 index 72226f5..0000000 Binary files a/inkplate_dashboard/statics/fonts/roboto-latin-300-normal.woff2 and /dev/null differ diff --git a/inkplate_dashboard/statics/fonts/roboto-latin-400-normal.woff b/inkplate_dashboard/statics/fonts/roboto-latin-400-normal.woff deleted file mode 100644 index 4333e5a..0000000 Binary files a/inkplate_dashboard/statics/fonts/roboto-latin-400-normal.woff and /dev/null differ diff --git a/inkplate_dashboard/statics/fonts/roboto-latin-400-normal.woff2 b/inkplate_dashboard/statics/fonts/roboto-latin-400-normal.woff2 deleted file mode 100644 index 2d7b215..0000000 Binary files a/inkplate_dashboard/statics/fonts/roboto-latin-400-normal.woff2 and /dev/null differ diff --git a/inkplate_dashboard/statics/fonts/roboto-latin-500-normal.woff b/inkplate_dashboard/statics/fonts/roboto-latin-500-normal.woff deleted file mode 100644 index cae9ed1..0000000 Binary files a/inkplate_dashboard/statics/fonts/roboto-latin-500-normal.woff and /dev/null differ diff --git a/inkplate_dashboard/statics/fonts/roboto-latin-500-normal.woff2 b/inkplate_dashboard/statics/fonts/roboto-latin-500-normal.woff2 deleted file mode 100644 index 88c1773..0000000 Binary files a/inkplate_dashboard/statics/fonts/roboto-latin-500-normal.woff2 and /dev/null differ diff --git a/inkplate_dashboard/statics/js/textFit.min.js b/inkplate_dashboard/statics/js/textFit.min.js new file mode 100644 index 0000000..d2af16d --- /dev/null +++ b/inkplate_dashboard/statics/js/textFit.min.js @@ -0,0 +1 @@ +(function(root,factory){"use strict";if(typeof define==="function"&&define.amd){define([],factory)}else if(typeof exports==="object"){module.exports=factory()}else{root.textFit=factory()}})(typeof global==="object"?global:this,function(){"use strict";var defaultSettings={alignVert:false,alignHoriz:false,multiLine:false,detectMultiLine:true,minFontSize:6,maxFontSize:80,reProcess:true,widthOnly:false,alignVertWithFlexbox:false};return function textFit(els,options){if(!options)options={};var settings={};for(var key in defaultSettings){if(options.hasOwnProperty(key)){settings[key]=options[key]}else{settings[key]=defaultSettings[key]}}if(typeof els.toArray==="function"){els=els.toArray()}var elType=Object.prototype.toString.call(els);if(elType!=="[object Array]"&&elType!=="[object NodeList]"&&elType!=="[object HTMLCollection]"){els=[els]}for(var i=0;i=parseInt(window.getComputedStyle(innerSpan)["font-size"],10)*2){multiLine=true}if(!multiLine){el.style["white-space"]="nowrap"}low=settings.minFontSize;high=settings.maxFontSize;var size=low;while(low<=high){mid=high+low>>1;innerSpan.style.fontSize=mid+"px";if(innerSpan.scrollWidth<=originalWidth&&(settings.widthOnly||innerSpan.scrollHeight<=originalHeight)){size=mid;low=mid+1}else{high=mid-1}}if(innerSpan.style.fontSize!=size+"px")innerSpan.style.fontSize=size+"px";if(settings.alignVert){addStyleSheet();var height=innerSpan.scrollHeight;if(window.getComputedStyle(el)["position"]==="static"){el.style["position"]="relative"}if(!hasClass(innerSpan,"textFitAlignVert")){innerSpan.className=innerSpan.className+" textFitAlignVert"}innerSpan.style["height"]=height+"px";if(settings.alignVertWithFlexbox&&!hasClass(el,"textFitAlignVertFlex")){el.className=el.className+" textFitAlignVertFlex"}}}function innerHeight(el){var style=window.getComputedStyle(el,null);return el.clientHeight-parseInt(style.getPropertyValue("padding-top"),10)-parseInt(style.getPropertyValue("padding-bottom"),10)}function innerWidth(el){var style=window.getComputedStyle(el,null);return el.clientWidth-parseInt(style.getPropertyValue("padding-left"),10)-parseInt(style.getPropertyValue("padding-right"),10)}function isElement(o){return typeof HTMLElement==="object"?o instanceof HTMLElement:o&&typeof o==="object"&&o!==null&&o.nodeType===1&&typeof o.nodeName==="string"}function hasClass(element,cls){return(" "+element.className+" ").indexOf(" "+cls+" ")>-1}function addStyleSheet(){if(document.getElementById("textFitStyleSheet"))return;var style=[".textFitAlignVert{","position: absolute;","top: 0; right: 0; bottom: 0; left: 0;","margin: auto;","display: flex;","justify-content: center;","flex-direction: column;","}",".textFitAlignVertFlex{","display: flex;","}",".textFitAlignVertFlex .textFitAlignVert{","position: static;","}"].join("");var css=document.createElement("style");css.type="text/css";css.id="textFitStyleSheet";css.innerHTML=style;document.body.appendChild(css)}}); diff --git a/inkplate_dashboard/styles/styles.scss b/inkplate_dashboard/styles/styles.scss index 20dd13d..17fb041 100644 --- a/inkplate_dashboard/styles/styles.scss +++ b/inkplate_dashboard/styles/styles.scss @@ -18,21 +18,16 @@ @font-face { font-family: "Roboto Condensed"; font-style: normal; - font-display: swap; + font-display: block; font-weight: 400; src: url(./fonts/roboto-condensed-latin-400-normal.woff2) format("woff2"), url(./fonts/roboto-condensed-latin-400-normal.woff) format("woff"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } -body { - margin: 0; -} - // Font configuration -$font-size-default: 25px; -$font-size-large: 30px; +$font-size-default: 30px; $font-size-huge: 40px; -$font-size-current-temp: 60px; +$font-size-current-temp: 70px; $font-family-serif: "Noto Serif", serif; $font-family-default: "Open Sans", serif; $font-family-temp: "Roboto Condensed", sans-serif; @@ -42,173 +37,136 @@ $display-background: #000; $display-padding: 5px; $display-text-color: #fff; -// Configuration for the inner background -$content-background: rgb(200, 200, 200); -$content-border-radius: 12px; +$spacing: 30px; -// Configuration for widgets -$widget-background: #fff; -$widget-border: 1px solid rgb(130, 130, 130); -$widget-border-radius: 8px; -$widget-spacing: 14px; -$widget-inner-padding: 14px; // Layout +body { + margin: 0; +} + #display { - background: $display-background; - padding: $display-padding; - font-size: $font-size-default; - font-family: $font-family-default; + width: 825px; + height: 1200px; box-sizing: border-box; - display: flex; - flex-direction: column; overflow: hidden; font-weight: 300; + font-size: $font-size-default; + border-bottom: 1px solid #000; + border-right: 1px solid #000; + font-family: $font-family-default; } -#content { - background: $content-background; - padding: $widget-spacing/2; - border-radius: $content-border-radius; - flex-grow: 1; - display: flex; - flex-direction: row; -} - -.column { - flex: 2; - height: 100%; - display: flex; - flex-direction: column; -} - -.widget { - background: $widget-background; - border: $widget-border; - border-radius: $widget-border-radius; - margin: $widget-spacing/2; - overflow: hidden; -} +h1 { + height: 100px; + padding: $spacing; + margin: 0; + box-sizing: border-box; -// Date -#header { font-family: $font-family-serif; - font-size: $font-size-large; - color: $display-text-color; + font-size: $font-size-huge; font-weight: 700; - padding: $widget-spacing/2 0 $widget-spacing/2 $widget-inner-padding+$widget-spacing; -} + line-height: 100px - 2*$spacing; -/* News Widget */ -.widget-headlines { - @extend .widget; - flex: 1 0 0%; - display: flex; - flex-direction: column; - justify-content: center; - - .content { - margin: $widget-inner-padding; - display: -webkit-box; - -webkit-line-clamp: 11; - -webkit-box-orient: vertical; - overflow: hidden; - - h1 { - font-family: $font-family-serif; - font-size: $font-size-default; - margin: 0 0 $widget-inner-padding 0; - flex-shrink: 0; - } - } + color: #fff; + background-color: #000; } -/* News Widget */ -.widget-large-headline { - @extend .widget; - flex: 3; - display: flex; - flex-direction: column; - - .image { - flex-grow: 1; - background-size: cover; - background-position: center center; - } - - .content { - margin: $widget-inner-padding; - font-size: $font-size-large; - - display: -webkit-box; - -webkit-line-clamp: 10; - -webkit-box-orient: vertical; - overflow: hidden; - - h1 { - margin: 0 0 $widget-inner-padding 0; - font-family: $font-family-serif; - font-weight: 700; - font-size: $font-size-huge; - } - } -} +.widget-headlines { + height: 900px; + /* will be overridden by textfit, all the following sizes are relative */ + padding: $spacing $spacing 0 $spacing; + box-sizing: border-box; -.widget-large-headline-smaller { - .content { - font-size: $font-size-default; + line-height: 1.25; - h1 { - font-size: $font-size-large; + .widget-headlines-inner { /* We need this because textFit messes with flexbox */ + height: 900px - $spacing; + display: flex; + flex-direction: column; + justify-content: space-evenly; + box-sizing: border-box; + + article { + padding-bottom: $spacing; + + h2 { + margin: 0 0 .4em 0; + font-family: $font-family-serif; + font-size: 1.3em; + font-weight: 700; + } + + .widget-headlines-content { + display: flex; + flex-direction: row; + text-align: justify; + + .widget-headlines-illustration { + margin: .125em 1em .125em 0; + border: 1px solid #000; + background-size: cover; + background-position: center center; + width: 250px; + flex-grow: 0; + flex-shrink: 0; + } + } } } } /* Weather */ .widget-weather { - @extend .widget; + height: 200px; + overflow: hidden; + + box-sizing: border-box; + padding: 0 $spacing $spacing $spacing; + display: flex; - flex-direction: column; + flex-direction: row; + justify-content: space-between; + align-items: center; + + background-color: #000; + color: #fff; .widget-weather-current { display: flex; + align-items: center; - .widget-weather-current-icon { - height: 110px; - width: 110px; - filter: brightness(80%); /* Not very readable on inkplate otherwise */ - margin: $widget-inner-padding; + img { + height: 140px; + width: 140px; + margin-right: $spacing; } - .widget-weather-current-details { - flex: 1; - margin: $widget-inner-padding; + svg { + width: 24px; + height: 24px; } .widget-weather-current-temperature { font-family: $font-family-temp; font-size: $font-size-current-temp; - font-weight: 300; + line-height: $font-size-current-temp; + font-weight: 400; } } - .widget-weather-table { - border-top: $widget-border; - padding: $widget-inner-padding 0; - display: flex; - justify-content: space-evenly; + .widget-weather-next { text-align: center; img { margin-top: 5px; - width: 60px; - height: 60px; - filter: brightness(80%); /* Not very readable on inkplate otherwise */ + width: 75px; + height: 75px; } .widget-weather-table-temp { font-family: $font-family-temp; - font-weight: 300; + font-weight: 400; } } } \ No newline at end of file diff --git a/inkplate_dashboard/templates/display.html b/inkplate_dashboard/templates/display.html index 7bdd942..3a25f27 100644 --- a/inkplate_dashboard/templates/display.html +++ b/inkplate_dashboard/templates/display.html @@ -1,23 +1,23 @@ {% from 'widget-headlines.html' import widget_headlines %} -{% from 'widget-large-headline.html' import widget_large_headline %} {% from 'widget-weather.html' import widget_weather %} - e-paper display + Inkplate 10 display + -
- -
- {{ widget_large_headline(data.headlines[0]) }} -
- {{ widget_weather(data.weather) }} - {{ widget_headlines(data.headlines[1:]) }} -
-
+
+

{{ data.date }}

+ {{ widget_weather(data.weather) }} + {{ widget_headlines(data.headlines) }}
+ \ No newline at end of file diff --git a/inkplate_dashboard/templates/widget-headlines.html b/inkplate_dashboard/templates/widget-headlines.html index 8556153..fa32ed5 100644 --- a/inkplate_dashboard/templates/widget-headlines.html +++ b/inkplate_dashboard/templates/widget-headlines.html @@ -1,13 +1,20 @@ {% macro widget_headlines(headlines) %}
-
-

{{ headlines[1].title }}

- {{ headlines[1].description }} +
+
+

{{ headlines[0].title }}

+
+
+
{{ headlines[0].description }}
+
+
+
+

{{ headlines[1].title }}

+
+
+
{{ headlines[1].description }}
+
+
-
{% endmacro %} \ No newline at end of file diff --git a/inkplate_dashboard/templates/widget-large-headline.html b/inkplate_dashboard/templates/widget-large-headline.html deleted file mode 100644 index 49ba4ed..0000000 --- a/inkplate_dashboard/templates/widget-large-headline.html +++ /dev/null @@ -1,9 +0,0 @@ -{% macro widget_large_headline(headline) %} -
-
-
-

{{ headline.title }}

-
{{ headline.description }}
-
-
-{% endmacro %} \ No newline at end of file diff --git a/inkplate_dashboard/templates/widget-weather.html b/inkplate_dashboard/templates/widget-weather.html index 697b0b3..c774682 100644 --- a/inkplate_dashboard/templates/widget-weather.html +++ b/inkplate_dashboard/templates/widget-weather.html @@ -1,28 +1,29 @@ {% macro widget_weather(weather) %}
- -
+ +
{{ weather[0].temperature }}
-
- +
+ - - {{ weather[0].wind }} - {% if weather[0].rain %}  + {{ weather[0].wind }} +
+ {% if weather[0].rain %} +
+ - {{ weather[0].rain }}{% endif %} + {{ weather[0].rain }}
+ {% endif %}
-
-{% for observation in weather[1:5] %} -
-
{{ observation.date_short_locale }}
- -
{{ observation.temperature }}
-
-{% endfor %} +{% for observation in weather[1:4] %} +
+
{{ observation.date_short_locale }}
+ +
{{ observation.temperature }}
+{% endfor %}
{% endmacro %} \ No newline at end of file