Skip to content

Commit

Permalink
Add support for export control block
Browse files Browse the repository at this point in the history
The export control block allows control of how much power is exported
from the system to the grid. This may be useful, for example, to prevent
export if the export rate is negative.
  • Loading branch information
mpredfearn committed Sep 21, 2021
1 parent de4ce9e commit ff21b05
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 5 deletions.
43 changes: 40 additions & 3 deletions custom_components/solaredge_modbus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
DEFAULT_READ_BATTERY1,
DEFAULT_READ_BATTERY2,
BATTERY_STATUSSES,
EXPORT_CONTROL_MODE,
EXPORT_CONTROL_LIMIT_MODE,
STOREDGE_CONTROL_MODE,
STOREDGE_AC_CHARGE_POLICY,
STOREDGE_CHARGE_DISCHARGE_MODE
Expand Down Expand Up @@ -191,6 +193,16 @@ def connect(self):
with self._lock:
self._client.connect()

@property
def has_meter(self):
"""Return true if a meter is available"""
return self.read_meter1 or self.read_meter2 or self.read_meter3

@property
def has_battery(self):
"""Return true if a battery is available"""
return self.read_battery1 or self.read_battery2

def read_holding_registers(self, unit, address, count):
"""Read holding registers."""
with self._lock:
Expand Down Expand Up @@ -652,15 +664,40 @@ def read_modbus_data_inverter(self):
return False

def read_modbus_data_storage(self):
if not self.read_battery1 and not self.read_battery2:
return True
if self.has_battery:
count = 0x12 # Read storedge block as well
elif self.has_meter:
count = 4 # Just read export control block
else:
return True # Nothing to read here

storage_data = self.read_holding_registers(unit=1, address=57348, count=14)
storage_data = self.read_holding_registers(unit=1, address=0xE000, count=count)
if not storage_data.isError():
decoder = BinaryPayloadDecoder.fromRegisters(
storage_data.registers, byteorder=Endian.Big,wordorder=Endian.Little
)

#0xE000 - 1 - Export control mode
export_control_mode = decoder.decode_16bit_uint() & 7
if export_control_mode in EXPORT_CONTROL_MODE:
self.data["export_control_mode"] = EXPORT_CONTROL_MODE[export_control_mode]
else:
self.data["export_control_mode"] = export_control_mode

#0xE001 - 1 - Export control limit mode
export_control_limit_mode = decoder.decode_16bit_uint() & 1
if export_control_limit_mode in EXPORT_CONTROL_MODE:
self.data["export_control_limit_mode"] = EXPORT_CONTROL_LIMIT_MODE[export_control_limit_mode]
else:
self.data["export_control_limit_mode"] = export_control_limit_mode

#0xE002 - 2 - Export control site limit
self.data["export_control_site_limit"] = round(decoder.decode_32bit_float(), 3)

if not self.has_battery:
# Done with the export control block
return True

#0xE004 - 1 - storage control mode
storage_control_mode = decoder.decode_16bit_uint()
if storage_control_mode in STOREDGE_CONTROL_MODE:
Expand Down
21 changes: 21 additions & 0 deletions custom_components/solaredge_modbus/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,18 @@
10: "Sleep"
}

EXPORT_CONTROL_MODE = {
0: "Disabled",
1: "Direct Export Limitation",
2: "Indirect Export Limitation",
4: "Production Limitation"
}

EXPORT_CONTROL_LIMIT_MODE = {
0: "Total",
1: "Per phase"
}

STOREDGE_CONTROL_MODE = {
0: "Disabled",
1: "Maximize Self Consumption",
Expand All @@ -308,6 +320,15 @@
7: "Maximize self consumption",
}

EXPORT_CONTROL_SELECT_TYPES = [
["Export control mode", "export_control_mode", 0xE000, EXPORT_CONTROL_MODE],
["Export control limit mode", "export_control_limit_mode", 0xE001, EXPORT_CONTROL_LIMIT_MODE],
]

EXPORT_CONTROL_NUMBER_TYPES = [
["Export control site limit", "export_control_site_limit", 0xE002, "f", {"min": 0, "max": 10000, "unit": "W"}],
]

STORAGE_SELECT_TYPES = [
["Storage Control Mode", "storage_contol_mode", 0xE004, STOREDGE_CONTROL_MODE],
["Storage AC Charge Policy", "storage_ac_charge_policy", 0xE005, STOREDGE_AC_CHARGE_POLICY],
Expand Down
19 changes: 18 additions & 1 deletion custom_components/solaredge_modbus/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .const import (
DOMAIN,
ATTR_MANUFACTURER,
EXPORT_CONTROL_NUMBER_TYPES,
STORAGE_NUMBER_TYPES,
)

Expand Down Expand Up @@ -32,7 +33,23 @@ async def async_setup_entry(hass, entry, async_add_entities) -> None:

entities = []

if hub.read_battery1 == True or hub.read_battery2 == True:
# If a meter is available add export control
if hub.has_meter:
for number_info in EXPORT_CONTROL_NUMBER_TYPES:
number = SolarEdgeNumber(
hub_name,
hub,
device_info,
number_info[0],
number_info[1],
number_info[2],
number_info[3],
number_info[4],
)
entities.append(number)

# If a battery is available add storage control
if hub.has_battery:
for number_info in STORAGE_NUMBER_TYPES:
number = SolarEdgeNumber(
hub_name,
Expand Down
18 changes: 17 additions & 1 deletion custom_components/solaredge_modbus/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .const import (
DOMAIN,
ATTR_MANUFACTURER,
EXPORT_CONTROL_SELECT_TYPES,
STORAGE_SELECT_TYPES,
)

Expand All @@ -29,7 +30,22 @@ async def async_setup_entry(hass, entry, async_add_entities) -> None:

entities = []

if hub.read_battery1 == True or hub.read_battery2 == True:
# If a meter is available add export control
if hub.has_meter:
for select_info in EXPORT_CONTROL_SELECT_TYPES:
select = SolarEdgeSelect(
hub_name,
hub,
device_info,
select_info[0],
select_info[1],
select_info[2],
select_info[3],
)
entities.append(select)

# If a battery is available add storage control
if hub.has_battery:
for select_info in STORAGE_SELECT_TYPES:
select = SolarEdgeSelect(
hub_name,
Expand Down

0 comments on commit ff21b05

Please sign in to comment.