Skip to content
13 changes: 12 additions & 1 deletion packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

class UpdateConfig:

DATASTORE_VERSION = 97
DATASTORE_VERSION = 98

valid_topic = [
"^openWB/bat/config/bat_control_permitted$",
Expand Down Expand Up @@ -2572,3 +2572,14 @@ def upgrade_datastore_96(self) -> None:
MessageType.INFO,
)
self.__update_topic("openWB/system/datastore_version", 97)

def upgrade_datastore_97(self) -> None:
def upgrade(topic: str, payload) -> None:
if re.search("openWB/system/device/[0-9]+/config$", topic) is not None:
payload = decode_payload(payload)
# add phase
if payload.get("type") == "shelly" and "phase" not in payload["configuration"]:
payload["configuration"].update({"phase": 1})
Pub().pub(topic, payload)
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 98)
68 changes: 43 additions & 25 deletions packages/modules/devices/shelly/shelly/bat.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class KwargsDict(TypedDict):
device_id: int
ip_address: str
factor: int
phase: int
generation: Optional[int]


Expand All @@ -29,6 +30,7 @@ def initialize(self) -> None:
self.__device_id: int = self.kwargs['device_id']
self.address: str = self.kwargs['ip_address']
self.factor: int = self.kwargs['factor']
self.phase: int = self.kwargs['phase']
self.generation: Optional[int] = self.kwargs['generation']
self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher")
self.store = get_bat_value_store(self.component_config.id)
Expand All @@ -43,38 +45,54 @@ def update(self) -> None:
status = req.get_http_session().get(status_url, timeout=3).json()

try:
if self.generation == 1:
if 'meters' in status:
meters = status['meters'] # shelly
else:
meters = status['emeters'] # shellyEM & shelly3EM
# shellyEM has one meter, shelly3EM has three meters:
for meter in meters:
power = power + meter['power']
currents = [0, 0, 0]
alphabetical_index = ['a', 'b', 'c']
currents = [0.0, 0.0, 0.0]
# GEN 1
if "meters" in status:
meters = status['meters'] # einphasiger shelly?
for i in range(len(meters)):
currents[(i+self.phase-1) % 3] = ((float(meters[i]['power']) * self.factor) / 230
if meters[i].get('power') else 0)
power = power + (float(meters[i]['power'] * self.factor))
elif "emeters" in status:
meters = status['emeters'] # shellyEM & shelly3EM
# shellyEM has one meter, shelly3EM has three meters
for i in range(len(meters)):
currents[(i+self.phase-1) % 3] = (float(meters[i]['current']) * self.factor
if meters[i].get('current') else 0)
power = power + (float(meters[i]['power'] * self.factor))
# GEN 2+
# shelly Pro3EM
elif "em:0" in status:
meters = status['em:0']
for i in range(0, 3):
currents[(i+self.phase-1) % 3] = (float(meters[f'{alphabetical_index[i]}_current']) * self.factor
if meters.get(f'{alphabetical_index[i]}_current') else 0)
power = float(meters['total_act_power']) * self.factor
# Shelly MiniPM G3
elif "pm1:0" in status:
log.debug("single phase shelly")
meters = status['pm1:0']
currents[self.phase-1] = meters['current'] * self.factor
power = meters['apower'] * self.factor
elif 'switch:0' in status and 'apower' in status['switch:0']:
log.debug("single phase shelly")
meters = status['switch:0']
currents[self.phase-1] = meters['current'] * self.factor
power = meters['apower'] * self.factor
else:
if 'switch:0' in status and 'apower' in status['switch:0']:
power = status['switch:0']['apower']
currents = [status['switch:0']['current'], 0, 0]
elif 'em1:0' in status:
power = status['em1:0']['act_power'] # shelly Pro EM Gen 2
currents = [status['em1:0']['current'], 0, 0]
elif 'pm1:0' in status:
power = status['pm1:0']['apower'] # shelly PM Mini Gen 3
currents = [status['pm1:0']['current'], 0, 0]
else:
power = status['em:0']['total_act_power'] # shelly Pro3EM
currents = [status['em:0'][f'{i}_current'] for i in 'abc']

power = power * self.factor
log.debug("single phase shelly")
meters = status['em1:0']
currents[self.phase-1] = meters['current'] * self.factor
power = meters['act_power'] * self.factor # shelly Pro EM Gen 2
imported, exported = self.sim_counter.sim_count(power)

bat_state = BatState(
power=power,
currents=currents,
imported=imported,
exported=exported
)
if 'currents' in locals():
bat_state.currents = currents
self.store.set(bat_state)
except KeyError:
log.exception("unsupported shelly device.")
Expand Down
3 changes: 2 additions & 1 deletion packages/modules/devices/shelly/shelly/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

@auto_str
class ShellyConfiguration:
def __init__(self, ip_address: Optional[str] = None, factor: Optional[int] = -1):
def __init__(self, ip_address: Optional[str] = None, factor: Optional[int] = -1, phase: Optional[int] = 1):
self.ip_address = ip_address
self.factor = factor
self.phase = phase


@auto_str
Expand Down
141 changes: 92 additions & 49 deletions packages/modules/devices/shelly/shelly/counter.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class KwargsDict(TypedDict):
device_id: int
ip_address: str
factor: int
phase: int
generation: Optional[int]


Expand All @@ -29,6 +30,7 @@ def initialize(self) -> None:
self.__device_id: int = self.kwargs['device_id']
self.address: str = self.kwargs['ip_address']
self.factor: int = self.kwargs['factor']
self.phase: int = self.kwargs['phase']
self.generation: Optional[int] = self.kwargs['generation']
self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug")
self.store = get_counter_value_store(self.component_config.id)
Expand All @@ -42,69 +44,110 @@ def update(self) -> None:
status_url = "http://" + self.address + "/rpc/Shelly.GetStatus"
status = req.get_http_session().get(status_url, timeout=3).json()
try:
if self.generation == 1: # shelly3EM
meters = status['emeters']
# shelly3EM has three meters:
for meter in meters:
power = power + meter['power']
power = power * self.factor
# GEN 1
alphabetical_index = ['a', 'b', 'c']
if "meters" in status:
powers = [0.0, 0.0, 0.0]
voltages = [0.0, 0.0, 0.0]
meters = status['meters'] # einphasiger shelly?
for i in range(len(meters)):
powers[(i+self.phase-1) % 3] = (float(meters[i]['power']) * self.factor
if meters[i].get('power') else 0)
voltages[(i+self.phase-1) % 3] = 230
power = sum(powers)
elif "emeters" in status:
powers = [0.0, 0.0, 0.0]
currents = [0.0, 0.0, 0.0]
voltages = [0.0, 0.0, 0.0]
power_factors = [0.0, 0.0, 0.0]
meters = status['emeters'] # shellyEM & shelly3EM
# shellyEM has one meter, shelly3EM has three meters
for i in range(len(meters)):
powers[(i+self.phase-1) % 3] = (float(meters[i]['power']) * self.factor
if meters[i].get('power') else 0)
currents[(i+self.phase-1) % 3] = (float(meters[i]['current']) * self.factor
if meters[i].get('current') else 0)
voltages[(i+self.phase-1) % 3] = (float(meters[i]['voltage'])
if meters[i].get('voltage') else 0)
power_factors[(i+self.phase-1) % 3] = (float(meters[i]['pf'])
if meters[i].get('pf') else 0)
power = sum(powers)

voltages = [status['emeters'][i]['voltage'] for i in range(0, 3)]
currents = [status['emeters'][i]['current'] for i in range(0, 3)]
powers = [status['emeters'][i]['power'] for i in range(0, 3)]
power_factors = [status['emeters'][i]['pf'] for i in range(0, 3)]
# GEN 2+
# shelly Pro3EM
elif "em:0" in status:
powers = [0.0, 0.0, 0.0]
currents = [0.0, 0.0, 0.0]
voltages = [0.0, 0.0, 0.0]
power_factors = [0.0, 0.0, 0.0]
meters = status['em:0']
for i in range(0, 3):
powers[(i+self.phase-1) % 3] = (float(meters[f'{alphabetical_index[i]}_act_power']) * self.factor
if meters.get(f'{alphabetical_index[i]}_act_power') else 0)
voltages[(i+self.phase-1) % 3] = (float(meters[f'{alphabetical_index[i]}_voltage'])
if meters.get(f'{alphabetical_index[i]}_voltage') else 0)
currents[(i+self.phase-1) % 3] = (float(meters[f'{alphabetical_index[i]}_current']) * self.factor
if meters.get(f'{alphabetical_index[i]}_current') else 0)
power_factors[(i+self.phase-1) % 3] = (float(meters[f'{alphabetical_index[i]}_pf'])
if meters.get(f'{alphabetical_index[i]}_pf') else 0)
power = float(meters['total_act_power']) * self.factor
# Shelly MiniPM G3
elif "pm1:0" in status:
log.debug("single phase shelly")
powers = [0.0, 0.0, 0.0]
currents = [0.0, 0.0, 0.0]
voltages = [0.0, 0.0, 0.0]
power_factors = [0.0, 0.0, 0.0]
meters = status['pm1:0']
powers[self.phase-1] = meters['apower'] * self.factor
voltages[self.phase-1] = meters['voltage']
currents[self.phase-1] = meters['current'] * self.factor
power_factors[self.phase-1] = meters['pf'] if meters.get('pf') else 0
power = meters['apower'] * self.factor
frequency = meters['freq']
elif 'switch:0' in status and 'apower' in status['switch:0']:
log.debug("single phase shelly")
powers = [0.0, 0.0, 0.0]
currents = [0.0, 0.0, 0.0]
voltages = [0.0, 0.0, 0.0]
power_factors = [0.0, 0.0, 0.0]
meters = status['switch:0']
powers[self.phase-1] = meters['apower'] * self.factor
voltages[self.phase-1] = meters['voltage']
currents[self.phase-1] = meters['current'] * self.factor
# power_factors[self.phase-1] = meters['pf']
power = meters['apower'] * self.factor
frequency = meters['freq']
else:
# shelly Pro3EM
if "em:0" in status:
meter = status['em:0']
voltages = [meter[f'{i}_voltage'] for i in 'abc']
currents = [meter[f'{i}_current'] for i in 'abc']
powers = [meter[f'{i}_act_power'] for i in 'abc']
power_factors = [meter[f'{i}_pf'] for i in 'abc']
power = meter['total_act_power'] * self.factor
# Shelly MiniPM G3
elif "pm1:0" in status:
log.debug("single phase shelly")
meter = status['pm1:0']
voltages = [meter['voltage'], 0, 0]
currents = [meter['current'], 0, 0]
power = meter['apower']
frequency = meter['freq']
powers = [meter['apower'], 0, 0]
elif 'switch:0' in status and 'apower' in status['switch:0']:
log.debug("single phase shelly")
meter = status['switch:0']
power = meter['apower']
voltages = [meter['voltage'], 0, 0]
currents = [meter['current'], 0, 0]
frequency = meter['freq']
power_factors = [meter['pf'], 0, 0]
powers = [meter['apower'], 0, 0]
else:
log.debug("single phase shelly")
meter = status['em1:0']
power = meter['act_power'] # shelly Pro EM Gen 2
voltages = [meter['voltage'], 0, 0]
currents = [meter['current'], 0, 0]
frequency = meter['freq']
power_factors = [meter['pf'], 0, 0]
powers = [meter['act_power'], 0, 0]
log.debug("single phase shelly")
powers = [0.0, 0.0, 0.0]
currents = [0.0, 0.0, 0.0]
voltages = [0.0, 0.0, 0.0]
power_factors = [0.0, 0.0, 0.0]
meters = status['em1:0']
powers[self.phase-1] = meters['act_power']
voltages[self.phase-1] = meters['voltage']
currents[self.phase-1] = meters['current'] * self.factor
power_factors[self.phase-1] = meters['pf']
power = meters['act_power'] # shelly Pro EM Gen 2
frequency = meters['freq']

imported, exported = self.sim_counter.sim_count(power)

counter_state = CounterState(
voltages=voltages,
currents=currents,
imported=imported,
exported=exported,
powers=powers,
power=power
)
if 'frequency' in locals():
counter_state.frequency = frequency
if "power_factors" in locals():
counter_state.power_factors = power_factors
if "powers" in locals():
counter_state.powers = powers
if "voltages" in locals():
counter_state.voltages = voltages
if "currents" in locals():
counter_state.currents = currents
self.store.set(counter_state)
except KeyError:
log.exception("unsupported shelly device?")
Expand Down
3 changes: 3 additions & 0 deletions packages/modules/devices/shelly/shelly/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def create_counter_component(component_config: ShellyCounterSetup) -> ShellyCoun
device_id=device_config.id,
ip_address=device_config.configuration.ip_address,
factor=device_config.configuration.factor,
phase=device_config.configuration.phase,
generation=generation)

def create_inverter_component(component_config: ShellyInverterSetup) -> ShellyInverter:
Expand All @@ -30,6 +31,7 @@ def create_inverter_component(component_config: ShellyInverterSetup) -> ShellyIn
device_id=device_config.id,
ip_address=device_config.configuration.ip_address,
factor=device_config.configuration.factor,
phase=device_config.configuration.phase,
generation=generation)

def create_bat_component(component_config: ShellyBatSetup) -> ShellyBat:
Expand All @@ -38,6 +40,7 @@ def create_bat_component(component_config: ShellyBatSetup) -> ShellyBat:
device_id=device_config.id,
ip_address=device_config.configuration.ip_address,
factor=device_config.configuration.factor,
phase=device_config.configuration.phase,
generation=generation)

def initializer() -> None:
Expand Down
Loading