Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions custom_components/powersensor/PlugMeasurements.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from enum import Enum


class PlugMeasurements(Enum):
WATTS = 1
VOLTAGE = 2
APPARENT_CURRENT = 3
ACTIVE_CURRENT = 4
REACTIVE_CURRENT =5
REACTIVE_CURRENT = 5
SUMMATION_ENERGY = 6
2 changes: 2 additions & 0 deletions custom_components/powersensor/PowersensorEntity.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ def __init__(self, hass: HomeAssistant, mac : str,
self._attr_native_unit_of_measurement = config["unit"]
self._attr_device_info = self.device_info
self._attr_suggested_display_precision = config["precision"]
self._attr_entity_registry_visible_default = config['visible'] if 'visible' in config.keys() else True

self._signal = f"{POWER_SENSOR_UPDATE_SIGNAL}_{self._mac}_{config['event']}"
if 'state_class' in config.keys():
self._attr_state_class = config['state_class']
Expand Down
16 changes: 9 additions & 7 deletions custom_components/powersensor/PowersensorPlugEntity.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
_LOGGER = logging.getLogger(__name__)



_config = {
PlugMeasurements.WATTS: {
"name": "Power",
Expand All @@ -28,31 +27,35 @@
"unit": UnitOfElectricPotential.VOLT,
"precision": 2,
'event': 'average_power_components',
'message_key': 'volts'
'message_key': 'volts',
'visible': False,
},
PlugMeasurements.APPARENT_CURRENT: {
"name": "Apparent Current",
"device_class": SensorDeviceClass.CURRENT,
"unit": UnitOfElectricCurrent.AMPERE,
"precision": 2,
'event': 'average_power_components',
'message_key': 'apparent_current'
'message_key': 'apparent_current',
'visible': False,
},
PlugMeasurements.ACTIVE_CURRENT: {
"name": "Current", #"Active Current"
"name": "Active Current",
"device_class": SensorDeviceClass.CURRENT,
"unit": UnitOfElectricCurrent.AMPERE,
"precision": 2,
'event': 'average_power_components',
'message_key': 'active_current'
'message_key': 'active_current',
'visible': False,
},
PlugMeasurements.REACTIVE_CURRENT: {
"name": "Reactive Current",
"device_class": SensorDeviceClass.CURRENT,
"unit": UnitOfElectricCurrent.AMPERE,
"precision": 2,
'event': 'average_power_components',
'message_key': 'reactive_current'
'message_key': 'reactive_current',
'visible': False,
},
PlugMeasurements.SUMMATION_ENERGY: {
"name": "Total Energy",
Expand Down Expand Up @@ -86,7 +89,6 @@ def device_info(self) -> DeviceInfo:
'manufacturer': "Powersensor",
'model': self._model,
'name': self._device_name,
# "via_device": # if we use this, can it be updated dynamically?
}

def _default_device_name(self) -> str:
Expand Down
1 change: 0 additions & 1 deletion custom_components/powersensor/PowersensorSensorEntity.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ def device_info(self) -> DeviceInfo:
'manufacturer': "Powersensor",
'model': self._model,
'name': self._device_name ,
# "via_device": # if we use this, can it be updated dynamically?
}

def _ensure_matching_prefix(self):
Expand Down
42 changes: 38 additions & 4 deletions custom_components/powersensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,26 @@

from .PowersensorDiscoveryService import PowersensorDiscoveryService
from .PowersensorMessageDispatcher import PowersensorMessageDispatcher
from .config_flow import PowersensorConfigFlow
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)

PLATFORMS: list[Platform] = [Platform.SENSOR]

#
# config entry.data structure (version 2.1):
# {
# devices = {
# mac = {
# name =,
# display_name =,
# mac =,
# host =,
# port =,
# }
# with_solar =,
# }
#

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up integration from a config entry."""
Expand All @@ -30,14 +45,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await zeroconf_service.start()

# Establish our virtual household
vhh = VirtualHousehold(False)
vhh = VirtualHousehold(entry.data['with_solar'])


# TODO: can we move the dispatcher into the entry.runtime_data dict?
# Set up message dispatcher
dispatcher = PowersensorMessageDispatcher(hass, vhh)
for mac, network_info in entry.data.items():
for mac, network_info in entry.data['devices'].items():
await dispatcher.enqueue_plug_for_adding(network_info)


entry.runtime_data = { "vhh": vhh , "dispatcher" : dispatcher, "zeroconf" : zeroconf_service}
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
Expand All @@ -52,5 +67,24 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if "zeroconf" in entry.runtime_data .keys():
await entry.runtime_data["zeroconf"].stop()

hass.data[DOMAIN].pop(entry.entry_id)

return unload_ok


async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old config entry."""
_LOGGER.debug("Upgrading config from %s.%s", entry.version, entry.minor_version)
if entry.version > PowersensorConfigFlow.VERSION:
# Downgrade from future version
return False

if entry.version == 1:
# Move device info into subkey, add with_solar key
devices = { **entry.data }
new_data = { 'devices': devices, 'with_solar': False }
hass.config_entries.async_update_entry(entry, data=new_data, version=2, minor_version=1)

_LOGGER.debug("Upgrading config to %s.%s", entry.version, entry.minor_version)
return True

9 changes: 6 additions & 3 deletions custom_components/powersensor/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def _extract_device_name(discovery_info) -> str:
class PowersensorConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""

VERSION = 1
VERSION = 2

def __init__(self):
"""Initialize the config flow."""
Expand Down Expand Up @@ -142,6 +142,9 @@ async def async_step_discovery_confirm(
_LOGGER.debug(self.hass.data[DOMAIN]["discovered_plugs"])
return self.async_create_entry(
title="Powersensor",
data=self.hass.data[DOMAIN]["discovered_plugs"]
data={
'devices': self.hass.data[DOMAIN]["discovered_plugs"],
'with_solar': False,
}
)
return self.async_show_form(step_id="discovery_confirm")
return self.async_show_form(step_id="discovery_confirm")
6 changes: 5 additions & 1 deletion custom_components/powersensor/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ async def async_setup_entry(
async def create_plug(plug_mac_address: str):
this_plug_sensors = [PowersensorPlugEntity(hass, plug_mac_address, PlugMeasurements.WATTS),
PowersensorPlugEntity(hass, plug_mac_address, PlugMeasurements.VOLTAGE),
PowersensorPlugEntity(hass, plug_mac_address, PlugMeasurements.APPARENT_CURRENT),
PowersensorPlugEntity(hass, plug_mac_address, PlugMeasurements.ACTIVE_CURRENT),
PowersensorPlugEntity(hass, plug_mac_address, PlugMeasurements.REACTIVE_CURRENT),
PowersensorPlugEntity(hass, plug_mac_address, PlugMeasurements.SUMMATION_ENERGY)]

async_add_entities(this_plug_sensors, True)
Expand All @@ -54,7 +56,9 @@ async def handle_discovered_plug(plug_mac_address: str, host: str, port: int, na

async def handle_discovered_sensor(sensor_mac: str, sensor_role: str):
if sensor_role == 'solar':
entry.runtime_data["with_solar"] = True # Remember for next time we start
new_data = { **entry.data }
new_data['with_solar'] = True # Remember for next time we start
hass.config_entries.async_update_entry(entry, data=new_data)

new_sensors = [
PowersensorSensorEntity(hass, sensor_mac, SensorMeasurements.Battery),
Expand Down
48 changes: 36 additions & 12 deletions docs/source/data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ household.
Virtual Household
------------------
Built on top of the sensors, the API provided by `powersensor_local <https://github.com/DiUS/python-powersensor_local>`_
provides a ``virtual household``. This view is captures the key data most users want to capture at the household level.
provides a "virtual household". This view captures the key data most users want to capture at the household level.
This includes

* Energy imported from the grid
* Total home energy usage
* Energy exported to the grid (from solar , if available)
* Energy imported from the grid
* Energy exported to the grid (from solar)
* Total solar production

as well as the corresponding instantaneous power consumption/production.

For installations lacking solar, the generation related entities will not
be available.

.. note::

Powersensor deals with *net* readings, meaning that the energy import and
Expand All @@ -36,17 +39,38 @@ The household readings update as sensor data becomes available.

Plugs
-----
Each plug exposes 6 entities reflecting the different measurements made by the plug these are
Each plug exposes several entities reflecting the different measuremens
made by the plug. By default only two are made visible:

* Power
* Total Energy Consumption
* Current
* Voltage
* Total Energy

The remaining entities are:

The current displayed in the integration represents active current only, but the Powersensor
api exposes reactive and apparent current as well. Future release plans intend to expose
all available data, with reactive and apparent current being optional during the configuration
step for the integration.
* Volts
* Active Current
* Reactive Current
* Apparent Current

These are of secondary importance and usefulness, and hence aren't visible
by default. They can be selectively made visible by going to Settings >
Devices & services > Powersensor and selecting the plug, then clicking on
the desired entity and then the gear on the following screen. Toggle the
Visible option there.

The Volts entity shows the mains voltage as seen at that particular plug. Due
to voltage drop in wires, each plug is likely to show a slightly different
mains voltage.

Simplified, the Apparent Current entity shows the effective current going
through the plug. This is the current that may lead to a breaker tripping if
it gets excessive. The Active and Reactive measurements are the components
of it, and are likely of little interest outside of curiousity.

.. note::
It is more common to hear of Active, Reactive and Apparent *Power*, and
the plug's different current measurements should not be confused for power
measurements.

The plug readings typically update every second.

Expand Down Expand Up @@ -78,4 +102,4 @@ Any of the plug, sensor or virtual household entities can be used in
automation workflows. To exercise control of other devices in your
household, first install any relevant integrations for those devices.
Then follow the usual Home Assistant steps for setting up rules:
Settings->Automations & Scenes and +Create Automation
Settings >Automations & Scenes and +Create Automation
Loading