Home Assistant Weather 🏠
Posted February 28, 2023 by ‐ 10 min read
Setting up Paperd.Ink to show weather and date information.
Home Assistant is currently only supported on Paperd.Ink Classic.
This example was inspired from Sainz work →.
Setting up Home Assistant
Follow the instructions provided on our documentation page to configure and install Home Assistant here →
Install and Configure AccuWeather Integration
- Create an account on the AccuWeather website →.
- Login to your account and go to the
MyApps
tab to create a new app. - After creating the app, note down your API key from the app details.
- In Home Assistant, go to
Settings > Devices & Services
and click onAdd Integration
. - Search and install the
AccuWeather
add-on, and input your API key. Ensure that the Lat and Lon values are correct for your location and name it asHome
. ClickFinish
on the success pop-up. - You should now see an AccuWeather card. Click on
Configure
, enable theWeather Forecast
button, and clickSubmit
. ClickFinish
on the success pop-up. - The weather information should now be available on your Home Assistant dashboard.
Add Sun and Moon integration
- In Home Assistant, go to
Settings > Devices & Services
. - Click on
+ Add integration
button on the bottom right. - Search for
Sun
andMoon
and add them.
Installing File Editor
- In Home Assistant, go to
Settings > Add-Ons
and click onAdd-On Store
. - Search for and install the
File Editor
add-on. - Enable all the necessary options, such as
Start on Boot
,Watchdog
,Auto Update
, andShow in Sidebar
.
Add new sensors for weather parsing
- Click on the
File Editor
icon in the Home Assistant sidebar. - Click on the
Folder
icon located on the top left corner of the window. - Ensure that you are in the
config/
directory and create a new file namedsensor.yaml
. - Copy and paste the following code into the newly created
sensor.yaml
file and save the file.
- platform: template
sensors:
weather_now:
friendly_name: "Weather Now"
value_template: >-
{{ states.weather.home.state }};{{ states.weather.home.attributes.temperature | round(0) }}°C
weather_fivedays:
friendly_name: "Five day weather"
value_template: >-
{% set days = {'Mon':'Mon','Tue':'Tue','Wed':'Wed','Thu':'Thu','Fri':'Fri','Sat':'Sat','Sun':'Sun'} %}
{% for state in states.weather.home.attributes.forecast[0:3] -%}
{{ days[as_timestamp(state.datetime)| timestamp_custom("%a")] }};{{state.templow}}/{{ state.temperature }}°C;{{ state.condition }}#
{%- endfor %}
it_days:
friendly_name: "Translated Days"
value_template: >-
{% set daysIt = ["Mon", "Tue","Wed","Thu","Fri","Suut","Sun"] %}
{{ daysIt[now().weekday()] }}
it_months:
friendly_name: "Translated Months"
value_template: >-
{% set monthsIt = ["Jan", "Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"] %}
{{ monthsIt[now().month - 1] }}
it_weather:
friendly_name: "Weather + Translated Days"
value_template: >-
{{ states.sensor.it_days.state }};{{ states.sensor.weather_now.state }};{{ states.sensor.it_months.state }};
- Open the configuration.yaml file and add the following line.
sensor: !include sensor.yaml
Add fonts
- Download Material WebIcons → and OpenSans →, extract the OpenSans zip file.
- Open
File Editor
from HA sidebar, navigate toconfig/esphome
directory by clicking on the folder icon and create a new directory namedfonts
. - Upload the
OpenSans-Bold.ttf
andmaterialdesignicons-webfont.ttf
files to thefonts
directory.
Configure Paperd.Ink
- Go to
ESPHome
in the HA sidebar. - Click on
Edit
under the Paperd.Ink device. - Note your API encryption key, OTA password, and Wi-Fi AP password.
- Please update the name, API encryption key, OTA password (remove the password line if there was no password), Wi-Fi AP password, timezone, latitude, and longitude as needed in the code below and replace it in the ESPHome code. You can also change the display
update_interval
, which is set to 1 hour (3600 secs) in the config below. - Click Save and Install.
- Wait for the installation to complete (about a min), then check the Paperd.Ink display to confirm that it’s working properly.
- It takes about 90 seconds to refresh with all the data.
esphome:
name: *****
on_boot:
priority: -10
then:
- delay: 90s
- component.update: epaperdisplay
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "*************************"
ota:
password: "********************"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Paperdink Fallback Hotspot"
password: "*********"
captive_portal:
time:
- platform: homeassistant
id: ha_time
timezone: America/Chicago
sun:
latitude: 20.1
longitude: 22.5
# Paperdink display config
output:
- platform: gpio
pin:
number: 12
id: enable_epd
font:
- file: 'fonts/OpenSans-Bold.ttf'
id: roboto_med_17
size: 17
- file: 'fonts/OpenSans-Bold.ttf'
id: roboto_reg_12
size: 12
- file: 'fonts/OpenSans-Bold.ttf'
id: roboto_reg_15
size: 15
- file: 'fonts/OpenSans-Bold.ttf'
id: roboto_reg_20
size: 20
glyphs:
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e',
'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z', 'å', 'ä', 'ö', 'à', 'è', 'ì', 'ò',
'ù', ':', '/']
- file: 'fonts/OpenSans-Bold.ttf'
id: roboto_bold_40
size: 40
glyphs:
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/','º','µ','³']
- file: 'fonts/OpenSans-Bold.ttf'
id: roboto_bold_80
size: 80
glyphs:
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/','º','µ','³']
- file: 'fonts/materialdesignicons-webfont.ttf'
id: weather_font_80
size: 80
glyphs: [
# Weather
"\U000F0590", # mdi-weather-cloudy
"\U000F0594", # mdi-weather-night
"\U000F0591", # mdi-weather-fog
"\U000F0592", # mdi-weather-hail
"\U000F0593", # mdi-weather-lightning
"\U000F067E", # mdi-weather-lightning-rainy
"\U000F0595", # mdi-weather-partly-cloudy
"\U000F0596", # mdi-weather-pouring
"\U000F0597", # mdi-weather-rainy
"\U000F0598", # mdi-weather-snowy
"\U000F067F", # mdi-weather-snowy-rainy
"\U000F0599", # mdi-weather-sunny
"\U000F059B", # mdi-weather-sunset-down
"\U000F059C", # mdi-weather-sunset-up
"\U000F059D", # mdi-weather-windy
"\U000F059E", # mdi-weather-windy-variant
]
- file: 'fonts/materialdesignicons-webfont.ttf'
id: weather_font_40
size: 40
glyphs: [
# Sun
"\U000F059A", # mdi-weather-sunset
# Moon
"\U000F0F64", # mdi-moon-new
"\U000F0F67", # mdi-moon-waxing-crescent
"\U000F0F61", # mdi-moon-first-quarter
"\U000F0F68", # mdi-moon-waxing-gibbous
"\U000F0F62", # mdi-moon-full
"\U000F0F66", # mdi-moon-waning-gibbous
"\U000F0F63", # mdi-moon-last-quarter
"\U000F0F65", # mdi-moon-waning-crescent
# Weather
"\U000F0590", # mdi-weather-cloudy
"\U000F0594", # mdi-weather-night
"\U000F0591", # mdi-weather-fog
"\U000F0592", # mdi-weather-hail
"\U000F0593", # mdi-weather-lightning
"\U000F067E", # mdi-weather-lightning-rainy
"\U000F0595", # mdi-weather-partly-cloudy
"\U000F0596", # mdi-weather-pouring
"\U000F0597", # mdi-weather-rainy
"\U000F0598", # mdi-weather-snowy
"\U000F067F", # mdi-weather-snowy-rainy
"\U000F0599", # mdi-weather-sunny
"\U000F059B", # mdi-weather-sunset-down
"\U000F059C", # mdi-weather-sunset-up
"\U000F059D", # mdi-weather-windy
"\U000F059E", # mdi-weather-windy-variant
]
- file: 'fonts/materialdesignicons-webfont.ttf'
id: weather_font_15
size: 15
glyphs: [
# Wifi
"\U000F092F", # mdi-wifi-strength-outline
"\U000F091F", # mdi-wifi-strength-1
"\U000F0922", # mdi-wifi-strength-2
"\U000F0925", # mdi-wifi-strength-3
"\U000F0928", # mdi-wifi-strength-4
]
- file: 'fonts/materialdesignicons-webfont.ttf'
id: weather_font_30
size: 30
glyphs: [
# Sun
"\U000F059A", # mdi-weather-sunset
# Moon
"\U000F0F64", # mdi-moon-new
"\U000F0F67", # mdi-moon-waxing-crescent
"\U000F0F61", # mdi-moon-first-quarter
"\U000F0F68", # mdi-moon-waxing-gibbous
"\U000F0F62", # mdi-moon-full
"\U000F0F66", # mdi-moon-waning-gibbous
"\U000F0F63", # mdi-moon-last-quarter
"\U000F0F65", # mdi-moon-waning-crescent
# Rooms
"\U000F09A0", # mdi-shower
"\U000F09A1", # mdi-shower-head
"\U000F04B9", # mdi-sofa
"\U000F0C99", # mdi-microwave
"\U000F0FD1", # mdi-bed-king-outline
"\U000F106E", # mdi-bed-single-outline
"\U000F0322", # mdi-laptop
"\U000F07F4", # mdi-television-classic
]
sensor:
- platform: homeassistant
entity_id: sensor.moon_phase
id: moon_status
internal: true
- platform: wifi_signal
name: "WiFi Signal Sensor"
id: wifisignal
update_interval: 100000s
text_sensor:
- platform: homeassistant
id: moon_icon
entity_id: sensor.moon_phase
- platform: sun
type: sunrise
id: sunrise_hour
- platform: sun
type: sunset
id: sunset_hour
- platform: homeassistant
entity_id: sensor.it_weather
id: weathertoday
- platform: homeassistant
entity_id: sensor.weather_fivedays
id: fweather
spi:
clk_pin: GPIO18
mosi_pin: GPIO23
miso_pin: GPIO19
display:
- platform: waveshare_epaper
cs_pin: GPIO22
dc_pin: GPIO15
busy_pin: GPIO34
reset_pin: GPIO13
model: 4.20in
update_interval: 3600s
id: epaperdisplay
lambda: |-
/* TURN ON EPD */
id(enable_epd).turn_on();
/* SENSOR VARIABLES */
std::string moon_info = id(moon_icon).state;
std::string weather_days = id(fweather).state;
std::string weather_today = id(weathertoday).state;
std::string sunrise = id(sunrise_hour).state;
std::string sunset = id(sunset_hour).state;
std::map<std::string, std::string> weather_icon_state {
{"cloudy", "\U000F0590"},
{"clear-day", "\U000F0599"},
{"clear-night", "\U000F0594"},
{"fog", "\U000F0591"},
{"hail", "\U000F0592"},
{"lightning", "\U000F0593"},
{"lightning-rainy", "\U000F067E"},
{"partlycloudy", "\U000F0595"},
{"pouring", "\U000F0596"},
{"rainy", "\U000F0597"},
{"snowy", "\U000F0598"},
{"snowy-rainy", "\U000F067F"},
{"sunny", "\U000F0599"},
{"sunset-down", "\U000F059B"},
{"sunset-up", "\U000F059C"},
{"windy", "\U000F059D"},
{"windy-variant", "\U000F059E"}
};
std::map<std::string, std::string> moon_state_icon {
{"new_moon", "\U000F0F64"},
{"waxing_crescent", "\U000F0F67"},
{"first_quarter", "\U000F0F61"},
{"waxing_gibbous", "\U000F0F68"},
{"full_moon", "\U000F0F62"},
{"waning_gibbous", "\U000F0F66"},
{"last_quarter", "\U000F0F63"},
{"waning_crescent", "\U000F0F65"}
};
std::map<std::string, std::string> moon_state_label {
{"new_moon", "New Moon"},
{"waxing_crescent", "Waxing Crescent"},
{"first_quarter", "First Quarter"},
{"waxing_gibbous", "Waxing Gibbous"},
{"full_moon", "Full Moon"},
{"waning_gibbous", "Waning Gibbous"},
{"last_quarter", "Last Quarter"},
{"waning_crescent", "Waning Crescent"}
};
char delimm = ';';
/* MAIN RECTANGLE */
it.rectangle(5, 5, 390, 295);
/* CALENDAR RECT */
it.filled_rectangle(10, 10, 120, 30);
it.rectangle(10, 40, 120, 90);
it.filled_rectangle(10, 130, 120, 30);
it.rectangle(10, 160, 120, 135);
/* DX RECT */
it.rectangle(135, 10, 255, 265);
/* Todays Date */
uint16_t todays_date_base_x = 262;
uint16_t todays_date_base_y = 80;
// Day of the week
it.strftime(todays_date_base_x, todays_date_base_y, id(roboto_bold_80), TextAlign::BASELINE_CENTER, "%a", id(ha_time).now());
// Day of the month
it.strftime(todays_date_base_x, todays_date_base_y + 50 , id(roboto_bold_40), TextAlign::BASELINE_CENTER, "%d %b", id(ha_time).now());
/* WEATHER TODAY */
std::size_t currentWeatherToday, previousWeatherToday = 0;
currentWeatherToday = weather_today.find(delimm);
ESP_LOGD("WEATHER TODAY", "%s", weather_today.c_str());
/* Loop for the single room */
for (int i=0; i<4; i++){
if (i == 0) {
// Print "Today"
it.printf(70, 30, id(roboto_reg_20), COLOR_OFF, TextAlign::BASELINE_CENTER, "Today");
} else if (i == 1) {
// Weather Icon
it.printf(70, 115, id(weather_font_80), TextAlign::BASELINE_CENTER, "%s", weather_icon_state[weather_today.substr(previousWeatherToday, currentWeatherToday - previousWeatherToday).c_str()].c_str());
} else if (i == 2){
// Current Temp
it.filled_circle(98, 63, 20, COLOR_OFF);
it.circle(98, 63, 20);
it.printf(98, 63, id(roboto_reg_15), TextAlign::CENTER, "%s", weather_today.substr(previousWeatherToday, currentWeatherToday - previousWeatherToday).c_str());
} else if (i == 3){
// Print "Next 3 days"
it.printf(70, 150, id(roboto_reg_20), COLOR_OFF, TextAlign::BASELINE_CENTER, "Next 3 days");
}
previousWeatherToday = currentWeatherToday + 1;
currentWeatherToday = weather_today.find(delimm, previousWeatherToday);
}
/* WEATHER FORECAST */
ESP_LOGD("WEATHER", "%s", weather_days.c_str());
std::size_t currentWeather, previousWeather = 0;
char delimWeather = '#';
currentWeather = weather_days.find(delimWeather);
int wx = 17; // start position x
int wy = 170; // start position y
/* Loop for the single string day */
for (int i=0; i<3; i++) {
std::string forecastInfo = weather_days.substr(previousWeather, currentWeather - previousWeather).c_str();
std::size_t currentInfo, previousInfo = 0;
currentInfo = forecastInfo.find(delimm);
/* Inner loop to get single information for each day */
for (int j=0; j<3; j++) {
if (j == 0) {
// Day (Mon/Tue...)
it.printf(wx + 42, wy + 2, id(roboto_med_17), "%s", forecastInfo.substr(previousInfo, currentInfo - previousInfo).c_str());
} else if (j == 1) {
// Temperature
it.printf(wx + 42, wy + 20, id(roboto_reg_12), "%s", forecastInfo.substr(previousInfo, currentInfo - previousInfo).c_str());
} else if (j == 2) {
// Weather Icon
it.printf(wx, wy, id(weather_font_40), "%s", weather_icon_state[forecastInfo.substr(previousInfo, currentInfo - previousInfo).c_str()].c_str() );
}
previousInfo = currentInfo + 1;
currentInfo = forecastInfo.find(delimm, previousInfo);
}
wy += 40; // move down 50 pixels and output next day
previousWeather = currentWeather + 1;
currentWeather = weather_days.find(delimWeather, previousWeather);
}
/* SUNRISE SUNSET */
uint16_t sunrise_sunset_base_x = 150;
uint16_t sunrise_sunset_base_y = 180;
ESP_LOGD("SUNRISE", "%s", sunrise.c_str());
ESP_LOGD("SUNSET", "%s", sunset.c_str());
it.printf(sunrise_sunset_base_x, sunrise_sunset_base_y, id(weather_font_40), TextAlign::BASELINE_LEFT, "%s", "\U000F059A");
if(id(sunrise_hour).has_state() && id(sunset_hour).has_state()) {
it.printf(sunrise_sunset_base_x + 45, sunrise_sunset_base_y - 10, id(roboto_reg_20), TextAlign::BASELINE_LEFT, "Sunrise: %s", sunrise.c_str());
it.printf(sunrise_sunset_base_x + 45, sunrise_sunset_base_y + 10, id(roboto_reg_20), TextAlign::BASELINE_LEFT, "Sunset : %s", sunset.c_str());
} else {
it.printf(sunrise_sunset_base_x + 45, sunrise_sunset_base_y - 10, id(roboto_reg_20), TextAlign::BASELINE_LEFT, "%s", "n/a");
it.printf(sunrise_sunset_base_x + 45, sunrise_sunset_base_y + 10, id(roboto_reg_20), TextAlign::BASELINE_LEFT, "%s", "n/a");
}
/* MOON */
uint16_t moon_base_x = 150;
uint16_t moon_base_y = 240;
if(id(moon_status).has_state()) {
ESP_LOGD("MOON", "%s", moon_info.c_str());
// Icon
it.printf(moon_base_x, moon_base_y, id(weather_font_40), TextAlign::BASELINE_LEFT, "%s", moon_state_icon[moon_info.c_str()].c_str());
// Label
it.printf(moon_base_x + 45, moon_base_y - 5, id(roboto_reg_20), TextAlign::BASELINE_LEFT, "%s", moon_state_label[moon_info.c_str()].c_str());
};
/* FOOTER */
it.strftime(355, 295 , id(roboto_reg_15), TextAlign::BASELINE_RIGHT , "Updated at: %d/%m/%Y %H:%M", id(ha_time).now());
/* WIFI SIGNAL */
if(id(wifisignal).has_state()) {
int wifi_x = 375;
int wifi_y = 295;
if (id(wifisignal).state >= -50) {
// Excellent
it.print(wifi_x, wifi_y, id(weather_font_15), TextAlign::BASELINE_CENTER, "\U000F0928");
ESP_LOGI("WiFi", "Exellent");
} else if (id(wifisignal).state >= -60) {
// Good
it.print(wifi_x, wifi_y, id(weather_font_15), TextAlign::BASELINE_CENTER, "\U000F0925");
ESP_LOGI("WiFi", "Good");
} else if (id(wifisignal).state >= -75) {
// Fair
it.print(wifi_x, wifi_y, id(weather_font_15), TextAlign::BASELINE_CENTER, "\U000F0922");
ESP_LOGI("WiFi", "Fair");
} else if (id(wifisignal).state >= -100) {
// Weak
it.print(wifi_x, wifi_y, id(weather_font_15), TextAlign::BASELINE_CENTER, "\U000F091F");
ESP_LOGI("WiFi", "Weak");
} else {
// Unlikely working signal
it.print(wifi_x, wifi_y, id(weather_font_15), TextAlign::BASELINE_CENTER, "\U000F092F");
ESP_LOGI("WiFi", "Unlikely");
}
}
id(enable_epd).turn_off();
We are currently experiencing a battery life of a day when using paperd.ink with Home Assistant, as the device maintains a constant WiFi connection. However, we are working on incorporating deep sleep functionality which will significantly extend the battery life.