Skip to content

Commit 9a682b3

Browse files
authored
Merge pull request #87 from RustyDust/sru_work
Cleanups and fixes:
2 parents 538e3fd + 493a185 commit 9a682b3

File tree

12 files changed

+190
-113
lines changed

12 files changed

+190
-113
lines changed

custom_components/sonnenbatterie/__init__.py

Lines changed: 91 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,10 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
100100
hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR] = sb_coordinator
101101

102102
inverter_power = sb_coordinator.latestData['battery_system']['battery_system']['system']['inverter_capacity']
103-
LOGGER.debug(f"inverter_power: {inverter_power}")
103+
max_tou_power = sb_coordinator.latestData.get('commissioning_settings',{}).get('data', {}).get('attributes', {}).get('tou_max_power_limit', '22000')
104+
LOGGER.debug(f"inverter_power: {inverter_power}, tou_power: {max_tou_power}")
105+
hass.data[DOMAIN][config_entry.entry_id][CONF_INVERTER_MAX] = inverter_power
106+
hass.data[DOMAIN][config_entry.entry_id][CONF_TOU_MAX] = int(max_tou_power)
104107

105108
# noinspection PyPep8Naming
106109
SCHEMA_CHARGE_BATTERY = vol.Schema(
@@ -114,97 +117,97 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
114117
# Initialize our services
115118
services = SonnenbatterieService(hass, config_entry, sb_coordinator)
116119

117-
# Set up base data in hass object
118-
# hass.data.setdefault(DOMAIN, {})
119-
hass.data[DOMAIN][config_entry.entry_id][CONF_INVERTER_MAX] = inverter_power
120-
121120
# Setup our sensors, services and whatnot
122121
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
123122

124-
# service registration
125-
hass.services.async_register(
126-
DOMAIN,
127-
"charge_battery",
128-
services.charge_battery,
129-
schema=SCHEMA_CHARGE_BATTERY,
130-
supports_response=SupportsResponse.OPTIONAL,
131-
)
132-
133-
hass.services.async_register(
134-
DOMAIN,
135-
"discharge_battery",
136-
services.discharge_battery,
137-
schema=SCHEMA_CHARGE_BATTERY,
138-
supports_response=SupportsResponse.OPTIONAL,
139-
)
140-
141-
hass.services.async_register(
142-
DOMAIN,
143-
"set_battery_reserve",
144-
services.set_battery_reserve,
145-
schema=SCHEMA_SET_BATTERY_RESERVE,
146-
supports_response=SupportsResponse.OPTIONAL,
147-
)
148-
149-
hass.services.async_register(
150-
DOMAIN,
151-
"set_config_item",
152-
services.set_config_item,
153-
schema=SCHEMA_SET_CONFIG_ITEM,
154-
supports_response=SupportsResponse.OPTIONAL,
155-
)
156-
157-
hass.services.async_register(
158-
DOMAIN,
159-
"set_operating_mode",
160-
services.set_operating_mode,
161-
schema=SCHEMA_SET_OPERATING_MODE,
162-
supports_response=SupportsResponse.OPTIONAL,
163-
)
164-
165-
hass.services.async_register(
166-
DOMAIN,
167-
"set_operating_mode_num",
168-
services.set_operating_mode_num,
169-
schema=SCHEMA_SET_OPERATING_MODE_NUM,
170-
supports_response=SupportsResponse.OPTIONAL,
171-
)
172-
173-
hass.services.async_register(
174-
DOMAIN,
175-
"set_tou_schedule",
176-
services.set_tou_schedule,
177-
schema=SCHEMA_SET_TOU_SCHEDULE_STRING,
178-
supports_response=SupportsResponse.OPTIONAL,
179-
)
180-
181-
hass.services.async_register(
182-
DOMAIN,
183-
"get_tou_schedule",
184-
services.get_tou_schedule,
185-
supports_response=SupportsResponse.OPTIONAL,
186-
)
187-
188-
hass.services.async_register(
189-
DOMAIN,
190-
"get_battery_reserve",
191-
services.get_battery_reserve,
192-
supports_response=SupportsResponse.OPTIONAL,
193-
)
194-
195-
hass.services.async_register(
196-
DOMAIN,
197-
"get_operating_mode",
198-
services.get_operating_mode,
199-
supports_response=SupportsResponse.OPTIONAL,
200-
)
123+
if sb_coordinator.latestData.get('api_configuration',{}).get('IN_LocalAPIWriteActive', '0') == '1':
124+
# service registration
125+
hass.services.async_register(
126+
DOMAIN,
127+
"charge_battery",
128+
services.charge_battery,
129+
schema=SCHEMA_CHARGE_BATTERY,
130+
supports_response=SupportsResponse.OPTIONAL,
131+
)
132+
133+
hass.services.async_register(
134+
DOMAIN,
135+
"discharge_battery",
136+
services.discharge_battery,
137+
schema=SCHEMA_CHARGE_BATTERY,
138+
supports_response=SupportsResponse.OPTIONAL,
139+
)
140+
141+
hass.services.async_register(
142+
DOMAIN,
143+
"set_battery_reserve",
144+
services.set_battery_reserve,
145+
schema=SCHEMA_SET_BATTERY_RESERVE,
146+
supports_response=SupportsResponse.OPTIONAL,
147+
)
148+
149+
hass.services.async_register(
150+
DOMAIN,
151+
"set_config_item",
152+
services.set_config_item,
153+
schema=SCHEMA_SET_CONFIG_ITEM,
154+
supports_response=SupportsResponse.OPTIONAL,
155+
)
156+
157+
hass.services.async_register(
158+
DOMAIN,
159+
"set_operating_mode",
160+
services.set_operating_mode,
161+
schema=SCHEMA_SET_OPERATING_MODE,
162+
supports_response=SupportsResponse.OPTIONAL,
163+
)
164+
165+
hass.services.async_register(
166+
DOMAIN,
167+
"set_operating_mode_num",
168+
services.set_operating_mode_num,
169+
schema=SCHEMA_SET_OPERATING_MODE_NUM,
170+
supports_response=SupportsResponse.OPTIONAL,
171+
)
172+
173+
hass.services.async_register(
174+
DOMAIN,
175+
"set_tou_schedule",
176+
services.set_tou_schedule,
177+
schema=SCHEMA_SET_TOU_SCHEDULE_STRING,
178+
supports_response=SupportsResponse.OPTIONAL,
179+
)
180+
181+
hass.services.async_register(
182+
DOMAIN,
183+
"get_tou_schedule",
184+
services.get_tou_schedule,
185+
supports_response=SupportsResponse.OPTIONAL,
186+
)
187+
188+
hass.services.async_register(
189+
DOMAIN,
190+
"get_battery_reserve",
191+
services.get_battery_reserve,
192+
supports_response=SupportsResponse.OPTIONAL,
193+
)
194+
195+
hass.services.async_register(
196+
DOMAIN,
197+
"get_operating_mode",
198+
services.get_operating_mode,
199+
supports_response=SupportsResponse.OPTIONAL,
200+
)
201+
202+
hass.services.async_register(
203+
DOMAIN,
204+
"get_operating_mode_num",
205+
services.get_operating_mode_num,
206+
supports_response=SupportsResponse.OPTIONAL,
207+
)
201208

202-
hass.services.async_register(
203-
DOMAIN,
204-
"get_operating_mode_num",
205-
services.get_operating_mode_num,
206-
supports_response=SupportsResponse.OPTIONAL,
207-
)
209+
else:
210+
LOGGER.info(f"JSON-API write access not enabled - disabling SERVICE functions")
208211

209212
# Done setting up the entry
210213
return True

custom_components/sonnenbatterie/button.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, asyn
1313
LOGGER.debug(f"BUTTON async_setup_entry - {config_entry}")
1414
coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]
1515

16-
entities = []
17-
for description in BUTTON_ENTITIES:
18-
if description.tag.type == Platform.BUTTON:
19-
entity = SonnenbatterieButton(coordinator, description)
20-
entities.append(entity)
21-
22-
async_add_entities(entities)
16+
if coordinator.latestData.get('api_configuration', {}).get('IN_LocalAPIWriteActive', '0') == '1':
17+
entities = []
18+
for description in BUTTON_ENTITIES:
19+
if description.tag.type == Platform.BUTTON:
20+
entity = SonnenbatterieButton(coordinator, description)
21+
entities.append(entity)
22+
23+
async_add_entities(entities)
24+
else:
25+
LOGGER.info(f"JSON-API write access not enabled - disabling BUTTON functions")
2326

2427
class SonnenbatterieButton(SonnenButtonEntity, ButtonEntity):
2528

custom_components/sonnenbatterie/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
CONF_CHARGE_WATT = "power"
3838
CONF_COORDINATOR = "coordinator"
3939
CONF_INVERTER_MAX = "inverter_max"
40+
CONF_TOU_MAX = "tou_max"
4041
CONF_SERVICE_ITEM = "item"
4142
CONF_SERVICE_MODE = "mode"
4243
CONF_SERVICE_SCHEDULE = "schedule"

custom_components/sonnenbatterie/coordinator.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ async def _async_update_data(self):
117117
result = await self.sbconn.sb2.get_configurations()
118118
self.latestData["configurations"] = result
119119

120+
result = await self.sbconn.get_api_configuration()
121+
self.latestData["api_configuration"] = result
122+
123+
result = await self.sbconn.get_commissioning_settings()
124+
self.latestData["commissioning_settings"] = result
125+
120126
self._last_error = None
121127

122128
except Exception as e:
@@ -165,4 +171,5 @@ def send_all_data_to_log(self):
165171
LOGGER.warning(f"System data:\n{self.latestData['system_data']}")
166172
LOGGER.warning(f"Status:\n{self.latestData['status']}")
167173
LOGGER.warning(f"Battery:\n{self.latestData['battery']}")
174+
LOGGER.warning(f"API-Config:\n{self.latestData['api_configuration']}")
168175
self._fullLogsAlreadySent = True

custom_components/sonnenbatterie/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
"documentation": "https://github.com/weltmeyer/ha_sonnenbatterie",
88
"iot_class": "local_polling",
99
"issue_tracker": "https://github.com/weltmeyer/ha_sonnenbatterie/issues",
10-
"requirements": ["requests","sonnenbatterie>=0.5.2"],
11-
"version": "2025.02.01"
10+
"requirements": ["requests","sonnenbatterie>=0.6.0"],
11+
"version": "2025.02.02"
1212
}

custom_components/sonnenbatterie/number.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,27 @@
1212
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None:
1313
LOGGER.debug(f"NUMBER - async_setup_entry: {config_entry}")
1414
coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]
15-
await coordinator.async_refresh()
15+
# await coordinator.async_refresh()
1616

17-
max_power = int(hass.data[DOMAIN][config_entry.entry_id][CONF_INVERTER_MAX])
18-
entities = []
19-
for description in NUMBER_ENTITIES:
20-
if description.tag.type == Platform.NUMBER:
21-
entity = SonnenbatterieNumber(coordinator, description, max_power)
22-
entities.append(entity)
23-
async_add_entities(entities)
17+
if coordinator.latestData.get('api_configuration',{}).get('IN_LocalAPIWriteActive', '0') == '1':
18+
19+
max_power = int(hass.data[DOMAIN][config_entry.entry_id][CONF_INVERTER_MAX])
20+
entities = []
21+
for description in NUMBER_ENTITIES:
22+
if description.tag.type == Platform.NUMBER:
23+
entity = SonnenbatterieNumber(coordinator, description, max_power)
24+
entities.append(entity)
25+
async_add_entities(entities)
26+
27+
else:
28+
LOGGER.info(f"JSON-API write access not enabled - disabling NUMBER functions")
2429

2530
class SonnenbatterieNumber(SonnenNumberEntity, NumberEntity):
2631
_attr_native_value: int = 0
2732

2833
def __init__(self, coordinator: SonnenbatterieCoordinator, description: SonnenbatterieNumberEntityDescription, max_power: int) -> None:
2934
super().__init__(coordinator, description)
30-
LOGGER.debug(f"SonnenbatterieNumberEntity: {description}")
35+
# LOGGER.debug(f"SonnenbatterieNumberEntity: {description}")
3136
if description.key == "battery_reserve":
3237
self._max_power = 100
3338
else:

custom_components/sonnenbatterie/select.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,19 @@ async def async_setup_entry(
1515
) -> None:
1616
LOGGER.debug(f"SELECT async_setup_entry: {config_entry}")
1717
coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]
18-
await coordinator.async_refresh()
1918

20-
entities = []
21-
for description in SELECT_ENTITIES:
22-
entity = SonnenBatterieSelect(coordinator, description)
23-
entities.append(entity)
19+
if coordinator.latestData.get('api_configuration',{}).get('IN_LocalAPIWriteActive', '0') == '1':
20+
# await coordinator.async_refresh()
2421

25-
async_entity_cb(entities) if len(entities) > 0 else None
22+
entities = []
23+
for description in SELECT_ENTITIES:
24+
entity = SonnenBatterieSelect(coordinator, description)
25+
entities.append(entity)
2626

27+
async_entity_cb(entities) if len(entities) > 0 else None
28+
29+
else:
30+
LOGGER.info(f"JSON-API write access not enabled - disabling SELECT functions")
2731

2832
class SonnenBatterieSelect(SonnenSelectEntity, SelectEntity):
2933
def __init__(self, coordinator: SonnenbatterieCoordinator, description: SonnenbatterieSelectEntityDescription):

custom_components/sonnenbatterie/sensor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
3232
LOGGER.debug(f"SENSOR async_setup_entry - {config_entry.data}")
3333
coordinator = hass.data[DOMAIN][config_entry.entry_id][CONF_COORDINATOR]
3434

35-
await coordinator.async_refresh()
35+
# await coordinator.async_refresh()
3636

3737
async_add_entities(
3838
SonnenbatterieSensor(coordinator=coordinator, entity_description=description)

custom_components/sonnenbatterie/sensor_list.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,4 +517,34 @@ def generate_powermeter_sensors(_coordinator):
517517
.get("tmax"),
518518
entity_registry_enabled_default=False,
519519
),
520+
###########################
521+
### -- advanced sensors ###
522+
###
523+
SonnenbatterieSensorEntityDescription(
524+
key="read_api",
525+
icon="mdi:alpha-r-circle-outline",
526+
state_class=SensorStateClass.MEASUREMENT,
527+
entity_category=EntityCategory.DIAGNOSTIC,
528+
value_fn=lambda coordinator: coordinator.latestData.get("api_configuration", {})
529+
.get('IN_LocalAPIReadActive', '0') == '1',
530+
entity_registry_enabled_default=True,
531+
),
532+
SonnenbatterieSensorEntityDescription(
533+
key="write_api",
534+
icon="mdi:alpha-w-circle-outline",
535+
state_class=SensorStateClass.MEASUREMENT,
536+
entity_category=EntityCategory.DIAGNOSTIC,
537+
value_fn= lambda coordinator: coordinator.latestData.get("api_configuration", {})
538+
.get('IN_LocalAPIWriteActive', '0') == '1',
539+
entity_registry_enabled_default = True,
540+
),
541+
SonnenbatterieSensorEntityDescription(
542+
key="tou_max_power",
543+
icon="mdi:transmission-tower-import",
544+
state_class=SensorStateClass.MEASUREMENT,
545+
entity_category=EntityCategory.DIAGNOSTIC,
546+
value_fn=lambda coordinator: coordinator.latestData.get("commissioning_settings", {})
547+
.get('data', {}).get('attributes',{}).get('tou_max_power_limit', 'unknown'),
548+
entity_registry_enabled_default=True,
549+
)
520550
)

custom_components/sonnenbatterie/service.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
CONF_SERVICE_ITEM,
1515
CONF_SERVICE_SCHEDULE,
1616
CONF_SERVICE_VALUE,
17+
CONF_TOU_MAX,
1718
DOMAIN,
1819
LOGGER,
1920
SB_OPERATING_MODES,
@@ -27,6 +28,7 @@ def __init__(self, hass, config, coordinator):
2728
self._hass = hass
2829
self._config = config
2930
self._coordinator = coordinator
31+
self._tou_max = hass.data[DOMAIN][config.entry_id][CONF_TOU_MAX]
3032

3133
def _get_sb_connection(self, call_data: ReadOnlyDict) -> AsyncSonnenBatterie:
3234
LOGGER.debug(f"_get_sb_connection: {call_data}")
@@ -112,6 +114,10 @@ async def set_tou_schedule(self, call: ServiceCall) -> ServiceResponse:
112114
except ValueError as e:
113115
raise HomeAssistantError(f"Schedule is not a valid JSON string: '{schedule}'") from e
114116

117+
if json_schedule['threshold_p_max'] > self._tou_max:
118+
LOGGER.warning(f"Specified 'threshold_p_max' exceeds configured limit of {self._tou_max}, value capped to {json_schedule['threshold_p_max']}")
119+
json_schedule['threshold_p_max'] = self._tou_max
120+
115121
tou = TimeofUseSchedule()
116122
try:
117123
tou.load_tou_schedule_from_json(json_schedule)

0 commit comments

Comments
 (0)