Skip to content

Commit 82de555

Browse files
Chore/refactor (#17)
* Remove unnecessary title field As per hassfest warning. * Refactor & simplify dynamic household entity handling Also avoid getting with_solar/with_mains out of sync by changing to pulling that knowledge from the known device roles instead. This also addresses the issue of not being able to go back to a mains-only install as there was no way of clearing with_solar. There is no support for removing already created household entities yet however. Switched more strings over to const.py imports to ensure consistency and reduce risk of typos. --------- Co-authored-by: Jade Mattsson <github@frozenlogic.org>
1 parent b56c508 commit 82de555

File tree

6 files changed

+200
-143
lines changed

6 files changed

+200
-143
lines changed

custom_components/powersensor/PowersensorMessageDispatcher.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
from custom_components.powersensor.AsyncSet import AsyncSet
1212
from custom_components.powersensor.const import (
13+
# Used config entry fields
14+
CFG_ROLES,
15+
16+
# Used signals
1317
CREATE_PLUG_SIGNAL,
1418
CREATE_SENSOR_SIGNAL,
1519
DATA_UPDATE_SIGNAL_FMT_MAC_EVENT,
@@ -173,7 +177,7 @@ async def handle_relaying_for(self, event: str, message: dict):
173177
_LOGGER.warning(f"Ignoring relayed device with MAC \"{mac}\" and type {device_type}")
174178
return
175179

176-
persisted_role = self._entry.data.get('roles', {}).get(mac, None)
180+
persisted_role = self._entry.data.get(CFG_ROLES, {}).get(mac, None)
177181
role = message.get('role', None)
178182
_LOGGER.debug(f"Relayed sensor {mac} with role {role} found")
179183

@@ -187,7 +191,7 @@ async def handle_relaying_for(self, event: str, message: dict):
187191

188192
async def handle_message(self, event: str, message: dict):
189193
mac = message['mac']
190-
persisted_role = self._entry.data.get('roles', {}).get(mac, None)
194+
persisted_role = self._entry.data.get(CFG_ROLES, {}).get(mac, None)
191195
role = message.get('role', persisted_role)
192196
message['role'] = role
193197

custom_components/powersensor/__init__.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,22 @@
1111
from .PowersensorDiscoveryService import PowersensorDiscoveryService
1212
from .PowersensorMessageDispatcher import PowersensorMessageDispatcher
1313
from .config_flow import PowersensorConfigFlow
14-
from .const import DOMAIN
14+
from .const import (
15+
CFG_DEVICES,
16+
CFG_ROLES,
17+
DOMAIN,
18+
ROLE_SOLAR,
19+
RT_VHH,
20+
RT_DISPATCHER,
21+
RT_ZEROCONF,
22+
)
23+
1524
_LOGGER = logging.getLogger(__name__)
1625

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

1928
#
20-
# config entry.data structure (version 2.1):
29+
# config entry.data structure (version 2.2):
2130
# {
2231
# devices = {
2332
# mac = {
@@ -27,7 +36,6 @@
2736
# host =,
2837
# port =,
2938
# }
30-
# with_solar =,
3139
# roles = {
3240
# mac = role,
3341
# }
@@ -41,22 +49,26 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
4149
hass.data[DOMAIN][entry.entry_id] = {}
4250

4351
integration = await async_get_integration(hass, DOMAIN)
44-
manifest = integration.manifest
52+
manifest = integration.manifest
4553

4654
# Establish create the zeroconf discovery service
47-
zeroconf_service= PowersensorDiscoveryService(hass, manifest["zeroconf"][0])
55+
zeroconf_service = PowersensorDiscoveryService(hass, manifest["zeroconf"][0])
4856
await zeroconf_service.start()
4957

5058
# Establish our virtual household
51-
vhh = VirtualHousehold(entry.data['with_solar'])
59+
with_solar = ROLE_SOLAR in entry.data.get(CFG_ROLES, {}).values()
60+
vhh = VirtualHousehold(with_solar)
5261

5362
# Set up message dispatcher
5463
dispatcher = PowersensorMessageDispatcher(hass, entry, vhh)
55-
for mac, network_info in entry.data['devices'].items():
64+
for mac, network_info in entry.data.get(CFG_DEVICES, {}).items():
5665
await dispatcher.enqueue_plug_for_adding(network_info)
5766

58-
59-
entry.runtime_data = { "vhh": vhh , "dispatcher" : dispatcher, "zeroconf" : zeroconf_service}
67+
entry.runtime_data = {
68+
RT_VHH: vhh,
69+
RT_DISPATCHER: dispatcher,
70+
RT_ZEROCONF: zeroconf_service
71+
}
6072
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
6173
return True
6274

@@ -65,10 +77,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
6577
"""Unload a config entry."""
6678
_LOGGER.debug("Started unloading for %s", entry.entry_id)
6779
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
68-
if "dispatcher" in entry.runtime_data .keys():
69-
await entry.runtime_data["dispatcher"].disconnect()
70-
if "zeroconf" in entry.runtime_data .keys():
71-
await entry.runtime_data["zeroconf"].stop()
80+
if RT_DISPATCHER in entry.runtime_data.keys():
81+
await entry.runtime_data[RT_DISPATCHER].disconnect()
82+
if RT_ZEROCONF in entry.runtime_data.keys():
83+
await entry.runtime_data[RT_ZEROCONF].stop()
7284

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

@@ -83,10 +95,10 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
8395
return False
8496

8597
if entry.version == 1:
86-
# Move device info into subkey, add with_solar key
98+
# Move device info into subkey
8799
devices = { **entry.data }
88-
new_data = { 'devices': devices, 'with_solar': False, 'with_mains': False, 'roles': {} }
89-
hass.config_entries.async_update_entry(entry, data=new_data, version=2, minor_version=1)
100+
new_data = { CFG_DEVICES: devices, CFG_ROLES: {} }
101+
hass.config_entries.async_update_entry(entry, data=new_data, version=2, minor_version=2)
90102

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

custom_components/powersensor/config_flow.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,24 @@
99
from homeassistant.helpers.service_info import zeroconf
1010
from homeassistant.helpers.selector import selector
1111

12-
from .const import DEFAULT_PORT, DOMAIN, ROLE_UPDATE_SIGNAL, SENSOR_NAME_FORMAT
12+
from .const import (
13+
CFG_DEVICES,
14+
CFG_ROLES,
15+
16+
DEFAULT_PORT,
17+
DOMAIN,
18+
19+
ROLE_APPLIANCE,
20+
ROLE_HOUSENET,
21+
ROLE_SOLAR,
22+
ROLE_WATER,
23+
24+
ROLE_UPDATE_SIGNAL,
25+
26+
RT_DISPATCHER,
27+
28+
SENSOR_NAME_FORMAT,
29+
)
1330

1431
def _extract_device_name(discovery_info) -> str:
1532
"""Extract a user-friendly device name from zeroconf info."""
@@ -53,7 +70,7 @@ def __init__(self):
5370

5471
async def async_step_reconfigure(self, user_input: dict | None = None)->FlowResult:
5572
entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
56-
dispatcher = entry.runtime_data["dispatcher"]
73+
dispatcher = entry.runtime_data[RT_DISPATCHER]
5774

5875
mac2name = { mac: SENSOR_NAME_FORMAT % mac for mac in dispatcher.sensors }
5976

@@ -70,12 +87,13 @@ async def async_step_reconfigure(self, user_input: dict | None = None)->FlowResu
7087

7188
sensor_roles = {}
7289
for sensor_mac in dispatcher.sensors:
73-
role = entry.data.get('roles', {}).get(sensor_mac, unknown)
90+
role = entry.data.get(CFG_ROLES, {}).get(sensor_mac, unknown)
7491
sel = selector({
7592
"select": {
7693
"options": [
7794
# Note: these strings are NOT subject to translation
78-
"house-net", "solar", "water", "appliance", unknown
95+
ROLE_HOUSENET, ROLE_SOLAR, ROLE_WATER, ROLE_APPLIANCE,
96+
unknown
7997
],
8098
"mode": "dropdown",
8199
}
@@ -154,9 +172,8 @@ async def async_step_confirm(self, step_id :str, user_input: dict[str, Any] | No
154172
return self.async_create_entry(
155173
title="Powersensor",
156174
data={
157-
'devices': self.hass.data[DOMAIN]["discovered_plugs"],
158-
'with_solar': False,
159-
'roles': {},
175+
CFG_DEVICES: self.hass.data[DOMAIN]["discovered_plugs"],
176+
CFG_ROLES: {},
160177
}
161178
)
162179
return self.async_show_form(step_id=step_id)
@@ -171,4 +188,4 @@ async def async_step_manual_confirm(
171188
self, user_input: dict[str, Any] | None = None
172189
) -> ConfigFlowResult:
173190

174-
return await self.async_step_confirm(step_id="manual_confirm", user_input=user_input)
191+
return await self.async_step_confirm(step_id="manual_confirm", user_input=user_input)

custom_components/powersensor/const.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,35 @@
55
DEFAULT_PORT = 49476
66
DEFAULT_SCAN_INTERVAL = 30
77

8+
# Internal signals
89
CREATE_PLUG_SIGNAL = f"{DOMAIN}_create_plug"
910
CREATE_SENSOR_SIGNAL = f"{DOMAIN}_create_sensor"
1011
DATA_UPDATE_SIGNAL_FMT_MAC_EVENT = f"{DOMAIN}_data_update_%s_%s"
11-
HAVE_SOLAR_SENSOR_SIGNAL = f"{DOMAIN}_have_solar_sensor"
12-
HAVE_MAINS_SENSOR_SIGNAL = f"{DOMAIN}_have_mains_sensor"
1312
ROLE_UPDATE_SIGNAL = f"{DOMAIN}_update_role"
1413
PLUG_ADDED_TO_HA_SIGNAL = f"{DOMAIN}_plug_added_to_homeassistant"
1514
SENSOR_ADDED_TO_HA_SIGNAL = f"{DOMAIN}_sensor_added_to_homeassistant"
15+
UPDATE_VHH_SIGNAL = f"{DOMAIN}_update_vhh"
1616
ZEROCONF_ADD_PLUG_SIGNAL = f"{DOMAIN}_zeroconf_add_plug"
1717
ZEROCONF_REMOVE_PLUG_SIGNAL = f"{DOMAIN}_zeroconf_remove_plug"
1818
ZEROCONF_UPDATE_PLUG_SIGNAL = f"{DOMAIN}_zeroconf_update_plug"
1919

20-
20+
# Formatting, would've liked to have been able to have this translatable
2121
SENSOR_NAME_FORMAT = "Powersensor Sensor (ID: %s) ⚡"
22+
23+
# Config entry keys
24+
CFG_DEVICES = "devices"
25+
CFG_ROLES = "roles"
26+
27+
# Role names (fixed, as-received from plug API)
28+
ROLE_APPLIANCE = "appliance"
29+
ROLE_HOUSENET = "house-net"
30+
ROLE_SOLAR = "solar"
31+
ROLE_WATER = "water"
32+
33+
# runtime_data keys
34+
RT_DISPATCHER = "dispatcher"
35+
RT_VHH = "vhh"
36+
RT_VHH_LOCK = "vhh_update_lock"
37+
RT_VHH_MAINS_ADDED = "vhh_main_added"
38+
RT_VHH_SOLAR_ADDED = "vhh_solar_added"
39+
RT_ZEROCONF = "zeroconf"

0 commit comments

Comments
 (0)