Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions packages/modules/common/configurable_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,25 @@ def update(self):
with SingleComponentUpdateContext(self.fault_state):
if self.module_updater is not None:
consumer_state = self.module_updater()
imported, exported = self.peak_filter.check_values(
consumer_state.power,
consumer_state.imported,
consumer_state.exported)
if consumer_state.imported is None or consumer_state.exported is None:
simulated_imported, simulated_exported = self.sim_counter.sim_count(consumer_state.power)
if consumer_state.power is None:
consumer_state.power = 0
if consumer_state.imported is None:
imported = simulated_imported
consumer_state.imported = 0
if consumer_state.exported is None:
exported = simulated_exported
consumer_state.imported = imported
consumer_state.exported = exported
consumer_state.exported = 0
else:
imported, exported = self.peak_filter.check_values(
consumer_state.power,
consumer_state.imported,
consumer_state.exported)
if consumer_state.imported is None or consumer_state.exported is None:
simulated_imported, simulated_exported = self.sim_counter.sim_count(consumer_state.power)
if consumer_state.imported is None:
imported = simulated_imported
if consumer_state.exported is None:
exported = simulated_exported
consumer_state.imported = imported
consumer_state.exported = exported
self.store.set(consumer_state)

def set_power_limit(self, power_limit: float, data: SetLimitData) -> None:
Expand Down
11 changes: 8 additions & 3 deletions packages/modules/consumers/shelly/shelly_em/consumer.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
#!/usr/bin/env python3
from typing import Optional
from requests.auth import AuthBase
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.configurable_consumer import ConfigurableConsumer
from modules.consumers.shelly.shelly_em.config import ShellyEM
from modules.devices.shelly.shelly.status_handler import get_generation, request_status, parse_data, parse_temperatures
from modules.devices.shelly.shelly.status_handler import (
get_auth, get_generation, request_status, parse_data, parse_temperatures)


def create_consumer(config: ShellyEM):
generation: int = 1
auth: Optional[AuthBase] = None

def initializer():
nonlocal generation
nonlocal generation, auth
generation, _ = get_generation(config.configuration.ip_address)
auth = get_auth(generation, config.configuration.username, config.configuration.password)

def error_handler() -> None:
initializer()

def update() -> ConsumerState:
status = request_status(config.configuration.ip_address, generation)
status = request_status(config.configuration.ip_address, generation, auth=auth)
powers, voltages, currents, _, power, _ = parse_data(1, config.configuration.factor, status)
consumer_state = ConsumerState(
power=power,
Expand Down
18 changes: 7 additions & 11 deletions packages/modules/consumers/shelly/shelly_pm/consumer.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
#!/usr/bin/env python3
from modules.common import req
from typing import Optional
from requests.auth import AuthBase
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.configurable_consumer import ConfigurableConsumer
from modules.consumers.shelly.shelly_pm.config import ShellyPM
from modules.devices.shelly.shelly.status_handler import get_generation, request_status, parse_data, parse_temperatures
from modules.devices.shelly.shelly.status_handler import (
get_auth, get_generation, request_status, parse_data, parse_temperatures)


def create_consumer(config: ShellyPM):
model: Optional[str] = None
generation: int = 1
auth: Optional[AuthBase] = None

def initializer():
nonlocal generation, model
nonlocal generation, model, auth
generation, model = get_generation(config.configuration.ip_address)
auth = get_auth(generation, config.configuration.username, config.configuration.password)

def error_handler() -> None:
initializer()
Expand All @@ -31,10 +35,6 @@ def switch_on() -> None:
chan = config.configuration.channel
# gen 2 will das als on cmd /rpc/Switch.Set?id=100&on=true
url = f"http://{config.configuration.ip_address}/rpc/Switch.Set?id={chan}&on=true"
if config.configuration.username and config.configuration.password:
auth = (config.configuration.username, config.configuration.password)
else:
auth = None
req.get_http_session().get(url, auth=auth, timeout=3)

def switch_off() -> None:
Expand All @@ -49,14 +49,10 @@ def switch_off() -> None:
chan = config.configuration.channel
# gen 2 will das als on cmd /rpc/Switch.Set?id=100&on=true
url = f"http://{config.configuration.ip_address}/rpc/Switch.Set?id={chan}&on=false"
if config.configuration.username and config.configuration.password:
auth = (config.configuration.username, config.configuration.password)
else:
auth = None
req.get_http_session().get(url, auth=auth, timeout=3)

def update() -> ConsumerState:
status = request_status(config.configuration.ip_address, generation)
status = request_status(config.configuration.ip_address, generation, auth=auth)
powers, voltages, currents, _, power, _ = parse_data(1, config.configuration.factor, status)
return ConsumerState(
power=power,
Expand Down
32 changes: 32 additions & 0 deletions packages/modules/consumers/shelly/shelly_relais/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Optional, Tuple

from control.consumer.consumer_data import ConsumerUsage
from helpermodules.auto_str import auto_str
from modules.common.consumer_setup import ConsumerSetup
from ..vendor import vendor_descriptor


@auto_str
class ShellyConfiguration:
def __init__(self,
ip_address: Optional[str] = None,
channel: int = 0,
username: Optional[str] = None,
password: Optional[str] = None) -> None:
self.ip_address = ip_address
self.channel = channel
self.username = username
self.password = password


@auto_str
class ShellyRelais(ConsumerSetup[ShellyConfiguration]):
def __init__(self,
name: str = "Shelly Relais (Schalten)",
type: str = "shelly_relais",
id: int = 0,
configuration: Optional[ShellyConfiguration] = None,
usage: Tuple[ConsumerUsage, ...] = (ConsumerUsage.SUSPENDABLE_ONOFF,),
**kwargs) -> None:
super().__init__(name, type, id, vendor=vendor_descriptor.configuration_factory(
).type, configuration=configuration or ShellyConfiguration(), usage=usage, **kwargs)
70 changes: 70 additions & 0 deletions packages/modules/consumers/shelly/shelly_relais/consumer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
from modules.common import req
from typing import Optional, Dict
from requests.auth import AuthBase
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.component_type import ComponentType
from modules.common.configurable_consumer import ConfigurableConsumer
from modules.common.simcount._simcounter import SimCounterConsumer
from modules.consumers.shelly.shelly_relais.config import ShellyRelais
from modules.devices.shelly.shelly.status_handler import get_auth, get_generation, request_status


def create_consumer(config: ShellyRelais):
sim_counter: Optional[SimCounterConsumer] = None
model: Optional[str] = None
generation: int = 1
auth: Optional[AuthBase] = None

def initializer():
nonlocal sim_counter, generation, model, auth
sim_counter = SimCounterConsumer(config.id, ComponentType.CONSUMER)
generation, model = get_generation(config.configuration.ip_address)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beim Endpunkt /shelly braucht man bei beiden Generationen kein Auth.

auth = get_auth(generation, config.configuration.username, config.configuration.password)

def error_handler() -> None:
initializer()

def switch_on() -> None:
if generation == 1:
url = f"http://{config.configuration.ip_address}/relay/{config.configuration.channel}?turn=on"
else:
chan = config.configuration.channel
# gen 2 will das als on cmd /rpc/Switch.Set?id=100&on=true
url = f"http://{config.configuration.ip_address}/rpc/Switch.Set?id={chan}&on=true"
req.get_http_session().get(url, auth=auth, timeout=3)

def switch_off() -> None:
if generation == 1:
url = f"http://{config.configuration.ip_address}/relay/{config.configuration.channel}?turn=off"
else:
chan = config.configuration.channel
# gen 2 will das als on cmd /rpc/Switch.Set?id=100&on=true
url = f"http://{config.configuration.ip_address}/rpc/Switch.Set?id={chan}&on=false"
req.get_http_session().get(url, auth=auth, timeout=3)

def update() -> ConsumerState:
status = request_status(config.configuration.ip_address, generation, auth=auth)
state = parse_state(status)
return ConsumerState(
state=state
)

def parse_state(status: Dict) -> bool:
try:
if generation == 1:
return status["relays"][config.configuration.channel]["ison"]
return status[f"switch:{config.configuration.channel}"]["output"]
except (KeyError, TypeError, IndexError):
raise Exception("Schaltstatus des Relais nicht lesbar.")

return ConfigurableConsumer(consumer_config=config,
initializer=initializer,
error_handler=error_handler,
switch_on=switch_on,
switch_off=switch_off,
update=update,)


device_descriptor = DeviceDescriptor(configuration_factory=ShellyRelais)
13 changes: 11 additions & 2 deletions packages/modules/devices/shelly/shelly/status_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from typing import Dict
from typing import Tuple, List, Optional
from requests.auth import AuthBase, HTTPBasicAuth, HTTPDigestAuth

from modules.common import req

Expand All @@ -23,12 +24,20 @@ def get_generation(address: str) -> Tuple[int, str]:
return generation, model


def request_status(address: str, generation: Optional[int]) -> Dict:
def get_auth(generation: int, username: Optional[str], password: Optional[str]) -> Optional[AuthBase]:
if not password:
return None
if generation == 1:
return HTTPBasicAuth(username, password) if username else None
return HTTPDigestAuth("admin", password)


def request_status(address: str, generation: Optional[int], auth: Optional[AuthBase] = None) -> Dict:
if generation == 1:
status_url = "http://" + address + "/status"
else:
status_url = "http://" + address + "/rpc/Shelly.GetStatus"
return req.get_http_session().get(status_url, timeout=3).json()
return req.get_http_session().get(status_url, auth=auth, timeout=3).json()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obwohl die Session-Hooks zuerst angelegt werden, registriert Requests beim Vorbereiten der Anfrage die Auth-Hooks vor den Session-Hooks.



def parse_temperatures(status: Dict, generation: int) -> Optional[List[float]]:
Expand Down