Very cool. It’s only for receiving. But it would make a good logging function.
Yep, few questions, any way to see what stage is running? Also when I see OnDuration with a request, is that how long it wants it to run in minutes?
2017-01-04 21:07:24 (00,00) ff 01 02 02 02 00 01 03 20 05 66 00 60 00 78 d5 10 Fan request 60%,
2017-01-04 21:07:24 (00,00) 01 ff 02 02 02 00 02 83 20 05 66 00 60 00 78 46 1e ACK reply
2017-01-04 21:07:25 (00,00) ff 01 02 02 02 00 01 03 20 04 64 00 60 78 87 61 Heat request 60%, OnDuration=00:07
2017-01-04 21:07:25 (00,00) 01 ff 02 02 02 00 02 83 20 04 64 00 60 78 79 ed ACK reply
2017-01-04 21:07:26 (00,00) ff 01 02 02 05 00 01 03 20 04 64 00 60 00 57 07 Heat request OFF,OAT=34F, OnDuration=00:00
I never figured out how to tell what stage is running.
The durations may be off because I reset the timer base values every time a re-sync occurred.
Yes, they are in MM:SS.
I found the heat requests to come out every 4 minutes approximately.
The duration is HH:MM
Got ya, could it be in GETSTATUS?
Just looked into this.
I don’t see a GetStatus in your logs.
I see a lot of GetConfig entries. But your system does not abide by the specification. Since the data is not changing in your log, I don’t think there is any STATUS info, just configuration info.
I think only the Thermostat calling for the heat would know the stage requested. There should be some info to glean from the commands to/from the heatpump unit, I just don’t see any.
If your friend can get a start on the binding, I can help look thru the logs to ‘infer’ more of what is going on.
It looks like it does it when it is in a heat or cool mode, see log at:
http://share.robotics.net/hvac.log
Not sure what it means, but it is changing:
nathan@maggie share $ more hvac.log |grep "GETSTATUS reply"
2017-01-11 16:42:32 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 01 59 0f 00 00 00 00 00 00 d3 03 00 00 59 0f 00 00 00 13 5b GETSTATUS reply
2017-01-11 16:42:32 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 01 59 00 00 00 00 00 00 00 d3 03 00 00 59 0f 00 00 00 13 6a GETSTATUS reply
2017-01-11 16:42:32 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 01 59 00 00 00 00 00 00 00 d3 03 00 00 59 0f 00 00 00 13 6a GETSTATUS reply
2017-01-11 17:35:29 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 17:35:59 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 17:36:29 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 17:36:56 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 17:37:26 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 17:37:57 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:38:27 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:38:55 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:39:25 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:39:56 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:40:26 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 17:40:55 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:41:25 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:41:57 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:42:27 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:42:58 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:43:26 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 17:43:56 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 36 0f 00 00 00 00 00 00 52 02 00 00 36 0f 00 00 00 63 d2 GETSTATUS reply
2017-01-11 18:42:13 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 18:42:43 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 18:43:13 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 18:43:39 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:44:10 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:44:41 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:45:09 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:45:39 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:46:09 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:46:39 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:47:10 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 0f 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e e9 GETSTATUS reply
2017-01-11 18:47:42 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:48:12 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:48:38 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:49:09 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:49:42 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:50:11 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:50:38 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:51:10 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 6d 00 00 00 00 00 00 00 af 04 00 00 6d 0f 00 00 00 7e f8 GETSTATUS reply
2017-01-11 18:51:41 (00,00) 02 ff 02 01 66 00 02 82 20 18 00 16 00 00 00 00 02 36 0f 00 00 00 00 00 00 52 02 00 00 36 0f 00 00 00 63 d2 GETSTATUS reply
As far as the binding, I have not contacted my developer yet because I was not sure it was worth me paying to write a binding if I could not get stage information since that is what I am missing today.
Dropbox link is dead- can you provide a copy of the python script?
Sure:
import socket
import sys
import datetime
import time
import pygame
from pygame.locals import *
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
w, h = 8, 16
SessionID = [[0 for x in range(w)] for y in range(h)]
MacID = [[0 for x in range(w)] for y in range(h)]
quitapp=0
outdoortemp=0
# Connect the socket to the port where the server is listening
#server_address = ('192.168.1.167', 23)
#server_address = ('50.246.121.173', 8023)
server_address = ('10.88.64.134', 23)
print 'connecting to %s port %s\r\n' % server_address,
pygame.init()
pygame.display.set_mode((100,100))
print 'Press ESC in the black open window form to quit\r\n',
state=0 # 0=sync, else decode
oldstate=state+1
try:
while quitapp==0:
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN and event.dict['key'] == 27:
quitapp=1
pygame.event.pump()
if state==0:
if oldstate!=state:
print "Opening Socket\r\n",
oldstate=state
try:
sock.connect(server_address)
state=1
except socket.error as msg:
#time.sleep(10)
oldstate=state+1 #reprint Opening info
continue
if state==1:
if oldstate!=state:
print "Sync Begin\r\n",
oldstate=state
seqcnt = 0 # 0=looking for $A0, 1=looking for $11, 2 thru 2+$11+2=waiting to finish sync
try:
# get in sync, look for $A0 then $11, then wait $11+2 bytes
data = sock.recv(1)
print data.encode('hex'),
if seqcnt>=20:
print '\r\nsync finished\r\n',
state=2
else:
if seqcnt>=2:
seqcnt+=1
else:
if seqcnt==1:
if data[0]==chr(0x11):
seqcnt+=1
else:
seqcnt=0
else: #if seqcnt==0:
if data[0]==chr(0xA0):
seqcnt+=1
else:
seqcnt=0
except socket.error as msg:
socket.close()
state=0
if state==2:
if oldstate!=state:
print "Decode Begin\r\n",
oldstate=state
seqcnt=0
payload=0
command=bytearray()
heatstart=heatend=0
coolstart=coolend=0
fanstart=fanend=0
altheatstart=altheatend=0
heaton=coolon=fanon=altheaton=0
try:
data = sock.recv(1)
#if ord(data[0])!=0xFF or payload==2 or payload==1: # or len(command)>=8:
if True:
command.append(data[0])
if seqcnt>9:
seqcnt+=1
if payload>0:
payload-=1
if payload==0:
lrc1 = 0xAA
lrc2 = 0
for b in command:
lrc1 += b
if lrc1>= 255:
lrc1 -= 255
lrc2 += lrc1
if lrc2>= 255:
lrc2 -= 255
if lrc1!=0:
state=1 # resync
print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S "),
print '(%02x,%02x) ' % (lrc1,lrc2),
print ' '.join('{:02x}'.format(x) for x in command),
command[8]=0xFF #dummy command
continue
if (command[8]&0x80)==0:
if command[7]!=0x79: #don't print the NODE queries
if len(command)>0:
print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S "),
print '(%02x,%02x) ' % (lrc1,lrc2),
print ' '.join('{:02x}'.format(x) for x in command),
#else:
# print "NODE query",
if command[7]==0xF9:
print "NODEDISCOVERY reply",
if command[7]==0x7A:
print "SETADDR request",
if command[7]==0xFA:
print "SETADDR reply",
if command[7]==0x7B:
print "GETNODEID request",
if command[7]==0xFB:
print "GETNODEID reply",
if command[7]==0x14:
print "NODELST request",
if command[7]==0x94:
print "NODELST reply",
if command[7]==0x02:
print "GETSTATUS request",
if command[7]==0x82:
print "GETSTATUS reply",
if command[7]==0x01:
print "GETCFG request",
if command[7]==0x81:
print "GETCFG reply",
if command[7]==0x05:
print "DIAG request",
if command[7]==0x85:
print "DIAG reply",
if command[7]==0x07:
print "OAT request",
if command[7]==0x87:
val=(command[13]&0x3F)*16
val+=(command[12]>>4)
frac=((command[12]&0x0f)*10)/16
outdoortemp=val
if (command[13]&0x40)>0:
outdoortemp*=-1
val*=-1
print "OAT reply %d.%dF" % (val,frac),
if command[7]==0x03:
if command[10]==0x69:
print "AltHeat request ",
val=command[13]/2
if val==0:
if altheaton==1:
altheaton=0
print("OFF,OAT={}F,".format(int(outdoortemp))),
#altheatend = time.time()
#if altheatstart==0: altheatstart=altheatend
#hours, rem = divmod(altheatend-altheatstart, 3600)
#minutes, seconds = divmod(rem, 60)
#if seconds>(60-minutes*2): minutes+=1
#print("OnDuration={:0>2}:{:0>2}".format(int(hours),int(minutes))),
else:
print "%d%%," % val,
if altheaton==0:
altheaton=1
#altheatstart=time.time()
#if altheatend==0: altheatend=altheatstart
#hours, rem = divmod(altheatstart-altheatend, 3600)
#minutes, seconds = divmod(rem, 60)
#if seconds>(60-minutes*2): minutes+=1
#print("OffDuration={:0>2}:{:0>2}".format(int(hours),int(minutes))),
if command[10]==0x66:
print "Fan request ",
val=command[14]/2
if val==0:
if fanon==1:
fanon=0
print("OFF,OAT={}F,".format(int(outdoortemp))),
#fanend = time.time()
#if fanstart==0: fanstart=fanend
#hours, rem = divmod(fanend-fanstart, 3600)
#minutes, seconds = divmod(rem, 60)
#if seconds>(60-minutes*2): minutes+=1
#print("OnDuration={:0>2}:{:0>2}".format(int(hours),int(minutes))),
else:
print "%d%%," % val,
if fanon==0:
fanon=1
#fanstart=time.time()
#if fanend==0: fanend=fanstart
#hours, rem = divmod(fanstart-fanend, 3600)
#minutes, seconds = divmod(rem, 60)
#if seconds>(60-minutes*2): minutes+=1
#print("OffDuration={:0>2}:{:0>2}".format(int(hours),int(minutes))),
if command[10]==0x65:
print "Cool request",
val=command[13]/2
if val==0:
if coolon==1:
coolon=0
print("OFF,OAT={}F,".format(int(outdoortemp))),
#coolend = time.time()
#if coolstart==0: coolstart=coolend
#hours, rem = divmod(coolend-coolstart, 3600)
#minutes, seconds = divmod(rem, 60)
#if seconds>(60-minutes*2): minutes+=1
#print("OnDuration={:0>2}:{:0>2}".format(int(hours),int(minutes))),
else:
print "%d%%," % val,
if coolon==0:
coolon=1
#coolstart=time.time()
#if coolend==0: coolend=coolstart
#hours, rem = divmod(coolstart-coolend, 3600)
#minutes, seconds = divmod(rem, 60)
#if seconds>(60-minutes*2): minutes+=1
#print("OffDuration={:0>2}:{:0>2}".format(int(hours),int(minutes))),
if command[10]==0x64:
print "Heat request",
val=command[13]/2
if val==0:
if heaton==1:
heaton=0
print("OFF,OAT={}F,".format(int(outdoortemp))),
#heatend = time.time()
#if heatstart==0: heatstart=heatend
#hours, rem = divmod(heatend-heatstart, 3600)
#minutes, seconds = divmod(rem, 60)
#if seconds>(60-minutes*2): minutes+=1
#print("OnDuration={:0>2}:{:0>2}".format(int(hours),int(minutes))),
else:
print "%d%%," % val,
if heaton==0:
heaton=1
#heatstart=time.time()
#if heatend==0: heatend=heatstart
#hours, rem = divmod(heatstart-heatend, 3600)
#minutes, seconds = divmod(rem, 60)
#if seconds>(60-minutes*2): minutes+=1
#print("OnDuration={:0>2}:{:0>2}".format(int(hours),int(minutes))),
if command[7]==0x83:
print "ACK reply",
if command[7]!=0x79: #don't print the NODE queries
print '\r\n',
else:
# test
#print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S "),
#print '(%02x,%02x) ' % (lrc1,lrc2),
#print ' '.join('{:02x}'.format(x) for x in command),
#print '\r\n',
if command[6]<10 :
device=command[6]&0x0F # get deviceID
match=1
if len(command)>=27 and command[8]==0xA0:
for x in range(0,8):
if SessionID[device][x]!=command[19+x]:
match=0
if match==0:
# test
#print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S "),
#print '(%02x,%02x) ' % (lrc1,lrc2),
#print 'Old SessionID[%d] ' % device,
#for x in range(0,8):
# print "%02X" % SessionID[device][x],
#print '\r\n',
print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S "),
print '(%02x,%02x) ' % (lrc1,lrc2),
print 'New SessionID[%d] ' % device,
for x in range(0,8):
SessionID[device][x]=command[19+x]
print "%02X" % SessionID[device][x],
print '\r\n',
match=1
if len(command)>=27 and command[8]==0xA0:
for x in range(0,8):
if MacID[device][x]!=command[11+x]:
match=0
if match==0:
print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S "),
print '(%02x,%02x) ' % (lrc1,lrc2),
print 'New MacID[%d] ' % device,
for x in range(0,8):
MacID[device][x]=command[11+x]
print "%02X" % MacID[device][x],
print '\r\n',
#cmdlen=len(command)
#for x in range(0,cmdlen):
# command.pop(0)
command=bytearray()
seqcnt=0
else:
if seqcnt==9:
payload=ord(data[0])+2
seqcnt+=1
#print data.encode('hex'),
else:
seqcnt+=1
#print data.encode('hex'),
except socket.error as msg:
socket.close()
state=0
finally:
print >>sys.stderr, 'closing socket\r\n',
sock.close()
Are you guys still working this? If so I’d like to jump in. Do you guys have anythign you can sare with me. Wondering if everything is dead or if climate talk is still moving forward. All the links I can find are dead:
http://www.climatetalkalliance.org/ClimateTalkTechnology/SpecDownload.aspx - DEAD
http://www.climatetalkalliance.org/ - Dead
You’re python script on your dropbox share - also dead.
-Loren
Nest was already pretty bad. My end game is to use the data from my remove sensors from my Ecobee (temperature and occupancy) to run my zone controller. I’m running Goodman equipment which I believe supports ClimateTalk but I’m struggling to find much documentation on the spec.
-Loren
This is the python script. I can’t find the climate talk spec, but will keep looking.
https://drive.google.com/file/d/1fRIYgLZ3L2n1TG2CI6uMPTJOi4JBw5OE/view?usp=sharing
Here is a dropbox link to all my notes/files for ClimateTalk. I also have two Goodman Systems.
Wow, great to hear from you Jeff it’s been a long time. Have you done any more on this since last year? I have 3 goodman systems that are each setup for 2 cool stage and 4 heat stages. I am looking to feed as much data as possible into openhab.
Nothing on my side. I was hoping someone would take the info and write a binding to get some more usage.
Hi,
any update to this? Very excited on the progress made.
I am as well, but I am not sure he is working on it anymore.
I have the Climatetalk protocol manuals. I can share them with anyone that is working on this.
Sadly I don’t think anyone is. : (