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.