Skip to content

Commit

Permalink
Merge branch 'main' of
Browse files Browse the repository at this point in the history
github.com:OStrama/weishaupt_modbus into test
  • Loading branch information
MadOne committed Oct 22, 2024
2 parents f479d90 + f7f8c18 commit 78649f4
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 92 deletions.
15 changes: 4 additions & 11 deletions custom_components/weishaupt_modbus/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ async def validate_input(hass: HomeAssistant, data: dict) -> dict[str, Any]:
# throw CannotConnect
# If the authentication is wrong:
# InvalidAuth
if not await validateModbusConnection(data["host"], data["port"]):
raise CannotConnect
# if not await validateModbusConnection(data["host"], data["port"]):
# raise CannotConnect
# Return info that you want to store in the config entry.
# "Title" is what is displayed to the user for this hub device
# It is stored internally in HA as part of the device config.
Expand Down Expand Up @@ -74,16 +74,9 @@ async def async_step_user(self, user_input=None):
)


async def validateModbusConnection(host, port):
"""Validate the host."""
client = AsyncModbusTcpClient(host=host, port=port)
await client.connect()
return client.connected


class InvalidHost(exceptions.HomeAssistantError):
"""Error to indicate there is an invalid hostname."""


class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate that the connecton to the heatpump failed."""
class ConnectionFailed(exceptions.HomeAssistantError):
"""Error to indicate there is an invalid hostname."""
16 changes: 14 additions & 2 deletions custom_components/weishaupt_modbus/const.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
from dataclasses import dataclass
from datetime import timedelta
from homeassistant.const import UnitOfEnergy, UnitOfTemperature, UnitOfTime, UnitOfVolumeFlowRate, UnitOfPower, PERCENTAGE
from homeassistant.const import (
UnitOfEnergy,
UnitOfTemperature,
UnitOfTime,
UnitOfVolumeFlowRate,
UnitOfPower,
PERCENTAGE,
)


@dataclass(frozen=True)
class MainConstants:
Expand All @@ -10,8 +18,10 @@ class MainConstants:
APPID = 100
KENNFELDFILE = "weishaupt_wbb_kennfeld.json"


CONST = MainConstants()


@dataclass(frozen=True)
class FormatConstants:
TEMPERATUR = UnitOfTemperature.CELSIUS
Expand All @@ -25,8 +35,10 @@ class FormatConstants:
TIME_MIN = UnitOfTime.MINUTES
TIME_H = UnitOfTime.HOURS


FORMATS = FormatConstants()


@dataclass(frozen=True)
class TypeConstants:
SENSOR = "Sensor"
Expand All @@ -35,5 +47,5 @@ class TypeConstants:
NUMBER = "Number"
NUMBER_RO = "Number_RO"

TYPES = TypeConstants()

TYPES = TypeConstants()
24 changes: 13 additions & 11 deletions custom_components/weishaupt_modbus/entities.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# import warnings
import warnings
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PORT
from homeassistant.components.sensor import (
Expand Down Expand Up @@ -109,8 +109,8 @@ async def translateVal(self):
return val
return val / self._divider

@translateVal.setter
async def translateVal(self, value):
# @translateVal.setter
async def settranslateVal(self, value):
# translates and writes a value to the modbus
mbo = ModbusObject(self._config_entry, self._modbus_item)
if mbo == None:
Expand All @@ -122,7 +122,7 @@ async def translateVal(self, value):
val = self._modbus_item.getNumberFromText(value)
case _:
val = value * self._divider
mbo.value = val
await mbo.setvalue(val) # = val

def my_device_info(self) -> DeviceInfo:
# helper to build the device info
Expand Down Expand Up @@ -189,7 +189,8 @@ async def translateVal(self):
mbo_x = ModbusObject(self._config_entry, mb_x)
if mbo_x == None:
return None
val_x = self.calcTemperature(await mbo_x.value) / 10
t_temp = await mbo_x.value
val_x = self.calcTemperature(t_temp) / 10
mb_y = ModbusItem(
self._modbus_item.getNumberFromText("y"),
"y",
Expand All @@ -199,9 +200,10 @@ async def translateVal(self):
TEMPRANGE_STD,
)
mbo_y = ModbusObject(self._config_entry, mb_y)
if mbo_x == None:
if mbo_y == None:
return None
val_y = self.calcTemperature(await mbo_y.value) / 10
t_temp = await mbo_y.value
val_y = self.calcTemperature(t_temp) / 10

match self._modbus_item.format:
case FORMATS.POWER:
Expand Down Expand Up @@ -232,8 +234,8 @@ def __init__(self, config_entry, modbus_item) -> None:
self._attr_native_step = self._modbus_item.getNumberFromText("step")

async def async_set_native_value(self, value: float) -> None:
self.translateVal = value
self._attr_native_value = self.translateVal
await self.settranslateVal(value)
self._attr_native_value = await self.translateVal
self.async_write_ha_state()

async def async_update(self) -> None:
Expand Down Expand Up @@ -263,8 +265,8 @@ def __init__(self, config_entry, modbus_item) -> None:

async def async_select_option(self, option: str) -> None:
# the synching is done by the ModbusObject of the entity
self.translateVal = option
self._attr_current_option = self.translateVal
await self.settranslateVal(option)
self._attr_current_option = await self.translateVal
self.async_write_ha_state()

async def async_update(self) -> None:
Expand Down
6 changes: 4 additions & 2 deletions custom_components/weishaupt_modbus/hpconst.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,9 +629,11 @@ class DeviceConstants:
34101, "Status 2. WEZ", FORMATS.STATUS, TYPES.SENSOR, DEVICES.W2, W2_STATUS
),
ModbusItem(
34102, "Betriebsstunden 2. WEZ", FORMATS.TIME_H, TYPES.SENSOR, DEVICES.W2
34102, "Schaltspiele E-Heizung 1", FORMATS.NUMBER, TYPES.SENSOR, DEVICES.W2
),
ModbusItem(
34103, "Schaltspiele E-Heizung 2", FORMATS.NUMBER, TYPES.SENSOR, DEVICES.W2
),
ModbusItem(34103, "Schaltspiele 2. WEZ", FORMATS.NUMBER, TYPES.SENSOR, DEVICES.W2),
ModbusItem(
34104, "Status E-Heizung 1", FORMATS.STATUS, TYPES.SENSOR, DEVICES.W2, W2_STATUS
),
Expand Down
149 changes: 92 additions & 57 deletions custom_components/weishaupt_modbus/kennfeld.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,64 @@
#from scipy.interpolate import CubicSpline
# from scipy.interpolate import CubicSpline
from numpy.polynomial import Chebyshev
import numpy as np

#import matplotlib.pyplot as plt
# import matplotlib.pyplot as plt
import json
from .const import CONST

class PowerMap():

class PowerMap:
# these are values extracted from the characteristic curves of heating power found ion the documentation of my heat pump.
# there are two diagrams:
# there are two diagrams:
# - heating power vs. outside temperature @ 35 °C flow temperature
# - heating power vs. outside temperature @ 55 °C flow temperature
known_x = [ -30, -25, -22, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40]
known_x = [-30, -25, -22, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40]
# known power values read out from the graphs plotted in documentation. Only 35 °C and 55 °C available
known_y = [[ 5700, 5700, 5700, 5700, 6290, 7580, 8660, 9625, 10300, 10580, 10750, 10790, 10830, 11000, 11000, 11000 ],
[ 5700, 5700, 5700, 5700, 6860, 7300, 8150, 9500, 10300, 10580, 10750, 10790, 10830, 11000, 11000, 11000 ]]
known_y = [
[
5700,
5700,
5700,
5700,
6290,
7580,
8660,
9625,
10300,
10580,
10750,
10790,
10830,
11000,
11000,
11000,
],
[
5700,
5700,
5700,
5700,
6860,
7300,
8150,
9500,
10300,
10580,
10750,
10790,
10830,
11000,
11000,
11000,
],
]

# the known x values for linear interpolation
known_t = [35, 55]

# the aim is generating a 2D power map that gives back the actual power for a certain flow temperature and a given outside temperature
# the map should have values on every integer temperature point
# at first, all flow temoperatures are lineary interpolated
# at first, all flow temoperatures are lineary interpolated
steps = 21
r_to_interpolate = np.linspace(35, 55, steps)

Expand All @@ -31,36 +68,38 @@ class PowerMap():
interp_y = []

def __init__(self) -> None:
#try to load values from json file
# try to load values from json file

try:
openfile = open(CONST.KENNFELDFILE, "r")
except IOError:
kennfeld = {'known_x': self.known_x,
'known_y': self.known_y,
'known_t': self.known_t}
kennfeld = {
"known_x": self.known_x,
"known_y": self.known_y,
"known_t": self.known_t,
}
with open(CONST.KENNFELDFILE, "w") as outfile:
json.dump(kennfeld, outfile)
else:
json_object = json.load(openfile)
self.known_x = json_object['known_x']
self.known_y = json_object['known_y']
self.known_t = json_object['known_t']
self.known_x = json_object["known_x"]
self.known_y = json_object["known_y"]
self.known_t = json_object["known_t"]
openfile.close()

# build the matrix with linear interpolated samples
# 1st and last row are populated by known values from diagrem, the rest is zero
self.interp_y.append(self.known_y[0])
v = np.linspace(0, self.steps-3, self.steps-2)
v = np.linspace(0, self.steps - 3, self.steps - 2)
for idx in v:
self.interp_y.append(np.zeros_like(self.known_x))
self.interp_y.append(self.known_y[1])

for idx in range(0, len(self.known_x)):
# the known y for every column
yk = [self.interp_y[0][idx], self.interp_y[self.steps-1][idx]]
yk = [self.interp_y[0][idx], self.interp_y[self.steps - 1][idx]]

#linear interpolation
# linear interpolation
ip = np.interp(self.r_to_interpolate, self.known_t, yk)

# sort the interpolated values into the array
Expand All @@ -72,59 +111,55 @@ def __init__(self) -> None:
t = np.linspace(-30, 40, 71)
# cubic spline interpolation of power curves
for idx in range(0, len(self.r_to_interpolate)):
#f = CubicSpline(self.known_x, self.interp_y[idx], bc_type='natural')
f = Chebyshev.fit(self.known_x, self.interp_y[idx], deg = 8)
# f = CubicSpline(self.known_x, self.interp_y[idx], bc_type='natural')
f = Chebyshev.fit(self.known_x, self.interp_y[idx], deg=8)
self.max_power.append(f(t))

def map(self,x,y):

numrows = len(self.max_power) # 3 rows in your example

numcols = len(self.max_power[0]) # 2 columns in your example
x=x-self.known_x[0]
if x<0:
x=0
if x>70:
x= 70
y=y-self.known_t[0]
if y<0:
y=0
if y> (self.steps-1):
y=self.steps-1

def map(self, x, y):
numrows = len(self.max_power) # 3 rows in your example

numcols = len(self.max_power[0]) # 2 columns in your example
x = x - self.known_x[0]
if x < 0:
x = 0
if x > 70:
x = 70
y = y - self.known_t[0]
if y < 0:
y = 0
if y > (self.steps - 1):
y = self.steps - 1

return self.max_power[int(y)][int(x)]


#map = PowerMap()
# map = PowerMap()

#plt.plot(t,np.transpose(map.max_power))
#plt.ylabel('Max Power')
#plt.xlabel('°C')
#plt.show()
# plt.plot(t,np.transpose(map.max_power))
# plt.ylabel('Max Power')
# plt.xlabel('°C')
# plt.show()

#kennfeld = {'known_x': map.known_x,
# kennfeld = {'known_x': map.known_x,
# 'known_y': map.known_y,
# 'known_t': map.known_t}

#with open("sample1.json", "w") as outfile:
# with open("sample1.json", "w") as outfile:
# outfile.write(kennfeld)



#with open("sample2.json", "w") as outfile:
# with open("sample2.json", "w") as outfile:
# json.dump(kennfeld, outfile)

#with open('sample2.json', 'r') as openfile:

# Reading from json file
#json_object = json.load(openfile)

#map.known_x = json_object['known_x']
#map.known_y = json_object['known_y']
#map.known_t = json_object['known_t']
# with open('sample2.json', 'r') as openfile:

#print(map.known_x)
#print(map.known_y)
#print(map.known_t)
# Reading from json file
# json_object = json.load(openfile)

# map.known_x = json_object['known_x']
# map.known_y = json_object['known_y']
# map.known_t = json_object['known_t']

# print(map.known_x)
# print(map.known_y)
# print(map.known_t)
4 changes: 2 additions & 2 deletions custom_components/weishaupt_modbus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"documentation": "https://github.com/OStrama/weishaupt_modbus/",
"iot_class": "local_polling",
"issue_tracker": "https://github.com/OStrama/weishaupt_modbus/issues",
"requirements": [],
"version": "0.0.6"
"requirements": ["pymodbus"],
"version": "0.0.8"
}

Loading

0 comments on commit 78649f4

Please sign in to comment.