In Home Display
Smart Meter "In Home Displays" are rubbish!
There's no escaping it. The concept is good, have a nice little display which is showing your current, daily, weekly, monthly consumption for Gas and Electricity. For that part ok they might just about do the job. However, for monthly you can't set your billing date, so monthly for the meter doesn't match your billing cycle so it's wrong. The displays also try to tell you how much you've spent on electricity and if you are on a fixed tariff they "might" get it right. However the point of having a smart meter is to also have a smart tariff, well that's where it all goes wrong. The smart meter and display can't handle half hourly pricing changes, or even half day changes (Economy 7 and a few others being the exception).
Those of us who are using Home Asisstant to help with monitoring and automation have long since assigned the IHD to the back cupboard to be ignored and can monitor via phone or laptop. But for those who arn't fixed in front of the computer or phone and just want to glance and see what's happening a little display in the kitchen would be nice and helpful.
If you have an old tablet or phone, you could stick it to the wall and use that. I however wanted something simple to use and did just want I wanted.
Building my own
With Home Assistant (HA) already setup I have access to all the data necessary to make an effective display. There are lots of ways to build a display but for me the quickest way to get sucess was to use ESPHome Development integration for HA.
I have a number of ESP32 style boards they are very cheap, small and easy to work with, one of these will provide the communication and control of the display. For the screen I'm using a Nextion 2.8" touch screen.
Nextion screens are great to work with and not expensive. Install their own development software (Windows only) and add buttons, graphics, etc. Save to an SD card and install on the screen.
The communication between the screen and the ESP32 is serial. Sending data to be displayed or actions to be done on the screen, run animations, change screen (page), etc.
For me the main feature is the GUI. This is what I'm going to have to look at and interact with, so it's important to get that right. To do this I opened a graphic package (OmniGraffle) and start to draw the UI. Create a box the size of the screen and add elements. Once I'm happy with the view I can then slice up the graphics into background objects (Those which don't change), and forgound objects.
For the Nextion screen all pages (screens) are seperate, you can't have a footer or header which is repeated. In this case the buttons at the bottom are part of the background for each page. Just hi-lighted to represent the current page.
Inside the Nextion HMI (Human Machine Interface) dev environment I can set the background graphic and then overlay the fields to present the data. I tend to make all the fields global and use the same names for fields which span pages, eg time, grid and battery status icons.
ESP32 code as YML script from ESPHome
esphome:
name: "house-monitor-display-kitchen"
friendly_name: Flow Display
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "Secret Key goes here"
actions:
- action: update_nextion_firmware
then:
- lambda: 'id(nexton1)-> upload_tft();'
ota:
platform: esphome
password: "secret password here"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
fast_connect: True
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Display Fallback Hotspot"
password: "AP Password"
# captive_portal:
web_server:
port: 80
uart:
id: uart_2
rx_pin: GPIO16
tx_pin: GPIO17
baud_rate: 115200
# time
time:
- platform: homeassistant
id: esptime
text_sensor:
- platform: template
id: "current_time"
lambda: return id(esptime).now().strftime("%Y-%m-%d %H:%M");
update_interval: 10s
- platform: homeassistant
entity_id: input_select.left_zappi
id: "left_zappi_connected"
name: "Left Zappi Connected"
internal: False
- platform: homeassistant
entity_id: input_select.right_zappi
id: "right_zappi_connected"
name: "Right Zappi Connected"
- platform: homeassistant
entity_id: sensor.myenergi_left_zappi_status
id: "left_zappi_status"
name: "Left Zappi Status"
internal: False
- platform: homeassistant
entity_id: sensor.myenergi_right_zappi_status
id: "right_zappi_status"
name: "Right Zappi Status"
binary_sensor:
- platform: homeassistant
entity_id: binary_sensor.octopus_energy_a_intelligent_dispatching
id: "dispatching"
name: "Intel Dispatching"
internal: False
sensor:
- platform: homeassistant
entity_id: sensor.octopus_energy_cost_tracker_grid_import_costing_peak
id: cost_peak
- platform: homeassistant
entity_id: sensor.octopus_energy_cost_tracker_grid_import_costing_off_peak
id: cost_offpeak
- platform: homeassistant
entity_id: sensor.octopus_energy_cost_tracker_grid_export_earned
id: cost_export
name: "cost Export"
internal: False
- platform: homeassistant
entity_id: sensor.octopus_energy_electricity_current_standing_charge
id: cost_standing
- platform: template
id: totalPrice
update_interval: 60s
lambda: return ( id(cost_peak).state + id(cost_offpeak).state - id(cost_export).state + id(cost_standing).state);
- platform: homeassistant
entity_id: sensor.myenergi_left_zappi_charge_added_session
id: "left_zappi_session"
name: "Left Zappi session"
- platform: homeassistant
entity_id: sensor.myenergi_right_zappi_charge_added_session
id: "right_zappi_session"
name: "Right Zappi session"
- platform: homeassistant
entity_id: sensor.id_3_battery_level
id: "id_3_level"
name: "ID.4 Level"
- platform: homeassistant
entity_id: sensor.id_4_battery_level
id: "id_4_level"
name: "ID.4 Level"
- platform: homeassistant
entity_id: sensor.car_car_evinfo_battery_stateofcharge
id: "jeep_level"
name: "Jeep Level"
# Power flow information
# GridRate
- platform: homeassistant
name: "Grid Power"
entity_id: sensor.myenergi_home_power_grid
unit_of_measurement: w
accuracy_decimals: 0
id: grid_power
internal: false
# BatteryRate
- platform: homeassistant
name: "Battery Power"
entity_id: sensor.powerwall_battery_now_2
unit_of_measurement: kW
id: battery_power
internal: false
# SolarRate
- platform: homeassistant
name: "Solar Power"
entity_id: sensor.myenergi_left_zappi_power_ct_generation
id: solar_power
unit_of_measurement: w
internal: false
# Grid import Today
- platform: homeassistant
entity_id: sensor.myenergi_home_grid_import_today
id: grid_import
unit_of_measurement: kWh
internal: false
- platform: homeassistant
entity_id: sensor.myenergi_home_grid_export_today
id: grid_export
unit_of_measurement: kWh
internal: false
- platform: homeassistant
entity_id: sensor.myenergi_home_generated_today
id: solar_generated
unit_of_measurement: kWh
internal: false
- platform: template
id: chartGrid
name: "chart Grid"
lambda: return ((id(grid_power).state/1000) + 24) * (122/48);
internal: false
- platform: template
id: chartBattery
name: "chart Battery"
lambda: return ((id(battery_power).state + 10) /20) * 122;
internal: false
- platform: template
id: chartSolar
name: "chart Solar"
lambda: return (id(solar_power).state / 4000) * 122;
internal: False
# Battery page
- platform: homeassistant
entity_id: sensor.powerwall_charge_2
name: "Battery Percent"
id: battery_percent
- platform: homeassistant
entity_id: sensor.tesla_usable_battery_remaining
id: battery_remaining
name: "Battery Remaining"
display:
- platform: nextion
id: nexton1
uart_id: uart_2
tft_url: "http://ha.local:8123/local/tft/HADisplaySmall.tft"
start_up_page: 1
lambda: |-
it.set_component_text_printf("Time", "%s", id(current_time).state.c_str());
if (id(dispatching).state) {
it.set_component_picture("gridState", 6);
} else {
it.set_component_picture("gridState", 7);
}
if (id(battery_power).state > 0 ) {
it.set_component_picture("bttCD", 11);
} else {
it.set_component_picture("bttCD", 10);
}
it.set_component_value("totalPrice", id(totalPrice).state*100);
it.set_component_value("import", id(grid_import).state);
it.set_component_value("export", id(grid_export).state*10);
it.add_waveform_data(6,0,id(chartGrid).state);
it.add_waveform_data(6,1,id(chartSolar).state);
it.add_waveform_data(6,2,id(chartBattery).state);
it.set_component_value("bttpercent", id(battery_percent).state);
it.set_component_value("bttgraphic", id(battery_percent).state);
it.set_component_value("bttPWR", id(battery_power).state*1000);
it.set_component_value("bttStored", id(battery_remaining).state);
it.set_component_value("solarGuage", (id(solar_power).state/4000) * 180);
it.set_component_value("solarGTotal", (id(solar_generated).state/30) * 180);
it.set_component_value("solarNow", id(solar_power).state);
it.set_component_value("solar", id(solar_generated).state*10);
it.set_component_value("importCost", (id(cost_peak).state + id(cost_offpeak).state)*100);
it.set_component_value("ExportCost", id(cost_export).state * 100);
it.set_component_value("gridNow", id(grid_power).state);
it.set_component_value("id_3_level", id(id_3_level).state);
it.set_component_value("id_4_level", id(id_4_level).state);
it.set_component_value("jeep_level", id(jeep_level).state);
it.set_component_value("leftSession", id(left_zappi_session).state);
it.set_component_value("rightSession", id(right_zappi_session).state);
it.set_component_text_printf("leftStatus", "%s", id(left_zappi_status).state.c_str());
it.set_component_text_printf("rightStatus", "%s", id(right_zappi_status).state.c_str());
it.set_component_text_printf("leftConnected", "%s", id(left_zappi_connected).state.c_str());
it.set_component_text_printf("rightConnected", "%s", id(right_zappi_connected).state.c_str());
The Serial connection between Nextion and the ESP32 is by default 9600, however this is a little slow so I increase this to 115200. Once you have programmed the Nextion screen and updated it's speed settings, you can push screen updates via Home Assistant, this makes for much quicker development times as you don't need to write to SD card, reboot Nextion, remove SD card and reboot Nextion again.
Plastic case
3D printers are great! Without too much effort I was able to design a nice little plastic box to put the screen and ESP32 inside just exposing the USB power connector.
There are lots of different ways of designing 3D objects, many people use TinkerCAD, however for me I like the simply ways or defining it as code, so I use OpenSCAD.