Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/jnimmo/pyIntesisHome
Browse files Browse the repository at this point in the history
  • Loading branch information
jnimmo committed Mar 6, 2021
2 parents b19a3c5 + e8121fc commit e911dcb
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var/
*.egg-info/
.installed.cfg
*.egg
.vscode/

# PyInstaller
# Usually these files are written by a python script from a template
Expand Down
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

57 changes: 57 additions & 0 deletions examples/dhw_aquarea.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env python3.7

import requests
from optparse import OptionParser
from pyintesishome import IntesisHome
import sys
import pycurl
import re
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
import time
from datetime import datetime
import asyncio
import logging
try:
from io import BytesIO
except ImportError:
from StringIO import StringIO as BytesIO

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)s.%(funcName)s +%(lineno)s: %(levelname)-8s [%(process)d] %(message)s',)

async def main(loop):
#def main():

username="xxxxxx"
password="yyyyyyy"
idd = zzzzzzzzzzz
aquarea = None
msgs = None

aquarea = IntesisHome(username, password, loop=loop)
await aquarea.connect()
await aquarea.poll_status()

today = datetime.today()
tank_temp = aquarea._get_gen_num_value(idd, "tank_water_temperature")
outdoor_temp = aquarea.get_outdoor_temperature(idd)
tank_set_point = aquarea._get_gen_num_value(idd,"tank_setpoint_temperature")
water_outlet_temp = aquarea._get_gen_num_value(idd,"water_outlet_temperature")
water_inlet_temp = aquarea._get_gen_num_value(idd,"water_inlet_temperature")
mode = aquarea.get_mode(idd)
water_target_temp = aquarea._get_gen_num_value(idd,"water_target_temperature")
wifi_signal = aquarea.get_rssi(idd)
power = aquarea.get_power_state(idd)
cool_setpoint = aquarea._get_gen_num_value(idd,"cool_water_setpoint_temperature")

print(today.strftime("%Y-%m-%d %H:%M:%S: ") + f"Mode = {mode}, Tank temp = {tank_temp:.1f}°C, Tank set point = {tank_set_point:.1f}°C, WIFI signal = {wifi_signal}, Power = {power}")
if (power == 'on' and (mode == 'heat' or mode == 'heat+tank')):
print(today.strftime("%Y-%m-%d %H:%M:%S: ") + f"Water Outlet temp = {water_outlet_temp:.1f}°C, Water Inlet temp = {water_inlet_temp:.1f}°C, Water Target temp = {water_target_temp:.1f}°C, Outdoor temp = {outdoor_temp:.1f}°C")
elif (power == 'on' and (mode == 'cool' or mode == 'cool+tank')):
print(today.strftime("%Y-%m-%d %H:%M:%S: ") + f"Water Outlet temp = {water_outlet_temp:.1f}°C, Water Inlet temp = {water_inlet_temp:.1f}°C, Water Target temp = {cool_setpoint:.1f}°C, Outdoor temp = {outdoor_temp:.1f}°C")


if __name__ == "__main__":
loop = asyncio.get_event_loop()
result = loop.run_until_complete(main(loop))

141 changes: 141 additions & 0 deletions examples/dhw_aquarea_domoticz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/usr/bin/env python3.7

import requests
from optparse import OptionParser
from pyintesishome import IntesisHome
import sys
import pycurl
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
import time
from datetime import datetime
import asyncio
import logging
try:
from io import BytesIO
except ImportError:
from StringIO import StringIO as BytesIO

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)s.%(funcName)s +%(lineno)s: %(levelname)-8s [%(process)d] %(message)s',)

def aquarea_to_domoticz(broker,
port,
temp_idx,
tank_set_point_idx,
water_set_point_idx,
ext_temp_idx,
out_temp_idx,
in_temp_idx,
power_idx,
date,
power,
mode,
tank_temp,
tank_set_point,
water_target_temp,
outdoor_temp,
water_outlet_temp,
water_inlet_temp,
cool_setpoint
):
msgs = ''
if (mode == 'tank' or power == 'off'):
msgs = [{'topic':"domoticz/in", 'payload':"{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (temp_idx, str(tank_temp))},
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (tank_set_point_idx, str(tank_set_point)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (ext_temp_idx, str(outdoor_temp)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":%d}" % (power_idx, 1 if power == 'on' else 0), 0, False)
]
elif (power == 'on' and (mode == 'heat' or mode == 'heat+tank' or mode == 'cool+tank' or mode == 'cool')):
if (mode == 'heat' or mode == 'heat+tank'):
msgs = [{'topic':"domoticz/in", 'payload':"{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (temp_idx, str(tank_temp))},
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (tank_set_point_idx, str(tank_set_point)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (water_set_point_idx, str(water_target_temp)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (ext_temp_idx, str(outdoor_temp)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (out_temp_idx, str(water_outlet_temp)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":%d}" % (power_idx, 1 if power == 'on' else 0), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (in_temp_idx, str(water_inlet_temp)), 0, False)
]
else:
msgs = [{'topic':"domoticz/in", 'payload':"{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (temp_idx, str(tank_temp))},
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (tank_set_point_idx, str(tank_set_point)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (water_set_point_idx, str(cool_setpoint)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (ext_temp_idx, str(outdoor_temp)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (out_temp_idx, str(water_outlet_temp)), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":%d}" % (power_idx, 1 if power == 'on' else 0), 0, False),
("domoticz/in", "{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}" % (in_temp_idx, str(water_inlet_temp)), 0, False)
]

print(msgs)

print(broker)
rc = publish.multiple(msgs, hostname=broker, port=port, client_id="pyIntesisHome2")
print(date.strftime("%Y-%m-%d %H:%M:%S: ") + "Publish: %s" % (rc))


async def main(loop):

username="xxxxxx"
password="yyyyyyyyy"
idd = zzzzzzzz
aquarea = None
msgs = None

aquarea = IntesisHome(username, password, loop=loop)
await aquarea.connect()
await aquarea.poll_status()

today = datetime.today()
tank_temp = aquarea._get_gen_num_value(idd, "tank_water_temperature")
outdoor_temp = aquarea.get_outdoor_temperature(idd)
tank_set_point = aquarea._get_gen_num_value(idd,"tank_setpoint_temperature")
water_outlet_temp = aquarea._get_gen_num_value(idd,"water_outlet_temperature")
water_inlet_temp = aquarea._get_gen_num_value(idd,"water_inlet_temperature")
mode = aquarea.get_mode(idd)
water_target_temp = aquarea._get_gen_num_value(idd,"water_target_temperature")
wifi_signal = aquarea.get_rssi(idd)
power = aquarea.get_power_state(idd)
cool_setpoint = aquarea._get_gen_num_value(idd,"cool_water_setpoint_temperature")

print(today.strftime("%Y-%m-%d %H:%M:%S: ") + f"Mode = {mode}, Tank temp = {tank_temp:.1f}°C, Tank set point = {tank_set_point:.1f}°C, WIFI signal = {wifi_signal}, Power = {power}")
if (power == 'on' and (mode == 'heat' or mode == 'heat+tank')):
print(today.strftime("%Y-%m-%d %H:%M:%S: ") + f"Water Outlet temp = {water_outlet_temp:.1f}°C, Water Inlet temp = {water_inlet_temp:.1f}°C, Water Target temp = {water_target_temp:.1f}°C, Outdoor temp = {outdoor_temp:.1f}°C")
elif (power == 'on' and (mode == 'cool' or mode == 'cool+tank')):
print(today.strftime("%Y-%m-%d %H:%M:%S: ") + f"Water Outlet temp = {water_outlet_temp:.1f}°C, Water Inlet temp = {water_inlet_temp:.1f}°C, Water Target temp = {cool_setpoint:.1f}°C, Outdoor temp = {outdoor_temp:.1f}°C")

if (wifi_signal > 0):
temp_idx = 8
tank_set_point_idx = 9
water_set_point_idx = 13
ext_temp_idx = 12
out_temp_idx = 10
in_temp_idx = 11
power_idx = 14

broker = "192.168.2.32"
port = 1883

aquarea_to_domoticz(broker,
port,
temp_idx,
tank_set_point_idx,
water_set_point_idx,
ext_temp_idx,
out_temp_idx,
in_temp_idx,
power_idx,
today,
power,
mode,
tank_temp,
tank_set_point,
water_target_temp,
outdoor_temp,
water_outlet_temp,
water_inlet_temp,
cool_setpoint)

if __name__ == "__main__":
loop = asyncio.get_event_loop()
result = loop.run_until_complete(main(loop))
#main()

122 changes: 117 additions & 5 deletions pyintesishome/pyintesishome.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
46: {"name": "solar_status"},
48: {"name": "thermoshift_heat_eco"},
49: {"name": "thermoshift_cool_eco"},
50: {"name": "thermoshift_heat_powerful"},
51: {"name": "thermoshift_cool_powerful"},
52: {"name": "thermoshift_tank_eco"},
53: {"name": "thermoshift_tank_powerful"},
Expand Down Expand Up @@ -250,7 +251,72 @@
"manual5": 5,
},
},
"setpoint": {"uid": 9},
"setpoint": {"uid": 9}
# aquarea
,"quiet": {"uid": 34, "values": {"off": 0, "on": 1}}
,"tank": {"uid": 44, "values": {"comfort": 0, "eco": 1, "powerful": 2}}
,"reset_eror": {"uid": 54, "values": {"on": 1}}
,"tank_setpoint_temperature": {"uid": 57}
,"thermoshift_heat_eco": {"uid": 48, "min": 0, "max": 5}
,"thermoshift_cool_eco": {"uid": 49, "min": 0, "max": 5} # 172
,"thermoshift_heat_powerful": {"uid": 50, "min": 0, "max": 5}
,"thermoshift_cool_powerful": {"uid": 51, "min": 0, "max": 5} # 171
,"thermoshift_tank_eco": {"uid": 52, "min": 0, "max": 10}
,"thermoshift_tank_powerful": {"uid": 53, "min": 0, "max": 10}
,"heat_thermo_shift": {"uid": 55, "min": -5, "max": 5}
,"cool_water_setpoint_temperature": {"uid": 56}
,"heat_high_water_set_temperature": {"uid": 83, "min": 25, "max": 55}
,"heat_low_outdoor_set_temperature": {"uid": 134, "min": -15, "max": 15}
,"heat_high_outdoor_set_temperature": {"uid": 135, "min": -15, "max": 15}
,"heat_low_water_set_temperature": {"uid": 136, "min": 25, "max": 55}
,"resync": {"uid": 143, "values": {"on": 1}}
,"remote_control_block": {"uid": 12, "values": {"on": 2, "off": 0}}
}

# aquarea
ERROR_MAP = {
0 : { 'code': 'H00', 'desc': 'No abnormality detected'},
2 : { 'code': 'H91', 'desc': 'Tank booster heater OLP abnormality'},
13 : { 'code': 'F38', 'desc': 'Unknown'},
20 : { 'code': 'H90', 'desc': 'Indoor / outdoor abnormal communication'},
36 : { 'code': 'H99', 'desc': 'Indoor heat exchanger freeze prevention'},
38 : { 'code': 'H72', 'desc': 'Tank temperature sensor abnormality'},
42 : { 'code': 'H12', 'desc': 'Indoor / outdoor capacity unmatched'},
156 : { 'code': 'H76', 'desc': 'Indoor - control panel communication abnormality'},
193 : { 'code': 'F12', 'desc': 'Pressure switch activate'},
195 : { 'code': 'F14', 'desc': 'Outdoor compressor abnormal rotation'},
196 : { 'code': 'F15', 'desc': 'Outdoor fan motor lock abnormality'},
197 : { 'code': 'F16', 'desc': 'Total running current protection'},
200 : { 'code': 'F20', 'desc': 'Outdoor compressor overheating protection'},
202 : { 'code': 'F22', 'desc': 'IPM overheating protection'},
203 : { 'code': 'F23', 'desc': 'Outdoor DC peak detection'},
204 : { 'code': 'F24', 'desc': 'Refrigerant cycle abnormality'},
205 : { 'code': 'F27', 'desc': 'Pressure switch abnormality'},
207 : { 'code': 'F46', 'desc': 'Outdoor current transformer open circuit'},
208 : { 'code': 'F36', 'desc': 'Outdoor air temperature sensor abnormality'},
209 : { 'code': 'F37', 'desc': 'Indoor water inlet temperature sensor abnormality'},
210 : { 'code': 'F45', 'desc': 'Indoor water outlet temperature sensor abnormality'},
212 : { 'code': 'F40', 'desc': 'Outdoor discharge pipe temperature sensor abnormality'},
214 : { 'code': 'F41', 'desc': 'PFC control'},
215 : { 'code': 'F42', 'desc': 'Outdoor heat exchanger temperature sensor abnormality'},
216 : { 'code': 'F43', 'desc': 'Outdoor defrost temperature sensor abnormality'},
222 : { 'code': 'H95', 'desc': 'Indoor / outdoor wrong connection'},
224 : { 'code': 'H15', 'desc': 'Outdoor compressor temperature sensor abnormality'},
225 : { 'code': 'H23', 'desc': 'Indoor refrigerant liquid temperature sensor abnormality'},
226 : { 'code': 'H24', 'desc': 'Unknown'},
227 : { 'code': 'H38', 'desc': 'Indoor / outdoor mismatch'},
228 : { 'code': 'H61', 'desc': 'Unknown'},
229 : { 'code': 'H62', 'desc': 'Water flow switch abnormality'},
230 : { 'code': 'H63', 'desc': 'Refrigerant low pressure abnormality'},
231 : { 'code': 'H64', 'desc': 'Refrigerant high pressure abnormality'},
232 : { 'code': 'H42', 'desc': 'Compressor low pressure abnormality'},
233 : { 'code': 'H98', 'desc': 'Outdoor high pressure overload protection'},
234 : { 'code': 'F25', 'desc': 'Cooling / heating cycle changeover abnormality'},
235 : { 'code': 'F95', 'desc': 'Cooling high pressure overload protection'},
236 : { 'code': 'H70', 'desc': 'Indoor backup heater OLP abnormality'},
237 : { 'code': 'F48', 'desc': 'Outdoor EVA outlet temperature sensor abnormality'},
238 : { 'code': 'F49', 'desc': 'Outdoor bypass outlet temperature sensor abnormality'},
65535 : { 'code': 'N/A', 'desc': 'Communication error between PA-IntesisHome'}
}

API_URL = {
Expand Down Expand Up @@ -492,12 +558,12 @@ async def poll_status(self, sendcallback=False):
) as resp:
status_response = await resp.json(content_type=None)
_LOGGER.debug(status_response)
except (aiohttp.client_exceptions.ClientConnectorError) as e:
raise IHConnectionError
except (aiohttp.client_exceptions.ClientError, socket.gaierror) as e:
self._errorMessage = f"Error connecting to {self._device_type} API: {e}"
_LOGGER.error(f"{type(e)} Exception. {repr(e.args)} / {e}")
raise IHConnectionError
except (aiohttp.client_exceptions.ClientConnectorError) as e:
raise IHConnectionError

if status_response:
if "errorCode" in status_response:
Expand Down Expand Up @@ -547,6 +613,10 @@ async def poll_status(self, sendcallback=False):

return self._authToken

def _get_uint32(self, value):
result = int(value) & 0xffff
return result

def get_run_hours(self, deviceId) -> str:
"""Public method returns the run hours of the IntesisHome controller."""
run_hours = self._devices[str(deviceId)].get("working_hours")
Expand Down Expand Up @@ -576,7 +646,7 @@ async def set_preset_mode(self, deviceId, preset: str):

async def set_temperature(self, deviceId, setpoint):
"""Public method for setting the temperature"""
set_temp = int(setpoint * 10)
set_temp = self._get_uint32((setpoint * 10))
await self._set_value(deviceId, COMMAND_MAP["setpoint"]["uid"], set_temp)

async def set_fan_speed(self, deviceId, fan: str):
Expand Down Expand Up @@ -830,7 +900,49 @@ def twos_complement_16bit(val):
"""Internal method to compute Two's Complement, to represent negative temperatures"""
if (val & (1 << 15)) != 0:
val = val - (1 << 16)
return val
return val

def get_error(self, deviceId) -> str:
"""Public method returns the current error code + description."""
error_code = self._devices[str(deviceId)].get('error_code')
remote_code = ERROR_MAP[error_code]['code']
error_desc = ERROR_MAP[error_code]['desc']
return (("%s: %s" % (remote_code, error_desc)))

def _get_gen_value(self, deviceId, name) -> str:
""" Internal method for getting generic value """
value = None
if name in self._devices[str(deviceId)]:
value = self._devices[str(deviceId)].get(name)
_LOGGER.debug(f"{name} = {value}")
else:
_LOGGER.debug(f"No value for {deviceId} {name}")
return value

def _get_gen_num_value(self, deviceId, name):
""" Internal method for getting generic value and dividing by 10 if numeric """
value = self._get_gen_value(deviceId, name)
if (isinstance(value, int) or isinstance(value, float)):
temperature = float(value / 10)
return temperature
else:
return value

def _set_gen_mode(self, deviceId, type, mode):
"""Internal method for setting the generic mode (type in {operating_mode, climate_working_mode, tank, etc.}) with a string value"""
if mode in COMMAND_MAP[type]['values']:
self._set_value( deviceId, COMMAND_MAP[type]['uid'], COMMAND_MAP[type]['values'][mode])

def _set_thermo_shift(self, deviceId, name, value):
"""Public method to set thermo shift temperature."""
min_shift = int(COMMAND_MAP[name]['min'])
max_shift = int(COMMAND_MAP[name]['max'])

if (min_shift <= value <= max_shift):
unsigned_value = self._get_uint32((value*10)) # unsigned int 16 bit
self._set_value(deviceId, COMMAND_MAP[name]['uid'], unsigned_value)
else:
raise ValueError("Value for %s has to be in range [%d,%d]" % name, min_shift, max_shift)

@property
def is_connected(self) -> bool:
Expand Down

0 comments on commit e911dcb

Please sign in to comment.