├── .gitattributes ├── .gitignore ├── README.md └── python ├── BoilerCLI.py ├── PoC.py ├── credentials.sample.json └── shared ├── NavienSmartControl.py └── __init__.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.pyc 4 | 5 | # Secret credentials. 6 | /python/credentials.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Navien-API 2 | A basic API for getting information about and controlling your Navien boiler. -------------------------------------------------------------------------------- /python/BoilerCLI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Support Python3 in Python2. 4 | from __future__ import print_function 5 | 6 | # The NavienSmartControl code is in a library. 7 | from shared.NavienSmartControl import NavienSmartControl, ModeState, OperateMode, HeatLevel 8 | 9 | # The credentials are loaded from a separate file. 10 | import json 11 | 12 | # We support command line arguments. 13 | import argparse 14 | 15 | # We use the system package for interaction with the OS. 16 | import sys 17 | 18 | # This script's version. 19 | version = 0.1 20 | 21 | # Check the user is invoking us directly rather than from a module. 22 | if __name__ == '__main__': 23 | 24 | # Output program banner. 25 | print('--------------') 26 | print('Navien-API V' + str(version)) 27 | print('--------------') 28 | print() 29 | 30 | # Get an initialised parser object. 31 | parser = argparse.ArgumentParser(description='Control a Navien boiler.', prefix_chars='-/') 32 | parser.add_argument('/roomtemp', '-roomtemp', type=float, help='Set the indoor room temperature to this value.') 33 | parser.add_argument('/heatingtemp', '-heatingtemp', type=float, help='Set the central heating temperature to this value.') 34 | parser.add_argument('/hotwatertemp', '-hotwatertemp', type=float, help='Set the hot water temperature to this value.') 35 | parser.add_argument('/heatlevel', '-heatlevel', type=int, choices={1,2,3}, help='Set the boiler\'s heat level.') 36 | parser.add_argument('/status', '-status', action='store_true', help='Show the boiler\'s simple status.') 37 | parser.add_argument('/summary', '-summary', action='store_true', help='Show the boiler\'s extended status.') 38 | parser.add_argument('/mode', '-mode', choices={'PowerOff', 'PowerOn', 'HolidayOn', 'HolidayOff', 'SummerOn', 'SummerOff', 'QuickHotWater'}, help='Set the boiler\'s mode.') 39 | 40 | # The following function provides arguments for calling functions when command line switches are used. 41 | args = parser.parse_args() 42 | 43 | # Were arguments specified? 44 | if len(sys.argv)==1: 45 | parser.print_help(sys.stderr) 46 | 47 | # Yes, there was. 48 | else: 49 | 50 | # Load credentials. 51 | with open('credentials.json', 'r') as in_file: 52 | credentials = json.load(in_file) 53 | 54 | # Create a reference to the NavienSmartControl library. 55 | navienSmartControl = NavienSmartControl(credentials['Username'], credentials['Password']) 56 | 57 | # Perform the login. 58 | encodedUserID = navienSmartControl.login() 59 | 60 | # Load the list of devices connected. 61 | gatewayList = navienSmartControl.gatewayList(encodedUserID) 62 | 63 | # The first 16 characters (8 bytes represented as hex characters) is the DeviceID (only seen with a valid response). 64 | if len(gatewayList[0]) == 16: 65 | 66 | # Connect to the socket. 67 | homeState = navienSmartControl.connect(gatewayList[0]) 68 | 69 | # We can provide a full summary. 70 | if args.summary: 71 | # Print out the gateway list information. 72 | print('---------------------------') 73 | print('Device ID: ' + gatewayList[0]) 74 | print('Ready?: ' + gatewayList[1]) 75 | print('Unknown 1: ' + gatewayList[2]) 76 | print('Connected: ' + gatewayList[3]) 77 | print('Last Seen: ' + gatewayList[4]) 78 | print('Unknown 2-4: ' + gatewayList[5] + '/' + gatewayList[6] + '/' + gatewayList[7]) 79 | print('IP Address: ' + gatewayList[8]) 80 | print('TCP Port Number: ' + gatewayList[9]) 81 | print('---------------------------\n') 82 | 83 | # Print out the current status. 84 | navienSmartControl.printHomeState(homeState) 85 | print() 86 | 87 | # We provide a quick status. 88 | if args.status: 89 | 90 | print('Current Mode: ', end = '') 91 | if homeState.currentMode == ModeState.POWER_OFF.value: 92 | print('Powered Off') 93 | elif homeState.currentMode == ModeState.GOOUT_ON.value: 94 | print('Holiday Mode') 95 | elif homeState.currentMode == ModeState.INSIDE_HEAT.value: 96 | print('Room Temperature Control') 97 | elif homeState.currentMode == ModeState.ONDOL_HEAT.value: 98 | print('Central Heating Control') 99 | elif homeState.currentMode == ModeState.SIMPLE_RESERVE.value: 100 | print('Heating Inteval') 101 | elif homeState.currentMode == ModeState.CIRCLE_RESERVE.value: 102 | print('24 Hour Program') 103 | elif homeState.currentMode == ModeState.HOTWATER_ON.value: 104 | print('Hot Water Only') 105 | else: 106 | print(str(homeState.currentMode)) 107 | print('Current Operation: ' + ('Active' if homeState.operateMode & OperateMode.ACTIVE.value else 'Inactive')) 108 | print() 109 | 110 | print('Room Temperature : ' + str(navienSmartControl.getTemperatureFromByte(homeState.currentInsideTemp))+ ' °C') 111 | print() 112 | 113 | if homeState.currentMode == ModeState.INSIDE_HEAT.value: 114 | print('Inside Heating Temperature: ' + str(navienSmartControl.getTemperatureFromByte(homeState.insideHeatTemp)) + ' °C') 115 | elif homeState.currentMode == ModeState.ONDOL_HEAT.value: 116 | print('Central Heating Temperature: ' + str(navienSmartControl.getTemperatureFromByte(homeState.ondolHeatTemp)) + ' °C') 117 | 118 | print('Hot Water Set Temperature : ' + str(navienSmartControl.getTemperatureFromByte(homeState.hotWaterSetTemp)) + ' °C') 119 | 120 | # Change the mode. 121 | if args.mode: 122 | 123 | # Various allowed mode toggles. 124 | if args.mode == 'PowerOff': 125 | navienSmartControl.setPowerOff(homeState) 126 | elif args.mode == 'PowerOn': 127 | navienSmartControl.setPowerOn(homeState) 128 | elif args.mode == 'HolidayOn': 129 | navienSmartControl.setGoOutOn(homeState) 130 | elif args.mode == 'HolidayOff': 131 | navienSmartControl.setGoOutOff(homeState) 132 | elif args.mode == 'SummerOn': 133 | navienSmartControl.setHotWaterOn(homeState) 134 | elif args.mode == 'SummerOff': 135 | navienSmartControl.setHotWaterOff(homeState) 136 | elif args.mode == 'QuickHotWater': 137 | navienSmartControl.setQuickHotWater(homeState) 138 | 139 | # Update user. 140 | print('Mode now set to ' + str(args.mode) + '.') 141 | 142 | # Change the heat level. 143 | if args.heatlevel: 144 | navienSmartControl.setHeatLevel(homeState, HeatLevel(args.heatlevel)) 145 | print('Heat level now set to ' + str(HeatLevel(args.heatlevel)) + '.') 146 | 147 | # Change the room temperature. 148 | if args.roomtemp: 149 | navienSmartControl.setInsideHeat(homeState, args.roomtemp) 150 | print('Indoor temperature now set to ' + str(args.roomtemp) + '°C.') 151 | 152 | # Change the central heating system's temperature. 153 | if args.heatingtemp: 154 | navienSmartControl.setOndolHeat(homeState, args.heatingtemp) 155 | print('Central heating temperature now set to ' + str(args.heatingtemp) + '°C.') 156 | 157 | # Change the room temperature. 158 | if args.hotwatertemp: 159 | navienSmartControl.setHotWaterHeat(homeState, args.hotwatertemp) 160 | print('Hot water temperature now set to ' + str(args.hotwatertemp) + '°C.') 161 | 162 | else: 163 | raise ValueError('Bad gateway list returned.') -------------------------------------------------------------------------------- /python/PoC.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Support Python3 in Python2. 4 | from __future__ import print_function 5 | 6 | # The NavienSmartControl code is in a library. 7 | from shared.NavienSmartControl import NavienSmartControl 8 | 9 | # The credentials are loaded from a separate file. 10 | import json 11 | 12 | # Load credentials. 13 | with open('credentials.json', 'r') as in_file: 14 | credentials = json.load(in_file) 15 | 16 | # Create a reference to the NavienSmartControl library. 17 | navienSmartControl = NavienSmartControl(credentials['Username'], credentials['Password']) 18 | 19 | # Perform the login. 20 | encodedUserID = navienSmartControl.login() 21 | 22 | # Load the list of devices connected. 23 | gatewayList = navienSmartControl.gatewayList(encodedUserID) 24 | 25 | # The first 16 characters (8 bytes represented as hex characters) is the DeviceID (only seen with a valid response). 26 | if len(gatewayList[0]) == 16: 27 | 28 | # Print out the gateway list information. 29 | print('---------------------------') 30 | print('Device ID: ' + gatewayList[0]) 31 | print('Ready?: ' + gatewayList[1]) 32 | print('Unknown 1: ' + gatewayList[2]) 33 | print('Connected: ' + gatewayList[3]) 34 | print('Last Seen: ' + gatewayList[4]) 35 | print('Unknown 2-4: ' + gatewayList[5] + '/' + gatewayList[6] + '/' + gatewayList[7]) 36 | print('IP Address: ' + gatewayList[8]) 37 | print('TCP Port Number: ' + gatewayList[9]) 38 | print('---------------------------\n') 39 | 40 | # Connect to the socket. 41 | homeState = navienSmartControl.connect(gatewayList[0]) 42 | 43 | # Print out the current status. 44 | navienSmartControl.printHomeState(homeState) 45 | 46 | # Change the temperature. 47 | #navienSmartControl.setInsideHeat(homeState, 19.0) 48 | 49 | else: 50 | raise ValueError('Bad gateway list returned.') -------------------------------------------------------------------------------- /python/credentials.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "Username": "MyUsername", 3 | "Password": "MyPassword" 4 | } -------------------------------------------------------------------------------- /python/shared/NavienSmartControl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Third party library; "pip install requests" if getting import errors. 4 | import requests 5 | 6 | # We use raw sockets. 7 | import socket 8 | 9 | # We unpack structures. 10 | import struct 11 | 12 | # We use namedtuple to reduce index errors. 13 | import collections 14 | 15 | # We use binascii to convert some consts from hex. 16 | import binascii 17 | 18 | # We use Python enums. 19 | import enum 20 | 21 | class OperateMode(enum.Enum): 22 | POWER_OFF = 1 23 | POWER_ON = 2 24 | GOOUT_OFF = 3 25 | GOOUT_ON = 4 26 | INSIDE_HEAT = 5 27 | ONDOL_HEAT = 6 28 | REPEAT_RESERVE = 7 29 | CIRCLE_RESERVE = 8 30 | SIMPLE_RESERVE = 9 31 | HOTWATER_ON = 10 32 | HOTWATER_OFF = 11 33 | WATER_SET_TEMP = 12 34 | QUICK_HOTWATER = 13 35 | HEAT_LEVEL = 14 36 | ACTIVE = 128 37 | 38 | class ModeState(enum.Enum): 39 | POWER_OFF = 1 40 | GOOUT_ON = 2 41 | INSIDE_HEAT = 3 42 | ONDOL_HEAT = 4 43 | SIMPLE_RESERVE = 5 44 | CIRCLE_RESERVE = 6 45 | HOTWATER_ON = 8 46 | 47 | class HeatLevel(enum.Enum): 48 | LOW = 1 49 | MEDIUM = 2 50 | HIGH = 3 51 | 52 | class TempControlType(enum.IntFlag): 53 | 54 | # 3rd bit. 55 | POINTINSIDE = 32 56 | 57 | # 4th bit. 58 | POINTONDOL = 16 59 | 60 | # 5th bit. 61 | POINTWATER = 8 62 | 63 | # 6th - 8th bits (last 3 bits). 64 | WATERMODE = 7 65 | 66 | class NavienSmartControl: 67 | 68 | # This prevents the requests module from creating its own user-agent. 69 | stealthyHeaders = {'User-Agent': None } 70 | 71 | # The Navien server. 72 | navienServer = 'ukst.naviensmartcontrol.com' 73 | navienWebServer = 'https://' + navienServer 74 | navienServerSocketPort = 6001 75 | 76 | def __init__(self, userID, passwd): 77 | self.userID = userID 78 | self.passwd = passwd 79 | self.connection = None 80 | 81 | def login(self): 82 | # Login. 83 | response = requests.post(NavienSmartControl.navienWebServer + '/mobile_login_check.asp', headers=NavienSmartControl.stealthyHeaders, data={'UserID': self.userID, 'Passwd': self.passwd, 'BundleVersion': '8', 'AutoLogin': '1', 'smartphoneID': '2'}) 84 | 85 | # If an error occurs this will raise it, otherwise it returns the encodedUserID (this is just the BASE64 UserID typically). 86 | return self.handleResponse(response) 87 | 88 | # This is the list of the details for the boiler controller or "gateway". Note how no login state is required. 89 | def gatewayList(self, encodedUserID): 90 | # Get the list of connected devices. 91 | response = requests.post(NavienSmartControl.navienWebServer + '/mobile_gateway_list.asp', headers=NavienSmartControl.stealthyHeaders, data={'UserID': encodedUserID, 'Ticket':'0'}) 92 | 93 | # The server replies with a pipe separated response. 94 | return self.handleResponse(response) 95 | 96 | def handleResponse(self, response): 97 | 98 | # The server replies with a pipe separated response. 99 | response_status = response.text.split('|') 100 | 101 | # The first value is either a status code or sometimes a raw result. 102 | response_status_code = response_status[0] 103 | 104 | if response_status_code == '0': 105 | raise Exception('Error: Controller not connected to the Internet server; please check your Wi-Fi network and wait until the connection to the Internet server is restored automatically.') 106 | elif response_status_code == '1': 107 | raise Exception('Error: Login details incorrect. Please note, these are case-sensitive.') 108 | elif response_status_code == '2': 109 | raise Exception('Error: The ID you have chosen is already in use.') 110 | elif response_status_code == '3': 111 | return response_status[1] 112 | elif response_status_code == '4': 113 | raise Exception('Error: Invalid ID.') 114 | elif response_status_code == '9': 115 | raise Exception('Error: The Navien TOK account you have chosen is already in use by other users. Try again later.') 116 | elif response_status_code == '201': 117 | raise Exception('Error: The software is updated automatically and a continuous internet connection is required for this. If the router is not on continually, updates may be missed.') 118 | elif response_status_code == '202': 119 | if len(response_status) == 2: 120 | raise Exception('Error: Service inspection. Please wait until the inspection is done and try again. (Inspection hour:' + response_status[1] + ')') 121 | else: 122 | raise Exception('Error: Service inspection. Please wait until the inspection is done and try again.') 123 | elif response_status_code == '203': 124 | raise Exception('Error: Shutting down the service. Thank you for using this service. Closing the current program.') 125 | elif response_status_code == '210': 126 | raise Exception('Error: This version is too old.') 127 | elif response_status_code == '999': 128 | raise Exception('Error: Sorry. Please try again later.') 129 | else: 130 | return response_status 131 | 132 | def connect(self, controllerMACAddress): 133 | 134 | # Construct a socket object. 135 | self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 136 | 137 | # Connect to the socket server. 138 | self.connection.connect((NavienSmartControl.navienServer, NavienSmartControl.navienServerSocketPort)) 139 | 140 | # Request the boiler status. 141 | self.connection.sendall((self.userID + '$' + 'iPhone1.0' + '$' + controllerMACAddress + '\n').encode()) 142 | 143 | # Receive the boiler status. 144 | data = self.connection.recv(1024) 145 | 146 | # Return the parsed home state data. 147 | return self.parseHomeState(data) 148 | 149 | def parseHomeState(self, data): 150 | 151 | # The data is returned with a fixed header for the first 42 bytes. 152 | homeStateColumns = collections.namedtuple('homeState', ['deviceid','nationCode','hwRev','swRev','netType','controlType','boilerModelType','roomCnt','smsFg','errorCode','hotWaterSetTemp','heatLevel','optionUseFg','currentMode','currentInsideTemp','insideHeatTemp','ondolHeatTemp','repeatReserveHour','repeatReserveMinute','hour24ReserveTime1','hour24ReserveTime2','hour24ReserveTime3','simpleReserveSetTime','simpleReserveSetMinute','operateMode','tempControlType','hotwaterMin','hotwaterMax','ondolHeatMin','ondolHeatMax','insideHeatMin','insideHeatMax','reserve09', 'reserve10']) 153 | homeState = homeStateColumns._make(struct.unpack(' 8s B B B B B B B B H B B B B B B B B B B B B B B B B B B B B B B B B', data[:42])) 154 | 155 | # If the roomCnt > 1 then the remaining data will be room state information. 156 | if len(data) > 42: 157 | print('Warning : Extra roomState data found but not implemented in this version.') 158 | 159 | # These are hardcoded values to watch out for. 160 | if data == binascii.unhexlify('444444444400000000000000000000') or data == binascii.unhexlify('04040404040404040404'): 161 | raise Exception('An error occurred in the process of retrieving data; please restart to retry.') 162 | 163 | # Return the resulting parsed data. 164 | return homeState 165 | 166 | def printHomeState(self, homeState): 167 | print('Device ID: ' + ':'.join('%02x' % b for b in homeState.deviceid)) 168 | print('Country Code: ' + str(homeState.nationCode)) 169 | print('Hardware Revision: V' + str(homeState.hwRev)) 170 | print('Software Version: V' + str(homeState.swRev) + '.0') 171 | print('Network Type: ' + str(homeState.netType)) 172 | print('Control Type?: ' + str(homeState.controlType)) 173 | print('Boiler Model Type: ' + str(homeState.boilerModelType)) 174 | print('Room Controllers: ' + str(homeState.roomCnt)) 175 | print('smsFg?: ' + str(homeState.smsFg)) 176 | print('Error: ' + ('No Error' if homeState.errorCode == 0 else homeState.errorCode)) 177 | print('Hot Water Set Temperature: ' + str(self.getTemperatureFromByte(homeState.hotWaterSetTemp)) + ' °C') 178 | print('Heat Intensity Type: ' + [ 'Unknown', 'Low', 'Medium', 'High' ][homeState.heatLevel]) 179 | print('Option Use Flags: ' + bin(homeState.optionUseFg) + (' (Usable 24 Hour Reserve)' if homeState.optionUseFg & 128 == 128 else '')) 180 | print() 181 | 182 | print('Current Mode: ', end = '') 183 | if homeState.currentMode == ModeState.POWER_OFF.value: 184 | print('Powered Off') 185 | elif homeState.currentMode == ModeState.GOOUT_ON.value: 186 | print('Holiday Mode') 187 | elif homeState.currentMode == ModeState.INSIDE_HEAT.value: 188 | print('Room Temperature Control') 189 | elif homeState.currentMode == ModeState.ONDOL_HEAT.value: 190 | print('Central Heating Control') 191 | elif homeState.currentMode == ModeState.SIMPLE_RESERVE.value: 192 | print('Heating Inteval') 193 | elif homeState.currentMode == ModeState.CIRCLE_RESERVE.value: 194 | print('24 Hour Program') 195 | elif homeState.currentMode == ModeState.HOTWATER_ON.value: 196 | print('Hot Water Only') 197 | else: 198 | print(str(homeState.currentMode)) 199 | 200 | print('Current Room Temperature: ' + str(self.getTemperatureFromByte(homeState.currentInsideTemp)) + ' °C') 201 | print('Inside Heating Temperature: ' + str(self.getTemperatureFromByte(homeState.insideHeatTemp)) + ' °C') 202 | print('Central Heating Temperature: ' + str(self.getTemperatureFromByte(homeState.ondolHeatTemp)) + ' °C') 203 | print() 204 | print('Heating Timer Interval: Every ' + str(homeState.repeatReserveHour) + ' hour(s)') 205 | print('Heating Timer Duration: ' + str(homeState.repeatReserveMinute) + ' minute(s)') 206 | print() 207 | print('24Hour Schedule (00-08h): ' + bin(homeState.hour24ReserveTime1)) 208 | print('24Hour Schedule (09-16h): ' + bin(homeState.hour24ReserveTime2)) 209 | print('24Hour Schedule (17-24h): ' + bin(homeState.hour24ReserveTime3)) 210 | print() 211 | print('Simple Reserve Set Time: ' + str(homeState.simpleReserveSetTime)) 212 | print('Simple Reserve Set Minute: ' + str(homeState.simpleReserveSetMinute)) 213 | print() 214 | print('Operation Mode Flags: ' + bin(homeState.operateMode) + (' (Active)' if homeState.operateMode & OperateMode.ACTIVE.value else '')) 215 | print() 216 | print('Temperature Control Supported Types: ' + bin(homeState.tempControlType)) 217 | if homeState.tempControlType & TempControlType.POINTINSIDE: print(' (POINTINSIDE)') 218 | if homeState.tempControlType & TempControlType.POINTONDOL: print(' (POINTONDOL)') 219 | if homeState.tempControlType & TempControlType.POINTWATER: print(' (POINTWATER)') 220 | if homeState.tempControlType & TempControlType.WATERMODE.value > 0: print(' (WATERMODE_' + str(homeState.tempControlType & TempControlType.WATERMODE.value) + ') = ' + ['Unknown','Stepped','Temperature'][(homeState.tempControlType & TempControlType.WATERMODE.value)-1] + ' Controlled') 221 | print() 222 | 223 | print('Hot Water Temperature Supported Range: ' + str(self.getTemperatureFromByte(homeState.hotwaterMin)) + ' °C - ' + str(self.getTemperatureFromByte(homeState.hotwaterMax)) + ' °C') 224 | print('Central Heating Temperature Supported Range: ' + str(self.getTemperatureFromByte(homeState.ondolHeatMin)) + ' °C - ' + str(self.getTemperatureFromByte(homeState.ondolHeatMax)) + ' °C') 225 | print('Room Temperature Supported Range: ' + str(self.getTemperatureFromByte(homeState.insideHeatMin)) + ' °C - ' + str(self.getTemperatureFromByte(homeState.insideHeatMax)) + ' °C') 226 | print() 227 | print('Reserved 09: ' + str(homeState.reserve09)) 228 | print('Reserved 10: ' + str(homeState.reserve10)) 229 | 230 | def getTemperatureByte(self, temperature): 231 | return int(2.0 * temperature) 232 | 233 | def getTemperatureFromByte(self, temperatureByte): 234 | return float((temperatureByte >> 1) + (0.5 if temperatureByte & 1 else 0)) 235 | 236 | def setOperationMode(self, homeState, operateMode, value01, value02, value03, value04, value05): 237 | 238 | commandListSequence = 0 239 | commandListCommand = 131 240 | commandListDataLength = 21 241 | commandListCount = 0 242 | 243 | sendData = bytearray([commandListSequence, commandListCommand, commandListDataLength, commandListCount]) 244 | sendData.extend(homeState.deviceid) 245 | 246 | commandSequence = 1 247 | sendData.extend([commandSequence, operateMode.value, value01, value02, value03, value04, value05]); 248 | 249 | self.connection.sendall(sendData) 250 | 251 | # ------ Set OperationMode convenience methods --------- # 252 | 253 | def setPowerOff(self, homeState): 254 | return self.setOperationMode(homeState, OperateMode.POWER_OFF, 1, 0, 0, 0, 0) 255 | 256 | def setPowerOn(self, homeState): 257 | return self.setOperationMode(homeState, OperateMode.POWER_ON, 1, 0, 0, 0, 0) 258 | 259 | def setGoOutOff(self, homeState): 260 | return self.setOperationMode(homeState, OperateMode.GOOUT_OFF, 1, 0, 0, 0, 0) 261 | 262 | def setGoOutOn(self, homeState): 263 | return self.setOperationMode(homeState, OperateMode.GOOUT_ON, 1, 0, 0, 0, 0) 264 | 265 | def setInsideHeat(self, homeState, temperature): 266 | if (temperature < self.getTemperatureFromByte(homeState.insideHeatMin) or temperature > self.getTemperatureFromByte(homeState.insideHeatMax)): raise ValueError('Temperature specified is outside the boiler\'s supported range.') 267 | return self.setOperationMode(homeState, OperateMode.INSIDE_HEAT, 1, 0, 0, 0, self.getTemperatureByte(temperature)) 268 | 269 | def setOndolHeat(self, homeState, temperature): 270 | if (temperature < self.getTemperatureFromByte(homeState.ondolHeatMin) or temperature > self.getTemperatureFromByte(homeState.ondolHeatMax)): raise ValueError('Temperature specified is outside the boiler\'s supported range.') 271 | return self.setOperationMode(homeState, OperateMode.ONDOL_HEAT, 1, 0, 0, 0, self.getTemperatureByte(temperature)) 272 | 273 | def setRepeatReserve(self, homeState, hourInterval, durationMinutes): 274 | return self.setOperationMode(homeState, OperateMode.REPEAT_RESERVE, 1, 0, 0, hourInterval, durationMinutes) 275 | 276 | def setCircleReserve(self, homeState, schedule1, schedule2, schedule3): 277 | return self.setOperationMode(homeState, OperateMode.CIRCLE_RESERVE, 1, 0, schedule1, schedule2, schedule3) 278 | 279 | def setHotWaterOn(self, homeState): 280 | return self.setOperationMode(homeState, OperateMode.HOTWATER_ON, 1, 0, 0, 0, 0) 281 | 282 | def setHotWaterOff(self, homeState): 283 | return self.setOperationMode(homeState, OperateMode.HOTWATER_OFF, 1, 0, 0, 0, 0) 284 | 285 | def setHotWaterHeat(self, homeState, temperature): 286 | if (temperature < self.getTemperatureFromByte(homeState.hotwaterMin) or temperature > self.getTemperatureFromByte(homeState.hotwaterMax)): raise ValueError('Temperature specified is outside the boiler\'s supported range.') 287 | return self.setOperationMode(homeState, OperateMode.WATER_SET_TEMP, 1, 0, 0, 0, self.getTemperatureByte(temperature)) 288 | 289 | def setQuickHotWater(self, homeState): 290 | return self.setOperationMode(homeState, OperateMode.QUICK_HOTWATER, 1, 0, 0, 0, 0) 291 | 292 | def setHeatLevel(self, homeState, heatLevel): 293 | return self.setOperationMode(homeState, OperateMode.HEAT_LEVEL, 1, 0, 0, 0, heatLevel.value) -------------------------------------------------------------------------------- /python/shared/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Matthew1471/Navien-API/2ba61d734f413f1e96fdf8dab616775b8b92b416/python/shared/__init__.py --------------------------------------------------------------------------------