Widget for TP-Link Smart Plug (HS110) with Energy Monitoring

My solution, but looking for a better one!

At amazon.ca

The widgets displayed in my dashboard.

To get here you need to setup the HA switch.

As a switch we can see the attributes if we click on the switch but they are not accessible until we pull them out as sensors.

> switch.garage_miner	on	current_power_w: 883.94
    >                           total_energy_kwh: 175.246
    >                           voltage: 111.0
    >                           current_a: 7.97
    >                           today_energy_kwh: 8.079
    >                           friendly_name: Garage Miner

So in HA we need a new sensor: tpsensor.yaml

- platform: template
  name: Garage_Miner
  sensors:
    tp_link_amps:
      friendly_name: "Amps"
      value_template: '{{ states.switch.garage_miner.attributes["current_a"] | replace(" A", "") | float }}'
      unit_of_measurement: 'A'
    tp_link_watts:
      friendly_name: "Watts"
      value_template: '{{ states.switch.garage_miner.attributes["current_power_w"] | replace(" W", "") | float }}'
      unit_of_measurement: 'W'
    tp_link_kw:
      friendly_name: "Current KiloWatts"
      value_template: '{{ states.switch.garage_miner.attributes["total_energy_kwh"] | replace(" kW", "") | float }}'
      unit_of_measurement: 'kWh'
    tp_link_kw_today:
      friendly_name: "Today's KiloWatts"
      value_template: '{{ states.switch.garage_miner.attributes["today_energy_kwh"] | replace(" kW", "") | float }}'
      unit_of_measurement: 'kWh'      
    tp_link_volts:
      friendly_name: "Current Voltage"
      value_template: '{{ states.switch.garage_miner.attributes["voltage"] | replace(" V", "") | float }}'
      unit_of_measurement: 'V'
      
    tp_link_amps_2:
      friendly_name: "Amps"
      value_template: '{{ states.switch.garage_2_right.attributes["current_a"] | replace(" A", "") | float }}'
      unit_of_measurement: 'A'
    tp_link_watts_2:
      friendly_name: "Watts"
      value_template: '{{ states.switch.garage_2_right.attributes["current_power_w"] | replace(" W", "") | float }}'
      unit_of_measurement: 'W'
    tp_link_kw_2:
      friendly_name: "Current KiloWatts"
      value_template: '{{ states.switch.garage_2_right.attributes["total_energy_kwh"] | replace(" kW", "") | float }}'
      unit_of_measurement: 'kWh'
    tp_link_kw_today_2:
      friendly_name: "Today's KiloWatts"
      value_template: '{{ states.switch.garage_2_right.attributes["today_energy_kwh"] | replace(" kW", "") | float }}'
      unit_of_measurement: 'kWh'      
    tp_link_volts_2:
      friendly_name: "Current Voltage"
      value_template: '{{ states.switch.garage_2_right.attributes["voltage"] | replace(" V", "") | float }}'
      unit_of_measurement: 'V'

Now we have 10 new sensors. (2 switches)

sensor.tp_link_amps	7.93	unit_of_measurement: A
                              friendly_name: Amps
sensor.tp_link_amps_2	5.04	unit_of_measurement: A
                              friendly_name: Amps
sensor.tp_link_kw	175.354	unit_of_measurement: kWh
                             friendly_name: Current KiloWatts
sensor.tp_link_kw_2	119.239	unit_of_measurement: kWh
                             friendly_name: Current KiloWatts
sensor.tp_link_kw_today	8.187	unit_of_measurement: kWh
                            friendly_name: Today's KiloWatts
sensor.tp_link_kw_today_2	5.394	unit_of_measurement: kWh
                            friendly_name: Today's KiloWatts
sensor.tp_link_volts	111.3	unit_of_measurement: V
                           friendly_name: Current Voltage
sensor.tp_link_volts_2	114.7	unit_of_measurement: V
                           friendly_name: Current Voltage
sensor.tp_link_watts	882.1	unit_of_measurement: W
                          friendly_name: Watts
sensor.tp_link_watts_2	577.03	unit_of_measurement: W
                          friendly_name: Watts
1 Like

Now that HA requirements are done lets hack a copy of the weather widget from github.

appdaemon widget directory on github

Using the weather widget as a base we create a new custom widget in the custom_widgets directory under the appdaemon directory.

\appdaemon\custom_widgets\tplink.yaml

widget_type: basetplink
fields:
  title: ""
  unit: ""
  tp_link_watts: ""
  tp_link_amps: ""
  tp_link_kw: ""
  tp_link_kw_today: ""
  tp_link_volts: ""
  dark_sky_icon: ""
css: []
static_css:
  title_style: $weather_sub_style
  unit_style: $weather_unit_style
  main_style: $weather_main_style
  sub_style: $weather_sub_style
  widget_style: $weather_widget_style
icons: []
static_icons: []

\appdaemon\custom_widgets\basetplink\basetplink.css

		color: white;
}

.widget-basetplink-{{id}} h1 {
		margin-bottom: 0px;
}

.widget-basetplink-{{id}} [class^="primary"] {
		display: inline-block;
		vertical-align: middle;
		padding: 0;
		margin-left: 10px;
		margin-right: 10px;
		margin-top: 0px;
		margin-bottom: 0px;
}

.widget-basetplink-{{id}} [class^="secondary"] {
		display: inline-block;
		vertical-align: middle;
		padding: 0;
		margin-left: 0px;
		margin-right: 0px;
		margin-top: 0px;
		margin-bottom: 0px;
	}

.widget-basetplink-{{id}} .primary-climacon {
		font-family: "Climacons-Font";
		font-size: 70px;
}

.widget-basetplink-{{id}} .primary-info {
		font-size: 300%;
		font-weight: 400;
}

.widget-basetplink-{{id}} .primary-unit {
		font-size: 100%;
		font-weight: 400;
		margin-left: 0;
		margin-top: 20px;
		vertical-align: top;
}

.widget-basetplink-{{id}} .secondary-icon {
		font-family: "Climacons-Font";
		font-size: 40px;
		margin-left: 10px;
		margin-right: 0px;
}

.widget-basetplink-{{id}} .secondary-climacon {
		font-family: "Climacons-Font";
		font-size: 55px;
		margin-left: 10px;
		margin-right: 10px;
}

.widget-basetplink-{{id}} .secondary-info {
		font-size: 125%;
		font-weight: 200;
}

.widget-basetplink-{{id}} hr {
		width: 90%;
		border: 0;
		height: 1px;
		background: rgba(255, 255, 255, 0.25);
		margin-top: 15px;
		margin-bottom: 15px;
}

U:\appdaemon\custom_widgets\basetplink\basetplink.html

<div data-bind="attr: {style: main_style}">
		<!-- <p class="primary-climacon" data-bind="html: dark_sky_icon"></p> -->
		<p class="primary-info" data-bind="text: tp_link_watts"></p>
		<p class="primary-unit" data-bind="html: unit, attr: {style: unit_style}"></p>
<br>
<div data-bind="attr: {style: sub_style}">
		<p class="secondary-info">AMPs:&nbsp;</p>
		<p class="secondary-info" data-bind="text: tp_link_amps"></p>
		<p class="secondary-info">&nbsp;A</p>
		<br>
		<p class="secondary-info">Volts:&nbsp;</p>
		<p class="secondary-info" data-bind="text: tp_link_volts"></p>
		<p class="secondary-info">&nbsp;V</p>
		<br>
		<p class="secondary-info">Current:&nbsp;</p>
		<p class="secondary-info" data-bind="text: tp_link_kw"></p>
		<p class="secondary-info">&nbsp;kWh</p>
		<br>
		<p class="secondary-info">Since Midnight:&nbsp;</p>
		<p class="secondary-info" data-bind="text: tp_link_kw_today"></p>
		<p class="secondary-info">&nbsp;KW</p>
</div>

U:\appdaemon\custom_widgets\basetplink\basetplink.js

{
    // Will be using "self" throughout for the various flavors of "this"
    // so for consistency ...

    self = this;

    self.weather_icons =
    {
      "rain": '&#xe009',
      "snow": '&#xe036',
      "sleet": '&#xe003',
      "wind": '&#xe021',
      "fog": '&#xe01b',
      "cloudy": '&#xe000',
      "clear-day": '&#xe028',
      "clear-night": '&#xe02d',
      "partly-cloudy-day": '&#xe001',
      "partly-cloudy-night": '&#xe002'
    };

    // Initialization

    self.widget_id = widget_id;

    // Store on brightness or fallback to a default

    // Parameters may come in useful later on

    self.parameters = parameters;

    var callbacks = [];

    // Define callbacks for entities - this model allows a widget to monitor multiple entities if needed
    // Initial will be called when the dashboard loads and state has been gathered for the entity
    // Update will be called every time an update occurs for that entity

    self.OnStateAvailable = OnStateAvailable;
    self.OnStateUpdate = OnStateUpdate;

    var monitored_entities =
    [
        {"entity": "sensor.tp_link_watts", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.tp_link_amps", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.tp_link_kw", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.tp_link_kw_today", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.tp_link_volts", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.dark_sky_icon", "initial": self.OnStateAvailable, "update": self.OnStateUpdate}
    ];

    // Finally, call the parent constructor to get things moving

    WidgetBase.call(self, widget_id, url, skin, parameters, monitored_entities, callbacks);

    // Function Definitions

    // The StateAvailable function will be called when
    // self.state[<entity>] has valid information for the requested entity
    // state is the initial state
    // Methods

    function OnStateUpdate(self, state)
    {
        set_view(self, state)
    }

    function OnStateAvailable(self, state)
    {
        if (state.entity_id == "sensor.tp_link_watts")
        {
            self.set_field(self, "unit", state.attributes.unit_of_measurement)
        }
        set_view(self, state)
    }

    function set_view(self, state)
    {
        if (state.entity_id == "sensor.dark_sky_icon")
        {
            self.set_field(self, "dark_sky_icon", self.weather_icons[state.state])
        }
        else
        {
            var field = state.entity_id.split(".")[1];
            self.set_field(self, field, state.state)
        }
    }
}

As you can see there is still some reference to the weather widget which is not needed but I’m still experimenting.

So that was how I did this. But a better way could be achieved by just going to the original switch that HA sees and get the attributes from it without creating 5 sensors per switch.

Ideally: 1 widget for all switches (same type) instead of a widget for each switch.

Then have a widget for multiple attributes defined in yaml file.

Entity:
switch.garage_miner

State: on - a control embedded for controlling the switch would be a bonus

Attributes:
current_power_w: 883.46
total_energy_kwh: 175.979
voltage: 111.7
current_a: 7.91
today_energy_kwh: 8.812
friendly_name: Garage Miner

Of course re-mapping the attributes to better names would be a must.

i think you did a lot right.
but off course you are right, you dont want to create a widget for every sensor.

you already created some fields in the yaml file:

  tp_link_watts: ""
  tp_link_amps: ""
  tp_link_kw: ""
  tp_link_kw_today: ""
  tp_link_volts: ""

so lets try to use them.
all fields that are set in the yaml have a function.
in the dashboard we can fill the value from that field and in the js file we can call that field again.
so lets change:

    var monitored_entities =
    [
        {"entity": "sensor.tp_link_watts", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.tp_link_amps", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.tp_link_kw", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.tp_link_kw_today", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.tp_link_volts", "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": "sensor.dark_sky_icon", "initial": self.OnStateAvailable, "update": self.OnStateUpdate}
    ];

to this:

    var monitored_entities =
    [
        {"entity": parameters.tp_link_watts, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.tp_link_amps, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.tp_link_kw, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.tp_link_kw_today, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.tp_link_volts, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
    ];

as you see i use parameters and then the fieldname. that is how we can call a set field from the dashboard.

but we need a bit of change in the yaml to connect it right.
the yaml file will then be like this:

widget_type: basetplink  # this somehow got lost
tp_link_watts: {{entity1}} # i use general entity names, so we can make e real general widget from it
tp_link_amps: {{entity2}} # the fieldnames we can change in the future, to something more general
tp_link_kw: {{entity3}}
tp_link_kw_today: {{entity4}}
tp_link_volts: {{entity5}}
fields:
  title: ""
  unit: ""
  dark_sky_icon: ""
css: []
static_css:
  title_style: $weather_sub_style
  unit_style: $weather_unit_style
  main_style: $weather_main_style
  sub_style: $weather_sub_style
  widget_style: $weather_widget_style
icons: []
static_icons: []

with those 2 changes it should be possible to use a widget in a dashboard like this:

tplink1:
  widget_type: tplink
  entity1: sensor.tp_link_watts
  entity2: sensor.tp_link_amps
  entity3: sensor.tp_link_kw
  entity4: sensor.tp_link_kw_today
  entity5: sensor.tp_link_volts

i think thats enough to try for a first step.

Unfortunately that did not work out. If I leave other widgets on the status page it crashes them, by it self it displays the html but no data.

image

[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 00-banner.sh: executing... 
-----------------------------------------------------------
 Hass.io Add-on: AppDaemon3 v1.0.0
 Python Apps and HADashboard using AppDaemon 3.x for Home Assistant
 From: Community Hass.io Add-ons
 By: Franck Nijhof <[email protected]>
-----------------------------------------------------------
[cont-init.d] 00-banner.sh: exited 0.
[cont-init.d] 01-log-level.sh: executing... 
Log level is set to INFO
[cont-init.d] 01-log-level.sh: exited 0.
[cont-init.d] 02-updates.sh: executing... 
INFO: You are running the latest version of this add-on
[cont-init.d] 02-updates.sh: exited 0.
[cont-init.d] 03-version-requirements.sh: executing... 
INFO: Supervisor version requirements checks passed.
[cont-init.d] 03-version-requirements.sh: exited 0.
[cont-init.d] 20-init-configuration.sh: executing... 
[cont-init.d] 20-init-configuration.sh: exited 0.
[cont-init.d] 21-compiled-dir.sh: executing... 
[cont-init.d] 21-compiled-dir.sh: exited 0.
[cont-init.d] 30-auto-password.sh: executing... 
[cont-init.d] 30-auto-password.sh: exited 0.
[cont-init.d] 31-ha-url.sh: executing... 
[cont-init.d] 31-ha-url.sh: exited 0.
[cont-init.d] 50-compiled-symlink.sh: executing... 
[cont-init.d] 50-compiled-symlink.sh: exited 0.
[cont-init.d] 80-system-packages.sh: executing... 
[cont-init.d] 80-system-packages.sh: exited 0.
[cont-init.d] 81-python-packages.sh: executing... 
[cont-init.d] 81-python-packages.sh: exited 0.
[cont-init.d] done.
[services.d] starting services
starting version 3.2.4
[services.d] done.
2018-04-09 22:26:47.936638 INFO AppDaemon Version 3.0.0 starting
2018-04-09 22:26:47.937241 INFO Configuration read from: /config/appdaemon/appdaemon.yaml
2018-04-09 22:26:47.939972 INFO AppDaemon: Starting Apps
2018-04-09 22:26:47.945894 INFO AppDaemon: Loading Plugin HASS using class HassPlugin from module hassplugin
2018-04-09 22:26:47.988935 INFO AppDaemon: HASS: HASS Plugin Initializing
2018-04-09 22:26:47.990111 INFO AppDaemon: HASS: HASS Plugin initialization complete
2018-04-09 22:26:47.991216 INFO Starting Dashboards
2018-04-09 22:26:48.010722 INFO API is disabled
2018-04-09 22:26:48.031544 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:26:48.270398 INFO AppDaemon: Got initial state from namespace default
2018-04-09 22:26:50.418029 INFO AppDaemon: Reading config
2018-04-09 22:26:50.427070 INFO AppDaemon: /config/appdaemon/apps/apps.yaml added or modified
2018-04-09 22:26:50.427782 INFO AppDaemon: /config/appdaemon/apps/apps.yaml added or modified
2018-04-09 22:26:50.428743 INFO AppDaemon: App 'hello_world' added
2018-04-09 22:26:50.429989 INFO AppDaemon: Adding /config/appdaemon/apps to module import path
2018-04-09 22:26:50.431919 INFO AppDaemon: Loading App Module: /config/appdaemon/apps/hello.py
2018-04-09 22:26:50.468297 INFO AppDaemon: Initializing app hello_world using class HelloWorld from module hello
2018-04-09 22:26:50.791366 INFO hello_world: Hello from AppDaemon
2018-04-09 22:26:50.796292 INFO hello_world: You are now ready to run Apps!
2018-04-09 22:26:50.798509 INFO AppDaemon: App initialization complete
2018-04-09 22:27:06.196870 INFO function [get_dashboard] finished in 322 ms
2018-04-09 22:27:40.599267 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:27:45.632329 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:27:45.663679 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:27:50.681914 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:27:50.702624 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:27:55.721698 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:27:55.742085 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:00.762322 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:00.784284 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:05.809863 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:05.829330 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:10.848371 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:10.867604 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:15.888549 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:15.907335 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:20.933226 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:20.952702 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:25.973239 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:25.994896 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:31.024394 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:31.045060 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:36.064686 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:36.090450 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:41.110202 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:41.127485 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:46.145575 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:46.170023 WARNING AppDaemon: HASS: Disconnected from Home Assistant, retrying in 5 seconds
2018-04-09 22:28:51.203556 INFO AppDaemon: HASS: Connected to Home Assistant 0.65.6
2018-04-09 22:28:51.513911 INFO AppDaemon: Processing restart for HASS
2018-04-09 22:28:51.515235 INFO AppDaemon: Terminating hello_world
2018-04-09 22:28:51.517402 INFO AppDaemon: Initializing app hello_world using class HelloWorld from module hello
2018-04-09 22:28:51.529638 INFO hello_world: Hello from AppDaemon
2018-04-09 22:28:51.540162 INFO hello_world: You are now ready to run Apps!
2018-04-09 22:29:37.066696 INFO function [get_dashboard] finished in 214 ms

oke, lets simplefy.

try this in your yaml:

widget_type: basetplink  # this somehow got lost
tp_link_watts: ""
tp_link_amps: ""
tp_link_kw: ""
tp_link_kw_today: ""
tp_link_volts: ""
fields:
  title: ""
  unit: ""
  dark_sky_icon: ""
css: []
static_css:
  title_style: $weather_sub_style
  unit_style: $weather_unit_style
  main_style: $weather_main_style
  sub_style: $weather_sub_style
  widget_style: $weather_widget_style
icons: []
static_icons: []

and this in your dashboard:

tplink1:
  widget_type: tplink
  tp_link_watts: sensor.tp_link_watts
  tp_link_amps: sensor.tp_link_amps
  tp_link_kw: sensor.tp_link_kw
  p_link_kw_today: sensor.tp_link_kw_today
  tp_link_volts: sensor.tp_link_volts

layout:
  - tplink1

if that doesnt work place check your javaconsole in google chrome (CTRL SHIFT I) for errors.

Ok it works, but not as written. Fields needed to be moved than follow yaml syntax. Causes dashboard to crash.

I try both ways with the dashboard changed as directed. (obliviously did not do both at same time hence the commented block)

# widget_type: basetplink  # this somehow got lost
# fields:
  # title: ""
  # unit: ""
  # dark_sky_icon: ""
  # tp_link_watts: ""
  # tp_link_amps: ""
  # tp_link_kw: ""
  # tp_link_kw_today: ""
  # tp_link_volts: ""

# css: []
# static_css:
  # title_style: $weather_sub_style
  # unit_style: $weather_unit_style
  # main_style: $weather_main_style
  # sub_style: $weather_sub_style
  # widget_style: $weather_widget_style
# icons: []
# static_icons: []

widget_type: basetplink  # this somehow got lost
fields:
  title: ""
  # unit: ""  
  # dark_sky_icon: ""
  tp_link_watts: {{entity1}} # i use general entity names, so we can make e real general widget from it
  tp_link_amps: {{entity2}} # the fieldnames we can change in the future, to something more general
  tp_link_kw: {{entity3}}
  tp_link_kw_today: {{entity4}}
  tp_link_volts: {{entity5}}

css: []
static_css:
  title_style: $weather_sub_style
  unit_style: $weather_unit_style
  main_style: $weather_main_style
  sub_style: $weather_sub_style
  widget_style: $weather_widget_style
icons: []
static_icons: []

Note you have a typo in you dash example missing a t at the 5th line

I have spent hours trying different things and figured it is a parameter issue of not being passed, but no idea where.

I’m close but no cigar! It reads both tp_link outlets but does not bring it to the widget. There are errors like “entity not defined” “undefined property…”

I’m sure the issue is in the functions but I do not know what I am doing. Also need declaration for entity.

I changed some names to clear some confusion I was having.

U:\appdaemon\custom_widgets\basetplink\basetplink.js

function basetplink(widget_id, url, skin, parameters)
{

    self = this;


    self.widget_id = widget_id;

    self.parameters = parameters;

    var callbacks = [];

    self.OnStateAvailable = OnStateAvailable;
    self.OnStateUpdate = OnStateUpdate;
	
    var monitored_entities =

    [
        {"entity": parameters.watts, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.amps, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.kw, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.kw_today, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.volts, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
    ];


    WidgetBase.call(self, widget_id, url, skin, parameters, monitored_entities, callbacks);

    function OnStateUpdate(self, state)
    {
        set_view(self, state)
    }

    function OnStateAvailable(self, state)
    {
        if (state.entity_id == "sensor.tp_link_watts")
        {
            self.set_field(self, "unit", state.attributes.unit_of_measurement)
        }
        set_view(self, state)
    }

    function set_view(self, state)
    {
        if (state.entity_id == entity)
        {
            // self.set_field(self, "entity", self.entity[state.state])
           self.set_field(self, entity, self.entity[state.state])
        } 

    }
}

U:\appdaemon\custom_widgets\basetplink\basetplink.html

<h1 class="title" data-bind="text: title, attr:{ style: title_style}"></h1>
<div data-bind="attr: {style: main_style}">
		<p class="primary-info" data-bind="text: watts"></p>
		<p class="secondary-info">&nbsp;W</p>
<br>
<div data-bind="attr: {style: sub_style}">
		<p class="secondary-info">AMPs:&nbsp;</p>
		<p class="secondary-info" data-bind="text: amps"></p>
		<p class="secondary-info">&nbsp;A</p>
		<br>
		<p class="secondary-info">Volts:&nbsp;</p>
		<p class="secondary-info" data-bind="text: volts"></p>
		<p class="secondary-info">&nbsp;V</p>
		<br>
		<p class="secondary-info">Current:&nbsp;</p>
		<p class="secondary-info" data-bind="text: kw"></p>
		<p class="secondary-info">&nbsp;kWh</p>
		<br>
		<p class="secondary-info">Since Midnight:&nbsp;</p>
		<p class="secondary-info" data-bind="text: kw_today"></p>
		<p class="secondary-info">&nbsp;KW</p>
</div>

U:\appdaemon\custom_widgets\tplink.yaml

widget_type: basetplink
fields:
  title: ""
  unit: ""
  dark_sky_icon: ""
  watts: ""
  amps: ""
  kw: ""
  kw_today: ""
  volts: ""

css: []
static_css:
  title_style: $weather_sub_style
  unit_style: $weather_unit_style
  main_style: $weather_main_style
  sub_style: $weather_sub_style
  widget_style: $weather_widget_style
icons: []
static_icons: []

U:\appdaemon\dashboards\Status.dash


title: Status
widget_dimensions: [120, 120]
widget_size: [1, 1]
widget_margins: [5, 5]
columns: 8
  
tplink1:
  widget_type: tplink
  title: TP Main
  watts: sensor.tp_link_watts
  amps: sensor.tp_link_amps
  kw: sensor.tp_link_kw
  kw_today: sensor.tp_link_kw_today
  volts: sensor.tp_link_volts
  
tplink:
  widget_type: tplink
  title: TP Tester
  watts: sensor.tp_link_watts_2
  amps: sensor.tp_link_amps_2
  kw: sensor.tp_link_kw_2
  kw_today: sensor.tp_link_kw_today_2
  volts: sensor.tp_link_volts_2

layout:
  - tplink1(3x2), tplink(3x2)

is this working now?

No unfortunately, I can see the values in chrome console as errors ‘cannot read property ‘114.0’ undefined’

there are a few things wrong.
the variables watts,amps,kw,kw_today and volts really need to be at the toplevel from the yaml file.
so not inside fields as you have it now.
if i look at other widgets i see that entity is also set there and when i look at heater.yaml (i created that widget) i see also that the both used entities are at toplevel.


so lets simplefy the js to get errors out.

function basetplink(widget_id, url, skin, parameters)
{

    self = this;


    self.widget_id = widget_id;

    self.parameters = parameters;

    var callbacks = [];

    self.OnStateAvailable = OnStateAvailable;
    self.OnStateUpdate = OnStateUpdate;
	
    var monitored_entities =

    [
        {"entity": parameters.watts, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.amps, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.kw, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.kw_today, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.volts, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
    ];

    WidgetBase.call(self, widget_id, url, skin, parameters, monitored_entities, callbacks);

    function OnStateUpdate(self, state)
    {
        set_view(self, state)
    }

    function OnStateAvailable(self, state)
    {
        set_view(self, state)
    }

    function set_view(self, state)
    {
       self.set_field(self, "tp_link_watts", "hello")
    }
}
 

the set_field line is the most important here. i used “tp_link_watts” because that was the name you used in the html file. if you changed the html then you need to change it here also.
i used “hello” just to see if we get the textvalue from the js to the html without errors.

so to do:

  1. edit the yaml as i said
  2. edit the js like i gave here
    after that you should get 2 widgets where on both the watts show “hello” as value.

If I do this in the yaml file.

widget_type: basetplink  # this somehow got lost
watts: ""
amps: ""
kw: ""
kw_today: ""
volts: ""
fields:
  title: ""
  unit: ""
  dark_sky_icon: ""
css: []
static_css:
  title_style: $weather_sub_style
  unit_style: $weather_unit_style
  main_style: $weather_main_style
  sub_style: $weather_sub_style
  widget_style: $weather_widget_style
icons: []

I get this.

If I do this.

widget_type: basetplink
fields:
  title: ""
  unit: ""
  dark_sky_icon: ""
  watts: ""
  amps: ""
  kw: ""
  kw_today: ""
  volts: ""

css: []
static_css:
  title_style: $weather_sub_style
  unit_style: $weather_unit_style
  main_style: $weather_main_style
  sub_style: $weather_sub_style
  widget_style: $weather_widget_style
icons: []
static_icons: []

I get this.

So hello works, but not with the suggested yaml file, which has been my experience with everything I try.

Closer, but it put’s all entity through watts!

    function set_view(self, state)
    {
       self.value = state.state;

	  self.set_field(self, "watts", (state.state))
    }

hmm strange, but if it works it works.

oke lets try to edit the set_view.

    function set_view(self, state)
    {
      if (self.entity == self.parameters.watts)
      {
        self.set_field(self, "watts", self.format_number(self, state.state))
      }
    }

first we check if the changed entity the watts entity then we change the watts field to the state.

edit: and if that works you can add the if statement for the other entities.

No errors, but doesn’t work.

oke, it was worth a try, but we need to splitt up the functions (callbacks)
this part:

    var monitored_entities =

    [
        {"entity": parameters.watts, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.amps, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.kw, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.kw_today, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
        {"entity": parameters.volts, "initial": self.OnStateAvailable, "update": self.OnStateUpdate},
    ];

sets the callbacks for each entity. right now we call the same callback for all, but then we cant know which entity needs to be updated.
so lets create 1 for every entity like this:

    var monitored_entities =

    [
        {"entity": parameters.watts, "initial": self.OnWattsStateAvailable, "update": self.OnWattsStateUpdate},
        {"entity": parameters.amps, "initial": self.OnAmpsStateAvailable, "update": self.OnAmpsStateUpdate},
        {"entity": parameters.kw, "initial": self.OnKWStateAvailable, "update": self.OnKWStateUpdate},
        {"entity": parameters.kw_today, "initial": self.OnKWTodayStateAvailable, "update": self.OnKWTodayStateUpdate},
        {"entity": parameters.volts, "initial": self.OnVoltsStateAvailable, "update": self.OnVoltsStateUpdate},
    ];

off course those callbacks need to exist and the existing functions become obsolete.
so lets change this:

    function OnStateUpdate(self, state)
    {
        set_view(self, state)
    }

    function OnStateAvailable(self, state)
    {
        set_view(self, state)
    }

    function set_view(self, state)
    {
       self.set_field(self, "tp_link_watts", "hello")
    }
}

to this:

    function OnWattsStateUpdate(self, state)
    {
       self.set_field(self, "watts", self.format_number(self, state.state))
    }
    function OnWattsStateAvailable(self, state)
    {
       self.set_field(self, "watts", self.format_number(self, state.state))
    }

    function OnAmpsStateUpdate(self, state)
    {
       self.set_field(self, "amps", self.format_number(self, state.state))
    }

    function OnAmpsStateAvailable(self, state)
    {
       self.set_field(self, "amps", self.format_number(self, state.state))
    }

the other 3 is just copy paste and change watts or amps to volts, KW or KWtoday

Thank you Rene. It works!
You should add it to your custom widgets on github!

basetplink.js

function basetplink(widget_id, url, skin, parameters)
{

    self = this;


    self.widget_id = widget_id;

    self.parameters = parameters;

    var callbacks = [];

    self.OnWattsStateAvailable = OnWattsStateAvailable;
    self.OnWattsStateUpdate = OnWattsStateUpdate;
    self.OnAmpsStateAvailable = OnAmpsStateAvailable;
    self.OnAmpsStateUpdate = OnAmpsStateUpdate;
    self.OnKWStateAvailable = OnKWStateAvailable;
    self.OnKWStateUpdate = OnKWStateUpdate;
    self.OnKWTodayStateAvailable = OnKWTodayStateAvailable;
    self.OnKWTodayStateUpdate = OnKWTodayStateUpdate;
    self.OnVoltsStateAvailable = OnVoltsStateAvailable;
    self.OnVoltsStateUpdate = OnVoltsStateUpdate;

    var monitored_entities =

    [
        {"entity": parameters.watts, "initial": self.OnWattsStateAvailable, "update": self.OnWattsStateUpdate},
        {"entity": parameters.amps, "initial": self.OnAmpsStateAvailable, "update": self.OnAmpsStateUpdate},
        {"entity": parameters.kw, "initial": self.OnKWStateAvailable, "update": self.OnKWStateUpdate},
        {"entity": parameters.kw_today, "initial": self.OnKWTodayStateAvailable, "update": self.OnKWTodayStateUpdate},
        {"entity": parameters.volts, "initial": self.OnVoltsStateAvailable, "update": self.OnVoltsStateUpdate},
    ];

    WidgetBase.call(self, widget_id, url, skin, parameters, monitored_entities, callbacks);

    function OnWattsStateUpdate(self, state)
    {
       self.set_field(self, "watts", self.format_number(self, state.state))
    }

    function OnWattsStateAvailable(self, state)
    {
       self.set_field(self, "watts", self.format_number(self, state.state))
    }

    function OnAmpsStateUpdate(self, state)
    {
       self.set_field(self, "amps", self.format_number(self, state.state))
    }

    function OnAmpsStateAvailable(self, state)
    {
       self.set_field(self, "amps", self.format_number(self, state.state))
    }

    function OnKWStateUpdate(self, state)
    {
       self.set_field(self, "kw", self.format_number(self, state.state))
    }

    function OnKWStateAvailable(self, state)
    {
       self.set_field(self, "kw", self.format_number(self, state.state))
    }
    function OnKWTodayStateUpdate(self, state)
    {
       self.set_field(self, "kw_today", self.format_number(self, state.state))
    }

    function OnKWTodayStateAvailable(self, state)
    {
       self.set_field(self, "kw_today", self.format_number(self, state.state))
    }

    function OnVoltsStateUpdate(self, state)
    {
       self.set_field(self, "volts", self.format_number(self, state.state))
    }

    function OnVoltsStateAvailable(self, state)
    {
       self.set_field(self, "volts", self.format_number(self, state.state))
    }
}

you can create your own :wink:
you did the work, so i dont take credit :wink:

but we are not ready yet.
because at this point the widget is still depending on the css from the weather widget.
so the yaml needs to be edited once more and we need to add some vars to the skin you use (if you dont use a skin it needs to be added to the variables.yaml from the default skin)
and to make it so that we can change the view from every entity seperately, we need to change the html file.
so lets do that first. your html file looks like this:

<h1 class="title" data-bind="text: title, attr:{ style: title_style}"></h1>
<div data-bind="attr: {style: main_style}">
		<p class="primary-info" data-bind="text: watts"></p>
		<p class="secondary-info">&nbsp;W</p>
<br>
<div data-bind="attr: {style: sub_style}">
		<p class="secondary-info">AMPs:&nbsp;</p>
		<p class="secondary-info" data-bind="text: amps"></p>
		<p class="secondary-info">&nbsp;A</p>
		<br>
		<p class="secondary-info">Volts:&nbsp;</p>
		<p class="secondary-info" data-bind="text: volts"></p>
		<p class="secondary-info">&nbsp;V</p>
		<br>
		<p class="secondary-info">Current:&nbsp;</p>
		<p class="secondary-info" data-bind="text: kw"></p>
		<p class="secondary-info">&nbsp;kWh</p>
		<br>
		<p class="secondary-info">Since Midnight:&nbsp;</p>
		<p class="secondary-info" data-bind="text: kw_today"></p>
		<p class="secondary-info">&nbsp;KW</p>
</div>

data-bind=“attr: {style: main_style}” makes that we can change the style from the div that it is in.
you have main_style and sub_style. and you can find them also inside the yaml
so lets make it like this (by the way you forgot to close the first div here):

<h1 class="title" data-bind="text: title, attr:{ style: title_style}"></h1>
<div data-bind="attr: {style: watts_style}">
		<p class="primary-info" data-bind="text: watts"></p>
		<p class="secondary-info">&nbsp;W</p>
</div>
<div data-bind="attr: {style: amp_style}">
		<p class="secondary-info">AMPs:&nbsp;</p>
		<p class="secondary-info" data-bind="text: amps"></p>
		<p class="secondary-info">&nbsp;A</p>
</div>
<div data-bind="attr: {style: volts_style}">
		<p class="secondary-info">Volts:&nbsp;</p>
		<p class="secondary-info" data-bind="text: volts"></p>
		<p class="secondary-info">&nbsp;V</p>
</div>
<div data-bind="attr: {style: kw_style}">
		<p class="secondary-info">Current:&nbsp;</p>
		<p class="secondary-info" data-bind="text: kw"></p>
		<p class="secondary-info">&nbsp;kWh</p>
</div>
<div data-bind="attr: {style: kw_today_style}">
		<p class="secondary-info">Since Midnight:&nbsp;</p>
		<p class="secondary-info" data-bind="text: kw_today"></p>
		<p class="secondary-info">&nbsp;KW</p>
</div>

now we need to change the yaml to connect the styles to it:

static_css:
  title_style: $tplink_sub_style
  watts_style: $tplink_watts_style
  amp_style: $tplink_amp_style
  volts_style: $tplink_volts_style
  kw_style: $tplink_kw_style
  kw_today_style: $tplink_kw_today_style
  widget_style: $tplink_widget_style

now that that is done you can set the style for all lines independant from another in your dashboard.
you can change color, height, top position, fontsize, etc.

but there will be errors in the log if we dont put a default inside the variables.yaml from the skin you use.
so in the variables.yaml we put:

tplink_title_style: $style_title
tplink_widget_style: $background_style
tplink_watts_style: $style_title
tplink_amps_style: $style_title
tplink_volts_style: $style_title
tplink_kw_style: $style_title
tplink_kw_today_style: $style_title

i used the style_title here for all lines as default, but off course you can change that to any css you like.

off course this is all not really needed, but why stop now, when you are so good on your way to create a nice general widget (allthough we could make it more general, and we can make it that you dont need to create 5 sensors in HA if you would like that)

Thanks again Rene, I have added the changes.

And yes I would be very interested to get rid of the computed sensors in HA! To me HA is like my Vera a place to collect the information but a separate dashboard to control all of that information/sensors.

Before we move on I have a question, can the switch attribute be added to the display of the information, or combined in some way. We are displaying information from a switch so the ability to control said switch without another widget would be nice.