diff --git a/custom_components/powersensor/PlugMeasurements.py b/custom_components/powersensor/PlugMeasurements.py index cf2e90e..f663a1d 100644 --- a/custom_components/powersensor/PlugMeasurements.py +++ b/custom_components/powersensor/PlugMeasurements.py @@ -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 diff --git a/custom_components/powersensor/PowersensorEntity.py b/custom_components/powersensor/PowersensorEntity.py index c9710e6..d3f6547 100644 --- a/custom_components/powersensor/PowersensorEntity.py +++ b/custom_components/powersensor/PowersensorEntity.py @@ -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'] diff --git a/custom_components/powersensor/PowersensorPlugEntity.py b/custom_components/powersensor/PowersensorPlugEntity.py index 6048882..f4c0dcc 100644 --- a/custom_components/powersensor/PowersensorPlugEntity.py +++ b/custom_components/powersensor/PowersensorPlugEntity.py @@ -12,7 +12,6 @@ _LOGGER = logging.getLogger(__name__) - _config = { PlugMeasurements.WATTS: { "name": "Power", @@ -28,7 +27,8 @@ "unit": UnitOfElectricPotential.VOLT, "precision": 2, 'event': 'average_power_components', - 'message_key': 'volts' + 'message_key': 'volts', + 'visible': False, }, PlugMeasurements.APPARENT_CURRENT: { "name": "Apparent Current", @@ -36,15 +36,17 @@ "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", @@ -52,7 +54,8 @@ "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", @@ -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: diff --git a/custom_components/powersensor/PowersensorSensorEntity.py b/custom_components/powersensor/PowersensorSensorEntity.py index 8654ed8..140e508 100644 --- a/custom_components/powersensor/PowersensorSensorEntity.py +++ b/custom_components/powersensor/PowersensorSensorEntity.py @@ -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): diff --git a/custom_components/powersensor/__init__.py b/custom_components/powersensor/__init__.py index fb9cd3b..e3fd26c 100644 --- a/custom_components/powersensor/__init__.py +++ b/custom_components/powersensor/__init__.py @@ -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.""" @@ -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 @@ -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 + diff --git a/custom_components/powersensor/config_flow.py b/custom_components/powersensor/config_flow.py index 95face4..851b0d1 100644 --- a/custom_components/powersensor/config_flow.py +++ b/custom_components/powersensor/config_flow.py @@ -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.""" @@ -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") \ No newline at end of file + return self.async_show_form(step_id="discovery_confirm") diff --git a/custom_components/powersensor/sensor.py b/custom_components/powersensor/sensor.py index b148758..8db3298 100644 --- a/custom_components/powersensor/sensor.py +++ b/custom_components/powersensor/sensor.py @@ -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) @@ -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), diff --git a/docs/source/data.rst b/docs/source/data.rst index e6e79e8..790a2ba 100644 --- a/docs/source/data.rst +++ b/docs/source/data.rst @@ -7,16 +7,19 @@ household. Virtual Household ------------------ Built on top of the sensors, the API provided by `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 @@ -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. @@ -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