From c9ed384231ab8df75826353b90d0a5f50a75bc7e Mon Sep 17 00:00:00 2001 From: MadOne Date: Sun, 28 Jul 2024 20:27:06 +0000 Subject: [PATCH] add files --- custom_components/weishaupt_modbus | 1 - .../weishaupt_modbus/__init__.py | 34 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1431 bytes .../__pycache__/config_flow.cpython-312.pyc | Bin 0 -> 2891 bytes .../__pycache__/const.cpython-312.pyc | Bin 0 -> 386 bytes .../__pycache__/number.cpython-312.pyc | Bin 0 -> 3960 bytes .../__pycache__/select.cpython-312.pyc | Bin 0 -> 4321 bytes .../__pycache__/sensor.cpython-312.pyc | Bin 0 -> 25255 bytes .../__pycache__/wp.cpython-312.pyc | Bin 0 -> 17426 bytes .../weishaupt_modbus/config_flow.py | 95 ++ custom_components/weishaupt_modbus/const.py | 8 + .../weishaupt_modbus/manifest.json | 11 + custom_components/weishaupt_modbus/number.py | 78 ++ custom_components/weishaupt_modbus/select.py | 108 +++ custom_components/weishaupt_modbus/sensor.py | 809 ++++++++++++++++++ .../weishaupt_modbus/strings.json | 32 + .../weishaupt_modbus/translations/en.json | 32 + custom_components/weishaupt_modbus/wp.py | 502 +++++++++++ 18 files changed, 1709 insertions(+), 1 deletion(-) delete mode 160000 custom_components/weishaupt_modbus create mode 100644 custom_components/weishaupt_modbus/__init__.py create mode 100644 custom_components/weishaupt_modbus/__pycache__/__init__.cpython-312.pyc create mode 100644 custom_components/weishaupt_modbus/__pycache__/config_flow.cpython-312.pyc create mode 100644 custom_components/weishaupt_modbus/__pycache__/const.cpython-312.pyc create mode 100644 custom_components/weishaupt_modbus/__pycache__/number.cpython-312.pyc create mode 100644 custom_components/weishaupt_modbus/__pycache__/select.cpython-312.pyc create mode 100644 custom_components/weishaupt_modbus/__pycache__/sensor.cpython-312.pyc create mode 100644 custom_components/weishaupt_modbus/__pycache__/wp.cpython-312.pyc create mode 100644 custom_components/weishaupt_modbus/config_flow.py create mode 100644 custom_components/weishaupt_modbus/const.py create mode 100644 custom_components/weishaupt_modbus/manifest.json create mode 100644 custom_components/weishaupt_modbus/number.py create mode 100644 custom_components/weishaupt_modbus/select.py create mode 100644 custom_components/weishaupt_modbus/sensor.py create mode 100644 custom_components/weishaupt_modbus/strings.json create mode 100644 custom_components/weishaupt_modbus/translations/en.json create mode 100644 custom_components/weishaupt_modbus/wp.py diff --git a/custom_components/weishaupt_modbus b/custom_components/weishaupt_modbus deleted file mode 160000 index 870804e..0000000 --- a/custom_components/weishaupt_modbus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 870804e8bbaa1d7995fa730c7515bf6f9c19f08a diff --git a/custom_components/weishaupt_modbus/__init__.py b/custom_components/weishaupt_modbus/__init__.py new file mode 100644 index 0000000..648d96c --- /dev/null +++ b/custom_components/weishaupt_modbus/__init__.py @@ -0,0 +1,34 @@ +"""Weishaupt Modbus Integration.""" + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DOMAIN + +PLATFORMS: list[str] = ["number", "select", "sensor"] + + +# Return boolean to indicate that initialization was successful. +# return True +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Hello World from a config entry.""" + # Store an instance of the "connecting" class that does the work of speaking + # with your actual devices. + # hass.data.setdefault(DOMAIN, {})[entry.entry_id] = hub.Hub(hass, entry.data["host"]) + + # This creates each HA object for each platform your device requires. + # It's done by calling the `async_setup_entry` function in each platform module. + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + # This is called when an entry/configured device is to be removed. The class + # needs to unload itself, and remove callbacks. See the classes for further + # details + unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + if unload_ok: + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/custom_components/weishaupt_modbus/__pycache__/__init__.cpython-312.pyc b/custom_components/weishaupt_modbus/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..052d363771478cf60bf47d31b712349b3f12307f GIT binary patch literal 1431 zcmcIkO=ufO6rR~#t#((I|FD(1u3#aiWxu zFmhEeNhu8!+CypDQ$r6)OX%IF(nAk{<`Ch6(t$!q2{bo>dni8j&B~%g?X?ef-kZmp z_xHWGKWDQR0{V9OcJ0*^Lchz)fb>)0I0E4&x{Q2OK|c1C0EbFN!BSQOHPk8^$eN!D zQlVbaG1^AH{td3Ai9yU6cuOoleGa&95Y0D<;#(wB)BVhcT4iKNHv+nBE9;Ez=dX}D z-|?D_*j}gpR+HPSQB1a(7uRW2cq(&qu*keYqgs9Ytte(KxMo*qNJ^a7dF(~;Q`|Sw z-@Le9THSC|p-0Vdi!h;c5|C;vdBiCb=DY8dF1>wmbGr<<5<2==(I#e!S)N~rutDT1s+?<%jlET z_v#*Y)cu#rB(|FkdxZo6wXaYX_;!ua(Dv+VQoNnyTG+S*1&;Pru2j_t7tedV6;<6D zWqTg;6LrhwP*j66iM*q*3@|nV2bcNS_Q`5-kFu-0;Z+GQu9Vy=Wu(~mTdX#DOhZtG z4H`iZUfdhN2Pqqb5;0QzDAy0V|U90k<#|An!WW@BJ4 za{b0O6Dzk7crid6_AwQj@5LUIX$rML8^TBi#I5^|`VZQ|Jbg^Bayh8BZLye%^T+DFLsQX zu95E;`7g=W(O1zg#=@h_)B|m*XQGKRfGW$mHdo36s-;=&&Jsu)&ZtPcu1~A3D_+=v zm4=njS|=ax zn7yMEsWRX=YU>mMs-i&c$D|KQ46CU6rlK$QTZ%qZstZV2q-cPkX#GZ1GSC=(=**Fn zCHtZ10-T$jncbP)`DT`XjKv}Z#%C{oQeKY{@-E* zk|-twA`&Xk9&5RO^!4S>J({;T8omvY;h{tQhUfa>7MD> znfthFTHsRFUdX3K-d=L-vUy3Pw#Q8B@(6vmM5}1(f;K!lm7maZQ-$d!`})-RX;>Z0 zITf09UDNe++q;h!4N_%n>Rfg*&!tl@oP=r6pk=*gd0d{+U6;#-?&(}%)T=SO)Vy>5 zjCd#S!*6E-$ZJFZJbBIB5wav00^V{f3x;?Z)-uK0+u<5utz~5j1@dcYUP#M}OK<3w z2|lUn%}}-GQl^@AwdSe1WjXUMNL0^J;rKQ!d6`H=g)s&pst~QeR5I< zs})9bAQFKk4g-H@OC#>GTkJA6ba!FO-8@~|@uq8o&gq5$1M zG=!pLh((zOX502`_n2bP2pICRPz(WP6&@M&Q8)onFMcbh+lEC|9diTFRPiio*Yzo$rW))c<)qrKcL8LE9AX!5N545@IExJA41}O zDcz8M90-sr(h`})y&oG(a6AO&lCUbZ))olZVq}jEL-G7pisB`4HP|Zd0y#}SJUKu- zrM2KFVIgRI@{(u-MnL!$`EYQayeqv$<^>G$A=SE9qUF} zYb%Tl?HBtVfg;m%)6$)R1@4c4_zFqZ-4N4-$p8PFEzappRTu&cyg;_V6$UjJK47yU7x5+ zBz|;AOhF2U?S?rDhRS|B?pT<^_x zlaGaLQvMZkzwZUv-J}5Q-R}16ap~?+5hce2lLbLJfdMG*DU1Q zH0_<5ZZ$PQO*5Plh=Xs8pD#>KBafvpq9Fva9Mg2>phoV)w*z zXY#jXmPW^eNKPQZhn!*mYD9$K@%Q#g5oSABt;>M2?=&pX@&6MkTwdc}`2=3h3jW@$wjww~@hTzu^=`>BD}oT=5pFrhSP)S0Owy=UDJK*PNQmCE$5%1|Cy* zAsdgWpH+q@;D-(ru&Lsm zPwK?d91k>HV(a2cq0uBl=jl5&lTm|95DgDa`F|F4i_2AqdF&|K%8*&F(L@krJdxY~ zcfm(MBV+#M9ZcoJ&`6^IzQF^WjePX&Pe(o;SqtAe^_PLk&rkm8 z^dC-d$b}tA>=3U;>k(BLxOt^c;IlU7f7Yk(4BT<=Jome|cTl=JAsiMWE6;3o4sLc1 puD$etz;t&ELi+B?;8tR2GcmMFV6q!R$>LUOcr!Kp03|*e{tIXo(jou= literal 0 HcmV?d00001 diff --git a/custom_components/weishaupt_modbus/__pycache__/const.cpython-312.pyc b/custom_components/weishaupt_modbus/__pycache__/const.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..179d2f6a3890ed8f75622c89a46d20007fa72569 GIT binary patch literal 386 zcmZ9HJxjwt7{@P3^HvQ67ZE`ax^!v4$w7qBRt#vYFLlUr#N1VbO)lmxlseT<(bY*5 z{U)vs?M;F>bQ8LD@`Aef4A1M2KmX^|vZ_GD*&z+ zd~TgKT_1Y>S^MPNJ>X{7_l~=5=(V`&b~;|`3Csl!7JJTs`beAF XYxkRPc5P%$mRCpS#8|phb6bGlYD{ns literal 0 HcmV?d00001 diff --git a/custom_components/weishaupt_modbus/__pycache__/number.cpython-312.pyc b/custom_components/weishaupt_modbus/__pycache__/number.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d322387ab004b3bda4c593c562a4f589e9feb89 GIT binary patch literal 3960 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 In)l)EUt~QM%m4rY literal 0 HcmV?d00001 diff --git a/custom_components/weishaupt_modbus/__pycache__/sensor.cpython-312.pyc b/custom_components/weishaupt_modbus/__pycache__/sensor.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52ce30719e468d0c0e21b395f4cc844ac7810518 GIT binary patch literal 25255 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 Jhc{Ho|34WuAhZAg literal 0 HcmV?d00001 diff --git a/custom_components/weishaupt_modbus/__pycache__/wp.cpython-312.pyc b/custom_components/weishaupt_modbus/__pycache__/wp.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..727bfbd2a231ccca8085c90e2cf82fcb0c079ed1 GIT binary patch literal 17426 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 s dict[str, Any]: + """Validate the user input allows us to connect. + + Data has the keys from DATA_SCHEMA with values provided by the user. + """ + # Validate the data can be used to set up a connection. + + # This is a simple example to show an error in the UI for a short hostname + # The exceptions are defined at the end of this file, and are used in the + # `async_step_user` method below. + if len(data["host"]) < 3: + raise InvalidHost + + # whp = wp.heat_pump("10.10.1.225", 502) + # if not whp: + # raise ConnectionFailed + + # If your PyPI package is not built with async, pass your methods + # to the executor: + # await hass.async_add_executor_job( + # your_validate_func, data["username"], data["password"] + # ) + + # If you cannot connect: + # throw CannotConnect + # If the authentication is wrong: + # InvalidAuth + + # 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. + # See `async_step_user` below for how this is used + return {"title": data["host"]} + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Hello World.""" + + VERSION = 1 + # Pick one of the available connection classes in homeassistant/config_entries.py + # This tells HA if it should be asking for updates, or it'll be notified of updates + # automatically. This example uses PUSH, as the dummy hub will notify HA of + # changes. + CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH + + async def async_step_user(self, user_input=None): + """Handle the initial step.""" + # This goes through the steps to take the user through the setup process. + # Using this it is possible to update the UI and prompt for additional + # information. This example provides a single form (built from `DATA_SCHEMA`), + # and when that has some validated input, it calls `async_create_entry` to + # actually create the HA config entry. Note the "title" value is returned by + # `validate_input` above. + errors = {} + if user_input is not None: + try: + info = await validate_input(self.hass, user_input) + + return self.async_create_entry(title=info["title"], data=user_input) + + except Exception: + errors["base"] = "unknown" + + # If there is no user input or there were errors, show the form again, including any errors that were found with the input. + return self.async_show_form( + step_id="user", data_schema=DATA_SCHEMA, errors=errors + ) + + +class InvalidHost(exceptions.HomeAssistantError): + """Error to indicate there is an invalid hostname.""" + + +class ConnectionFailed(exceptions.HomeAssistantError): + """Error to indicate there is an invalid hostname.""" diff --git a/custom_components/weishaupt_modbus/const.py b/custom_components/weishaupt_modbus/const.py new file mode 100644 index 0000000..8e32865 --- /dev/null +++ b/custom_components/weishaupt_modbus/const.py @@ -0,0 +1,8 @@ +"""Constants.""" + +from datetime import timedelta + +DOMAIN = "weishaupt_modbus" +SCAN_INTERVAL = timedelta(minutes=1) +UNIQUE_ID = "unique_id" +APPID = 100 diff --git a/custom_components/weishaupt_modbus/manifest.json b/custom_components/weishaupt_modbus/manifest.json new file mode 100644 index 0000000..851b5ef --- /dev/null +++ b/custom_components/weishaupt_modbus/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "weishaupt_modbus", + "name": "Weishaupt Modbus Integration", + "codeowners": ["@MadOne"], + "config_flow": true, + "documentation": "https://www.not_yet.com", + "iot_class": "local_polling", + "requirements": [], + "version": "0.0.1" +} + diff --git a/custom_components/weishaupt_modbus/number.py b/custom_components/weishaupt_modbus/number.py new file mode 100644 index 0000000..a113716 --- /dev/null +++ b/custom_components/weishaupt_modbus/number.py @@ -0,0 +1,78 @@ +"""Number platform for wemportal component.""" + +from homeassistant.components.number import NumberEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_HOST, CONF_PORT, UnitOfTemperature +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType + +from . import wp +from .const import DOMAIN + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the wemportal component.""" + hass.data.setdefault(DOMAIN, {}) + return True + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, + discovery_info=None, +) -> None: + """Set up Numbers.""" + hass.data.setdefault(DOMAIN, {}) + # hub = hass.data[DOMAIN][config_entry.entry_id] + host = config_entry.data[CONF_HOST] + port = config_entry.data[CONF_PORT] + # port = config_entry.data.get[CONF_PORT] + # host = "10.10.1.225" + # port = "502" + async_add_entities([Number(host, port)], update_before_add=True) + + +class Number(NumberEntity): + """Representation of a WEM Portal number.""" + + _attr_name = "WW Soll Temp" + _attr_unique_id = DOMAIN + _attr_name + _attr_native_value = 0 + _attr_should_poll = True + _attr_native_min_value = 40 + _attr_native_max_value = 60 + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + whp = wp.heat_pump(host, port) + whp.connect() + self._attr_native_value = whp.WW_Soll + # self.async_write_ha_state() + + async def async_set_native_value(self, value: float) -> None: + """Update the current value.""" + whp = wp.heat_pump(self._host, self._port) + whp.connect() + whp.WW_Soll = int(value) + + self._attr_native_value = whp.WW_Soll + self.async_write_ha_state() + + async def async_update(self) -> None: + """Update Entity Only used by the generic entity update service.""" + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.WW_Soll + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Warmwasser")}, + } diff --git a/custom_components/weishaupt_modbus/select.py b/custom_components/weishaupt_modbus/select.py new file mode 100644 index 0000000..bbfabc1 --- /dev/null +++ b/custom_components/weishaupt_modbus/select.py @@ -0,0 +1,108 @@ +"""Select platform for wemportal component.""" + +from homeassistant.components.select import SelectEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_HOST, CONF_PORT +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import wp +from .const import DOMAIN + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Select entry setup.""" + host = config_entry.data[CONF_HOST] + port = config_entry.data[CONF_PORT] + async_add_entities( + [ + Sys_Betriebsart(host, port), + HK_Konfiguration(host, port), + ], + update_before_add=True, + ) + + +class Sys_Betriebsart(SelectEntity): + """Representation of a WEM Portal Sensor.""" + + _attr_name = "Systembetriebsart" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + options = ["AUTOMATIK", "HEIZEN", "KÜHLEN", "SOMMER", "STANDBY", "2.WEZ", "FEHLER"] + _attr_current_option = "FEHLER" + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self.async_internal_will_remove_from_hass_port = port + + async def async_select_option(self, option: str) -> None: + """Call the API to change the parameter value.""" + + self._attr_current_option = option + + self.async_write_ha_state() + + async def async_update(self) -> None: + """Update Entity Only used by the generic entity update service.""" + # await self.coordinator.async_request_refresh() + whp = wp.heat_pump("10.10.1.225", 502) + whp.connect() + self._attr_current_option = whp.Sys_Betriebsart + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "System")}, + } + + +class HK_Konfiguration(SelectEntity): + """Representation of a WEM Portal Sensor.""" + + _attr_name = "Konfiguration" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + options = [ + "AUS", + "PUMPENKREIS", + "MISCHKREIS", + "SOLLWERT (PUMPE M1)", + "FEHLER", + ] + _attr_current_option = "FEHLER" + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_select_option(self, option: str) -> None: + """Call the API to change the parameter value.""" + + self._attr_current_option = option + + self.async_write_ha_state() + + async def async_update( + self, + ) -> None: + """Update Entity Only used by the generic entity update service.""" + # await self.coordinator.async_request_refresh() + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_current_option = whp.HK_Konfiguration + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Heizkreis")}, + } diff --git a/custom_components/weishaupt_modbus/sensor.py b/custom_components/weishaupt_modbus/sensor.py new file mode 100644 index 0000000..f80774c --- /dev/null +++ b/custom_components/weishaupt_modbus/sensor.py @@ -0,0 +1,809 @@ +"""Platform for sensor integration.""" + +from __future__ import annotations + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_HOST, CONF_PORT, UnitOfEnergy, UnitOfTemperature +from homeassistant.core import HomeAssistant + +# from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import DiscoveryInfoType + +# from time import gmtime, strftime +from . import wp +from .const import DOMAIN + + +async def async_setup_entry( + hass: HomeAssistant, + # config: ConfigType, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, + discovery_info: DiscoveryInfoType | None = None, +) -> None: + """Set up the sensor platform.""" + host = config_entry.data[CONF_HOST] + port = config_entry.data[CONF_PORT] + async_add_entities( + [ + Sys_Aussentemperatur1(host, port), + Sys_Aussentemperatur2(host, port), + Sys_Fehler(host, port), + Sys_Warnung(host, port), + Sys_Fehlerfrei(host, port), + Sys_Betriebsanzeige(host, port), + HK_RaumSollTemperatur(host, port), + HK_RaumTemperatur(host, port), + HK_RaumFeuchte(host, port), + HK_VorlaufSollTemperatur(host, port), + HK_VorlaufTemperatur(host, port), + sensor_measured_temp(host, port), + sensor_target_temp(host, port), + Energy_today(host, port), + Energy_yesterday(host, port), + Energy_month(host, port), + Energy_year(host, port), + HP_Betrieb(host, port), + HP_Stoermeldung(host, port), + HP_Leistungsanforderung(host, port), + Hp_Vorlauftemperatur(host, port), + Hp_Ruecklauftemperatur(host, port), + ], + update_before_add=True, + ) + + +##################### +# System # +##################### +class Sys_Aussentemperatur1(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Aussentemperatur1" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + # whp = wp.heat_pump(self._host, self._port) + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Sys_Aussentemperatur1 + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "System")}, + "name": "Wärmepumpe-System", + # "sw_version": "Device_SW_Version", + # "model": "Device_model", + "manufacturer": "Weishaupt", + } + + +class Sys_Aussentemperatur2(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Aussentemperatur2" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Sys_Aussentemperatur2 + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "System")}, + } + + +class Sys_Fehler(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Fehler" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Sys_Fehler + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "System")}, + } + + +class Sys_Warnung(SensorEntity): + """Representation of a Sensor.""" + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + _attr_name = "Warnung" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + # + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Sys_Warnung + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "System")}, + } + + +class Sys_Fehlerfrei(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Fehlerfrei" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + # + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Sys_Fehlerfrei + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "System")}, + } + + +class Sys_Betriebsanzeige(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Betriebsanzeige" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + # + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Sys_Betriebsanzeige + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "System")}, + } + + +##################### +# Heizkreis # +##################### +class HK_RaumSollTemperatur(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Raumsolltemperatur" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.HK_Raumsolltemperatur + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Heizkreis")}, + "name": "Wärmepumpe-Heizkreis", + # "sw_version": "Device_SW_Version", + # "model": "Device_model", + "manufacturer": "Weishaupt", + } + + +class HK_RaumTemperatur(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Raumtemperatur" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.HK_Raumtemperatur + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Heizkreis")}, + } + + +class HK_RaumFeuchte(SensorEntity): + """Representation of a Sensor.""" + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + _attr_name = "Raumfeuchte" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = "%" + _attr_device_class = SensorDeviceClass.MOISTURE + _attr_state_class = SensorStateClass.MEASUREMENT + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.HK_Raumfeuchte + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Heizkreis")}, + } + + +class HK_VorlaufSollTemperatur(SensorEntity): + """Representation of a Sensor.""" + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + _attr_name = "VorlaufSollTemperatur" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.HK_Vorlaufsolltemperatur + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Heizkreis")}, + } + + +class HK_VorlaufTemperatur(SensorEntity): + """Representation of a Sensor.""" + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + _attr_name = "VorlaufTemperatur" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.HK_Vorlauftemperatur + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Heizkreis")}, + } + + +##################### +# Warmwasser # +##################### + + +class sensor_measured_temp(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Ist Temperatur" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.WW_Ist + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Warmwasser")}, + "name": "Wärmepumpe-Warmwasser", + "manufacturer": "Weishaupt", + } + + +class sensor_target_temp(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Soll Temperatur" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.WW_Soll_info + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Warmwasser")}, + } + + ##################### + # Heatpump # + ##################### + + +class HP_Betrieb(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Wärmepumpe Betrieb" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Hp_Betrieb + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Wärmepumpe")}, + "name": "Wärmepumpe-Wärmepumpe", + "manufacturer": "Weishaupt", + } + + +class HP_Stoermeldung(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Wärmepumpe Störmeldung" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Hp_Stoermeldung + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Wärmepumpe")}, + } + + +class HP_Leistungsanforderung(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Wärmepumpe Leistungsanforderung" + _attr_unique_id = DOMAIN + _attr_name + _attr_native_unit_of_measurement = "%" + _attr_should_poll = True + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Hp_Leistungsanforderung + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Wärmepumpe")}, + } + + +class Hp_Vorlauftemperatur(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Wärmepumpe Vorlauftemperatur" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Hp_Vorlauftemperatur + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Wärmepumpe")}, + } + + +class Hp_Ruecklauftemperatur(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Wärmepumpe Rücklauftemperatur" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Hp_Ruecklauftemperatur + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Wärmepumpe")}, + } + + +##################### +# 2. WEZ # +##################### + +##################### +# Eingänge # +##################### + + +##################### +# Statistik # +##################### + + +class Energy_today(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Energy Today" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR + _attr_device_class = SensorDeviceClass.ENERGY + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Energy_total_today + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Statistics")}, + "name": "Wärmepumpe-Statistics", + "manufacturer": "Weishaupt", + } + + +class Energy_yesterday(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Energy yesterday" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR + _attr_device_class = SensorDeviceClass.ENERGY + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Energy_total_yesterday + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Statistics")}, + } + + +class Energy_month(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Energy month" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR + _attr_device_class = SensorDeviceClass.ENERGY + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Energy_total_month + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Statistics")}, + } + + +class Energy_year(SensorEntity): + """Representation of a Sensor.""" + + _attr_name = "Energy year" + _attr_unique_id = DOMAIN + _attr_name + _attr_should_poll = True + _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR + _attr_device_class = SensorDeviceClass.ENERGY + + def __init__(self, host, port) -> None: + """Init.""" + self._host = host + self._port = port + + async def async_update(self) -> None: + """Fetch new state data for the sensor. + + This is the only method that should fetch new data for Home Assistant. + """ + + whp = wp.heat_pump(self._host, self._port) + whp.connect() + self._attr_native_value = whp.Energy_total_year + + @property + def device_info(self) -> DeviceInfo: + """Information about this entity/device.""" + return { + "identifiers": {(DOMAIN, "Statistics")}, + } diff --git a/custom_components/weishaupt_modbus/strings.json b/custom_components/weishaupt_modbus/strings.json new file mode 100644 index 0000000..85a72ff --- /dev/null +++ b/custom_components/weishaupt_modbus/strings.json @@ -0,0 +1,32 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "Host", + "port": "Port" + } + } + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "unknown": "Unexpected error" + }, + "abort": { + "already_configured": "Account is already configured" + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Web scraping interval (default = 1800 sec)", + "api_scan_interval": "Api scan interval (default = 300 sec)", + "language": "Language (default = en)", + "mode": "Mode(default = api)" + } + } + } + } + } \ No newline at end of file diff --git a/custom_components/weishaupt_modbus/translations/en.json b/custom_components/weishaupt_modbus/translations/en.json new file mode 100644 index 0000000..386cd56 --- /dev/null +++ b/custom_components/weishaupt_modbus/translations/en.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Account is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "unknown": "Unexpected error" + }, + "step": { + "user": { + "data": { + "host": "Host", + "port": "Port" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "api_scan_interval": "Api scan interval (default = 300 sec)", + "language": "Language (default = en)", + "mode": "Mode(default = api)", + "scan_interval": "Web scraping interval (default = 1800 sec)" + } + } + } + } +} \ No newline at end of file diff --git a/custom_components/weishaupt_modbus/wp.py b/custom_components/weishaupt_modbus/wp.py new file mode 100644 index 0000000..50f6b2c --- /dev/null +++ b/custom_components/weishaupt_modbus/wp.py @@ -0,0 +1,502 @@ +"""Platform for sensor integration.""" + +from pymodbus.client import ModbusTcpClient as ModbusClient + +# import logging + +# hp_ip = "10.10.1.225" +# hp_port = 502 + +# logging.basicConfig() +# log = logging.getLogger() +# log.setLevel(logging.DEBUG) +APPID_offset = 100 + + +class heat_pump: + """Test.""" + + def __init__(self, hp_ip, hp_port) -> None: + """Test.""" + self._ip = hp_ip + self._port = hp_port + self.WWP = None + + def connect(self): + """Test.""" + try: + self.WWP = ModbusClient(host=self._ip, port=self._port) + return self.WWP.connected + except: + return None + + ############################################################################################################################## + # Modbus Register List: # + # https://docs.google.com/spreadsheets/d/1EZ3QgyB41xaXo4B5CfZe0Pi8KPwzIGzK/edit?gid=1730751621#gid=1730751621 # + ############################################################################################################################## + + ##################### + # System # + ##################### + @property + def Sys_Aussentemperatur1(self): + """Outer Temperature1.""" + try: + return self.WWP.read_input_registers(30001, slave=1).registers[0] / 10 + except: + return None + + @property + def Sys_Aussentemperatur2(self): + """Outer Temperature2.""" + try: + return self.WWP.read_input_registers(30002, slave=1).registers[0] / 10 + except: + return None + + @property + def Sys_Fehler(self): + """Outer Temperature2.""" + try: + val = self.WWP.read_input_registers(30004, slave=1).registers[0] + if val == 65535: + return "kein Fehler" + return "Fehler: " + val + except: + return None + + @property + def Sys_Warnung(self): + """Outer Temperature2.""" + try: + val = self.WWP.read_input_registers(30004, slave=1).registers[0] + if val == 65535: + return "kein Fehler" + return "Fehler: " + val + except: + return None + + @property + def Sys_Fehlerfrei(self): + """Outer Temperature2.""" + try: + val = self.WWP.read_input_registers(30005, slave=1).registers[0] + if val == 0: + return "Fehler aktiv" + return "Störungsfreier Betrieb" + except: + return None + + @property + def Sys_Betriebsanzeige(self): # noqa: C901 + """Energy used today.""" + try: + val = self.WWP.read_input_registers(30006, slave=1).registers[0] + match val: + case 0: + return "Undefiniert" + case 1: + return "Relaistest" + case 2: + return "Notaus" + case 3: + return "Diagnose" + case 4: + return "Handbetrieb" + case 5: + return "Handbetrieb Heizen" + case 6: + return "Handbetrieb Kühlen" + case 7: + return "Manueller Abtaubetrieb" + case 8: + return "Abtauen" + case 9: + return "WEZ2" + case 10: + return "EVU_SPERRE" + case 11: + return "SG Tarif" + case 12: + return "SG Maximal" + case 13: + return "Tarifladung" + case 14: + return "Erhöhter Betrieb" + case 15: + return "Standzeit" + case 16: + return "Standbybetrieb" + case 17: + return "Spülbetrieb" + case 18: + return "Frostschutz" + case 19: + return "Heizbetrieb" + case 20: + return "Warmwasserbetrieb" + case 21: + return "Legionellenschutz" + case 22: + return "Umschaltung HZ KU" + case 23: + return "Kühlbetrieb" + case 24: + return "Passive Kühlung" + case 25: + return "Sommerbetrieb" + case 26: + return "Schwimmbad" + case 27: + return "Urlaub" + case 28: + return "Estrich" + case 29: + return "Gesperrt" + case 30: + return "Sperre AT" + case 31: + return "Sperre Sommer" + case 32: + return "Sperre Winter" + case 33: + return "Einsatzgrenze" + case 34: + return "HK Sperre" + case 35: + return "Absenk" + except: + return None + + @property + def Sys_Betriebsart(self): + """Energy used today.""" + val = self.WWP.read_holding_registers(40001, slave=1).registers[0] + match val: + case 0: + return "AUTOMATIK" + case 1: + return "HEIZEN" + case 2: + return "KÜHLEN" + case 3: + return "SOMMER" + case 4: + return "STANDBY" + case 5: + return "2.WEZ" + + ##################### + # Heizkreis # + ##################### + @property + def HK_Raumsolltemperatur(self): + """Raumsolltemperatur.""" + return self.WWP.read_input_registers(31101, slave=1).registers[0] / 10 + + @property + def HK_Raumtemperatur(self): + """Raumtemperatur.""" + val = self.WWP.read_input_registers(31102, slave=1).registers[0] + if val == 32768: + return None + return val / 10 + + @property + def HK_Raumfeuchte(self): + """Raumtemperatur.""" + val = self.WWP.read_input_registers(31103, slave=1).registers[0] + if val == 65535: + return None + return val + + @property + def HK_Vorlaufsolltemperatur(self): + """HK_Vorlaufsolltemperatur.""" + return self.WWP.read_input_registers(31104, slave=1).registers[0] / 10 + + @property + def HK_Vorlauftemperatur(self): + """HK_Vorlauftemperatur.""" + val = self.WWP.read_input_registers(31105, slave=1).registers[0] + if val == 32768: + return None + return val / 10 + + @property + def HK_Konfiguration(self): + """Energy used today.""" + val = self.WWP.read_holding_registers(41101, slave=1).registers[0] + match val: + case 0: + return "AUS" + case 1: + return "PUMPENKREIS" + case 2: + return "MISCHKREIS" + case 3: + return "SOLLWERT (PUMPE M1)" + + @property + def HK_AnforderungTyp(self): + """Energy used today.""" + val = self.WWP.read_holding_registers(41102, slave=1).registers[0] + match val: + case 0: + return "AUS" + case 1: + return "WITTERUNGSGEFÜHRT" + case 2: + return "KONSTANT" + + @property + def HK_Betriebsart(self): + """Energy used today.""" + val = self.WWP.read_holding_registers(41103, slave=1).registers[0] + + match val: + case 0: + return "AUTOMATIK" + case 1: + return "KOMFORT" + case 2: + return "NORMAL" + case 3: + return "ABSENKBETRIEB" + case 5: + return "STANDBY" + + @property + def HK_Pause_Party(self): + """Energy used today.""" + val = self.WWP.read_holding_registers(41104, slave=1).registers[0] + if val == 25: + return "Automatik" + if val < 25: + time = (25 - val) * 0.5 + return "Pausenzeit " + time + "h" + if val > 25: + time = (val - 25) * 0.5 + return "Partyzeit " + val * 0.5 + "h" + + ##################### + # Warm Water # + ##################### + @property + def WW_Soll(self): + """Test.""" + return self.WWP.read_holding_registers(42103, slave=1).registers[0] / 10 + + @WW_Soll.setter + def WW_Soll(self, value): + self.WWP.write_register(42103, value * 10, slave=1) + + @property + def WW_Ist(self): + """Temperature of warm-water.""" + return self.WWP.read_input_registers(32102, slave=1).registers[0] / 10 + + @property + def WW_Soll_info(self): + """Temperature of warm-water.""" + return self.WWP.read_input_registers(32101, slave=1).registers[0] / 10 + + ##################### + # Heatpump # + ##################### + @property + def Hp_Betrieb(self): # noqa: C901 + """Energy used today.""" + val = self.WWP.read_input_registers(33101, slave=1).registers[0] + match val: + case 0: + return "Undefiniert" + case 1: + return "Relaistest" + case 2: + return "Notaus" + case 3: + return "Diagnose" + case 4: + return "Handbetrieb" + case 5: + return "Handbetrieb Heizen" + case 6: + return "Handbetrieb Kühlen" + case 7: + return "Manueller Abtaubetrieb" + case 8: + return "Abtauen" + case 9: + return "WEZ2" + case 10: + return "EVU_SPERRE" + case 11: + return "SG Tarif" + case 12: + return "SG Maximal" + case 13: + return "Tarifladung" + case 14: + return "Erhöhter Betrieb" + case 15: + return "Standzeit" + case 16: + return "Standbybetrieb" + case 17: + return "Spülbetrieb" + case 18: + return "Frostschutz" + case 19: + return "Heizbetrieb" + case 20: + return "Warmwasserbetrieb" + case 21: + return "Legionellenschutz" + case 22: + return "Umschaltung HZ KU" + case 23: + return "Kühlbetrieb" + case 24: + return "Passive Kühlung" + case 25: + return "Sommerbetrieb" + case 26: + return "Schwimmbad" + case 27: + return "Urlaub" + case 28: + return "Estrich" + case 29: + return "Gesperrt" + case 30: + return "Sperre AT" + case 31: + return "Sperre Sommer" + case 32: + return "Sperre Winter" + case 33: + return "Einsatzgrenze" + case 34: + return "HK Sperre" + case 35: + return "Absenk" + + @property + def Hp_Stoermeldung(self): + """Energy used today.""" + val = self.WWP.read_input_registers(33102, slave=1).registers[0] + match val: + case 0: + return "Störung" + case 1: + return "Störungsfrei" + + @property + def Hp_Leistungsanforderung(self): + """Energy used today.""" + return self.WWP.read_input_registers(33103, slave=1).registers[0] + + @property + def Hp_Vorlauftemperatur(self): + """Energy used today.""" + return self.WWP.read_input_registers(33104, slave=1).registers[0] / 10 + + @property + def Hp_Ruecklauftemperatur(self): + """Energy used today.""" + return self.WWP.read_input_registers(33105, slave=1).registers[0] / 10 + + ##################### + # Statistics # + ##################### + @property + def Energy_total_today(self): + """Energy used today.""" + return self.WWP.read_input_registers(36101, slave=1).registers[0] + + @property + def Energy_total_yesterday(self): + """Energy used yesterday.""" + return self.WWP.read_input_registers(36102, slave=1).registers[0] + + @property + def Energy_total_month(self): + """Energy used month.""" + return self.WWP.read_input_registers(36103, slave=1).registers[0] + + @property + def Energy_total_year(self): + """Energy used year.""" + return self.WWP.read_input_registers(36104, slave=1).registers[0] + + @property + def Heating_total_today(self): + """Energy used for heating today.""" + return self.WWP.read_input_registers(36201, slave=1).registers[0] + + @property + def Heating_total_yesterday(self): + """Energy used for heating yesterday.""" + return self.WWP.read_input_registers(36202, slave=1).registers[0] + + @property + def Heating_total_month(self): + """Energy used for heating month.""" + return self.WWP.read_input_registers(36203, slave=1).registers[0] + + @property + def Heating_total_year(self): + """Energy used for heating year.""" + return self.WWP.read_input_registers(36204, slave=1).registers[0] + + @property + def Water_total_today(self): + """Energy used for heating water today.""" + return self.WWP.read_input_registers(36301, slave=1).registers[0] + + @property + def Water_total_yesterday(self): + """Energy used for heating water yesterday.""" + return self.WWP.read_input_registers(36302, slave=1).registers[0] + + @property + def Water_total_month(self): + """Energy used for heating water month.""" + return self.WWP.read_input_registers(36303, slave=1).registers[0] + + @property + def Water_total_year(self): + """Energy used for heating water year.""" + return self.WWP.read_input_registers(36304, slave=1).registers[0] + + @property + def Cooling_total_today(self): + """Energy used for cooling.""" + return self.WWP.read_input_registers(36401, slave=1).registers[0] + + @property + def Cooling_total_yesterday(self): + """Energy used for cooling.""" + return self.WWP.read_input_registers(36402, slave=1).registers[0] + + @property + def Cooling_total_month(self): + """Energy used for cooling.""" + return self.WWP.read_input_registers(36403, slave=1).registers[0] + + @property + def Cooling_total_year(self): + """Energy used for cooling.""" + return self.WWP.read_input_registers(36404, slave=1).registers[0] + + +# whp = heat_pump(hp_ip, hp_port) +# whp.connect() +# print(whp.WW_Soll) +# whp.WW_Soll = 44 +# print(whp.WW_Soll) +# whp.WW_Soll = 45 +# print(whp.WW_Soll) + +# print(whp.WW_Ist)