pdemarco925 / homebridge-icomfort

This is a plugin for homebridge for the Lennox iComfort Thermostat implemented in Java Script.
MIT License
5 stars 4 forks source link

multiple zones? #1

Closed stevesreed closed 6 years ago

stevesreed commented 6 years ago

I was wondering if there is a way to support multiple zones. Maybe add it 4 times each with a unique zone id/name?

So far it great for the main zone. Thanks for setting this up.

pdemarco925 commented 6 years ago

I've done a little looking. Above my code is the homebridge system which uses the HAP-NodeJS settings. The definition for thermostat doesn't allow the definition of a multi-zone thermostat (as far as I can tell). So as you said we'd need to have multiple accessory instances set up.

The code that runs below my code is the icomfort-js project. I haven't talked to the author about this, but I think the only method that would support specifying the zone is the setProgramMode (e.g. off, heat, cool, auto).

Since I don't have a multi zone system it would be difficult to update / debug. If I can find some time I might include some debug statements for you to add to the existing code in your configuration so I can see what the system reports as your configuration. I could then try to make some mods if you could make the updates on your system and try them out. Would be slow to get working... that is assuming the icomfort-js project doesn't need to be modified as well.

stevesreed commented 6 years ago

Sounds good let me know if there is anything I can help with.

pdemarco925 commented 6 years ago

OK, let's try this....

First figure out where the icomfort project is and go in the tests directory, for me it is located here: /usr/lib/node_modules/icomfort/tests, but likely is inside the homebridge-icomfort project under node_modules.

Then from your shell: export ICOMFORT_USERNAME='your icomfort username' export ICOMFORT_PASSWORD='your icomfort password' export ICOMFORT_GATEWAY_SERIAL='your icomfort serial number' export NODE_DEBUG=1

Then run npm test

NOTE: The last two tests modify settings (increase temp and set program mode). So either comment them out in the test, or once it runs check your settings and set it back to what you want.
-- comment out using // from the line that says "describe('updates thermostat settings..." to the end of the file leaving the last "});" uncommented.

The test will dump a lot of data, copy it, remove information you don't want to post (e.g. address, dealer, serial number) and post back here. Then I'll try to figure out the differences between how it reports a multi-zone system different from my single-zone system.

Thanks

stevesreed commented 6 years ago

I tried the test, but got: pi@raspberrypi:/usr/lib/node_modules/homebridge-icomfort/node_modules/icomfort/tests $ npm test

icomfort@1.2.0 test /usr/lib/node_modules/homebridge-icomfort/node_modules/icomfort mocha ./tests/

sh: 1: mocha: not found npm ERR! Test failed. See above for more details.

I'll see if I can figure out how to get mocha installed

stevesreed commented 6 years ago

Got it working. Here's the dump:

tests the iComfort client ✓ instantiates the client without the 'new' keyword { "Buildings": [ { "Addr1": “1234 main street“, "Addr2": "", "Age_of_Building": 3, "BuildingAlert": true, "BuildingID": 1XXXXX0, "BuildingReminder": true, "BuildingSize": 4, "BuildingStyle": 2, "Building_Name": "Home", "City": "San Diego", "Country": "US", "DealerAlerts_DlrWants": false, "DealerAlerts_OwnerAllow": false, "DealerID": 108192, "DealerReminder_DlrWants": false, "DealerReminder_OwnerAllow": false, "DealerTStatView": false, "DefaultBuilding": true, "Latitude": 0, "Longitude": 0, "NotificationEmail": "", "Number_of_Bedrooms": 4, "Number_of_Floors": 2, "Number_of_Occupants": 4, "St_Prov": "CA", "UserID": null, "UtilityCompany": "SDG&E", "ZIP_PC": "92130" } ], "ReturnStatus": "SUCCESS" } ✓ gets buildings info (getBuildingsInfo) (611ms) { "Cool_Set_Point_High_Limit": 99, "Cool_Set_Point_Low_Limit": 60, "Daylight_Savings_Time": 1, "Heat_Cool_Dead_Band": 3, "Heat_Set_Point_High_Limit": 90, "Heat_Set_Point_Low_Limit": 40, "Pref_Language_Nbr": 0, "Pref_Temp_Unit": "0", "ReturnStatus": "SUCCESS", "SystemID": "1XXXX6" } ✓ gets gateway info (getGatewayInfo) (393ms) { "Alerts": [], "ReturnStatus": "SUCCESS" } ✓ gets gateway alerts (getGatewaysAlerts) (248ms) { "ReturnStatus": "SUCCESS", "Systems": [ { "BuildingID": 119210, "Firmware_Ver": "02.13.0240", "Gateway_SN": "WSXXXXXXX5", "RegistrationCompleteFlag": false, "Status": "GOOD", "SystemID": 1XXXX6, "System_Name": "Thermostat" } ] } ✓ gets system info (getSystemsInfo) (375ms) { "ReturnStatus": "SUCCESS", "tStatInfo": [ { "Away_Mode": 0, "Central_Zoned_Away": 0, "ConnectionStatus": "GOOD", "Cool_Set_Point": 84, "DateTime_Mark": "/Date(1521877839913+0000)/", "Fan_Mode": 0, "GMT_To_Local": -28800, "GatewaySN": "WSXXXXX285", "Golden_Table_Updated": true, "Heat_Set_Point": 72, "Indoor_Humidity": 32, "Indoor_Temp": 72, "Operation_Mode": 3, "Pref_Temp_Units": "0", "Program_Schedule_Mode": "1", "Program_Schedule_Selection": 0, "System_Status": 0, "Zone_Enabled": 1, "Zone_Name": "Zone 1", "Zone_Number": 0, "Zones_Installed": 4 }, { "Away_Mode": 0, "Central_Zoned_Away": 0, "ConnectionStatus": "GOOD", "Cool_Set_Point": 84, "DateTime_Mark": "/Date(1521878369573+0000)/", "Fan_Mode": 0, "GMT_To_Local": -28800, "GatewaySN": "WSXXXXX285", "Golden_Table_Updated": true, "Heat_Set_Point": 71, "Indoor_Humidity": 32, "Indoor_Temp": 71, "Operation_Mode": 3, "Pref_Temp_Units": "0", "Program_Schedule_Mode": "1", "Program_Schedule_Selection": 2, "System_Status": 0, "Zone_Enabled": 1, "Zone_Name": "Zone 2", "Zone_Number": 1, "Zones_Installed": 4 }, { "Away_Mode": 0, "Central_Zoned_Away": 0, "ConnectionStatus": "GOOD", "Cool_Set_Point": 84, "DateTime_Mark": "/Date(1521877879880+0000)/", "Fan_Mode": 0, "GMT_To_Local": -28800, "GatewaySN": "WSXXXXX85", "Golden_Table_Updated": true, "Heat_Set_Point": 70, "Indoor_Humidity": 32, "Indoor_Temp": 70, "Operation_Mode": 3, "Pref_Temp_Units": "0", "Program_Schedule_Mode": "1", "Program_Schedule_Selection": 3, "System_Status": 0, "Zone_Enabled": 1, "Zone_Name": "Zone 3", "Zone_Number": 2, "Zones_Installed": 4 }, { "Away_Mode": 0, "Central_Zoned_Away": 0, "ConnectionStatus": "GOOD", "Cool_Set_Point": 84, "DateTime_Mark": "/Date(1521878399930+0000)/", "Fan_Mode": 0, "GMT_To_Local": -28800, "GatewaySN": "WSXXXXXX85", "Golden_Table_Updated": true, "Heat_Set_Point": 68, "Indoor_Humidity": 32, "Indoor_Temp": 72, "Operation_Mode": 3, "Pref_Temp_Units": "0", "Program_Schedule_Mode": "1", "Program_Schedule_Selection": 1, "System_Status": 0, "Zone_Enabled": 1, "Zone_Name": "Zone 4", "Zone_Number": 3, "Zones_Installed": 4 } ] } ✓ gets thermostat info list (getThermostatInfoList) (381ms) { "ReturnStatus": "SUCCESS", "tStatlookupInfo": [ { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "cool only", "name": "Operation_mode", "sort_order": 0, "value": 2 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "on", "name": "Fan_mode", "sort_order": 0, "value": 1 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "Keep Standard Time", "name": "Daylight_savings", "sort_order": 1, "value": 0 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "auto", "name": "Fan_mode", "sort_order": 1, "value": 0 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "Contact Me", "name": "MessageType", "sort_order": 1, "value": 0 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "heat only", "name": "Operation_mode", "sort_order": 1, "value": 1 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "idle", "name": "System_status", "sort_order": 1, "value": 0 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "", "name": "Fahrenheit", "sort_order": 1, "value": 0 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "", "name": "Celcius", "sort_order": 2, "value": 1 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "heating", "name": "System_status", "sort_order": 2, "value": 1 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "heat or cool", "name": "Operation_mode", "sort_order": 2, "value": 3 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "Need Service", "name": "MessageType", "sort_order": 2, "value": 1 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "Observe Daylight Savings Time", "name": "Daylight_savings", "sort_order": 2, "value": 1 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "circulate", "name": "Fan_mode", "sort_order": 3, "value": 2 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "Need Information on Products", "name": "MessageType", "sort_order": 3, "value": 2 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "off", "name": "Operation_mode", "sort_order": 3, "value": 0 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "cooling", "name": "System_status", "sort_order": 3, "value": 2 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "waiting", "name": "System_status", "sort_order": 4, "value": 3 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "Unit Not Working", "name": "MessageType", "sort_order": 4, "value": 3 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "emergency heat", "name": "Operation_mode", "sort_order": 4, "value": 4 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "Other", "name": "MessageType", "sort_order": 5, "value": 4 }, { "Lang_Nbr": 0, "ReturnStatus": "SUCCESS", "description": "emergency heat", "name": "System_status", "sort_order": 5, "value": 4 } ] } ✓ gets thermostat lookup info (getThermostatLookupInfo) (403ms) { "ReturnStatus": "SUCCESS", "tStatScheduleInfo": [ { "Schedule_Name": "Kitchen", "Schedule_Number": 0 }, { "Schedule_Name": " Master", "Schedule_Number": 1 }, { "Schedule_Name": "Main", "Schedule_Number": 2 }, { "Schedule_Name": " Kidsrooms", "Schedule_Number": 3 }, { "Schedule_Name": "vacation", "Schedule_Number": 4 } ] } ✓ gets thermostat schedule info (getThermostatScheduleInfo) (212ms) { "msg_code": "SUCCESS", "msg_desc": "Success" } ✓ validates user (validateUser) (423ms) updates thermostat settings (setThermostatInfo) 0 ✓ updates the temperature (705ms) 1) sets the program schedule mode

10 passing (5s) 1 failing

1) tests the iComfort client updates thermostat settings (setThermostatInfo) sets the program schedule mode: StatusCodeError: 500 - {"Message":"Invalid web service call, missing value for parameter: 'authorizationToken'.","StackTrace":" at System.Web.Script.Services.WebServiceMethodData.CallMethod(Object target, IDictionary2 parameters)\r\n at System.Web.Script.Services.RestHandler.InvokeMethod(HttpContext context, WebServiceMethodData methodData, IDictionary2 rawParams)\r\n at System.Web.Script.Services.RestHandler.ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)","ExceptionType":"System.InvalidOperationException"} at new StatusCodeError (/usr/lib/node_modules/homebridge-icomfort/node_modules/request-promise-core/lib/errors.js:32:15) at Request.plumbing.callback (/usr/lib/node_modules/homebridge-icomfort/node_modules/request-promise-core/lib/plumbing.js:104:33) at Request.RP$callback [as _callback] (/usr/lib/node_modules/homebridge-icomfort/node_modules/request-promise-core/lib/plumbing.js:46:31) at Request.self.callback (/usr/lib/node_modules/homebridge-icomfort/node_modules/request/request.js:186:22) at Request. (/usr/lib/node_modules/homebridge-icomfort/node_modules/request/request.js:1163:10) at IncomingMessage. (/usr/lib/node_modules/homebridge-icomfort/node_modules/request/request.js:1085:12) at endReadableNT (_stream_readable.js:1101:12) at process._tickCallback (internal/process/next_tick.js:152:19)

npm ERR! Test failed. See above for more details.

pdemarco925 commented 6 years ago

OK, in the index.js file in the tests folder find the two routines that start with: describe('updates thermostat settings (setThermostatInfo)', () => { it('updates the temperature', () => {

Replace the code with the following:

describe('updates thermostat settings (setThermostatInfo)', () => {
    let currentSettings;
    before(() => {
        const getThermostatInfoListParams = {GatewaySN:ENV.GATEWAY_SN, TempUnit: 0};
        return icomfort.getThermostatInfoList(getThermostatInfoListParams)
             .then(res => {
                currentSettings = res.tStatInfo.find(tStat => tStat.Zone_Number === 0);
            });
    });

    it('updates the temperature', () => {
          console.log("Current: " + JSON.stringify(currentSettings));
          currentSettings.Cool_Set_Point = currentSettings.Cool_Set_Point - 4;
          console.log("New: " + JSON.stringify(currentSettings));
          return icomfort.setThermostatInfo(currentSettings).then(logResponse);
    });

Add a comment (the //) to this line // after(() => icomfort.setThermostatInfo(currentSettings));

Then run npm test When it is done copy the output between these two lines: updates thermostat settings (setThermostatInfo) ✓ updates the temperature (332ms) If the line just before the second line is 0 (success), check to see if the thermostat updated the cool set point by -4 degrees for zone number 0 (should change from 84 to 80 based on what you provided earlier). If the result is -1 then it failed.

Change "tStat.Zone_Number === 0" to "tStat.Zone_Number === 3" rerun npm test and check to see if zone number 3 was updated.

When done you'll have to set back your cool_set_points for each zone you did this to.

If this works, I think I might be able to make it work.

stevesreed commented 6 years ago

for the first test I get:

updates thermostat settings (setThermostatInfo)

Current: {"Away_Mode":0,"Central_Zoned_Away":0,"ConnectionStatus":"GOOD","Cool_Set_Point":84,"DateTime_Mark":"/Date(1522013429063+0000)/","Fan_Mode":0,"GMT_To_Local":-28800,"GatewaySN":"WS14G04285","Golden_Table_Updated":true,"Heat_Set_Point":55,"Indoor_Humidity":32,"Indoor_Temp":72,"Operation_Mode":3,"Pref_Temp_Units":"0","Program_Schedule_Mode":"1","Program_Schedule_Selection":0,"System_Status":0,"Zone_Enabled":1,"Zone_Name":"Zone 1","Zone_Number":0,"Zones_Installed":4} New: {"Away_Mode":0,"Central_Zoned_Away":0,"ConnectionStatus":"GOOD","Cool_Set_Point":80,"DateTime_Mark":"/Date(1522013429063+0000)/","Fan_Mode":0,"GMT_To_Local":-28800,"GatewaySN":"WS14G04285","Golden_Table_Updated":true,"Heat_Set_Point":55,"Indoor_Humidity":32,"Indoor_Temp":72,"Operation_Mode":3,"Pref_Temp_Units":"0","Program_Schedule_Mode":"1","Program_Schedule_Selection":0,"System_Status":0,"Zone_Enabled":1,"Zone_Name":"Zone 1","Zone_Number":0,"Zones_Installed":4} 0

and the cool to is set to 80.

for the second test:

updates thermostat settings (setThermostatInfo)

Current: {"Away_Mode":0,"Central_Zoned_Away":0,"ConnectionStatus":"GOOD","Cool_Set_Point":84,"DateTime_Mark":"/Date(1522014651787+0000)/","Fan_Mode":0,"GMT_To_Local":-28800,"GatewaySN":"WS14G04285","Golden_Table_Updated":true,"Heat_Set_Point":55,"Indoor_Humidity":32,"Indoor_Temp":74,"Operation_Mode":3,"Pref_Temp_Units":"0","Program_Schedule_Mode":"1","Program_Schedule_Selection":1,"System_Status":0,"Zone_Enabled":1,"Zone_Name":"Zone 4","Zone_Number":3,"Zones_Installed":4} New: {"Away_Mode":0,"Central_Zoned_Away":0,"ConnectionStatus":"GOOD","Cool_Set_Point":80,"DateTime_Mark":"/Date(1522014651787+0000)/","Fan_Mode":0,"GMT_To_Local":-28800,"GatewaySN":"WS14G04285","Golden_Table_Updated":true,"Heat_Set_Point":55,"Indoor_Humidity":32,"Indoor_Temp":74,"Operation_Mode":3,"Pref_Temp_Units":"0","Program_Schedule_Mode":"1","Program_Schedule_Selection":1,"System_Status":0,"Zone_Enabled":1,"Zone_Name":"Zone 4","Zone_Number":3,"Zones_Installed":4} 0

and the 4th zone "cool to" temp changed to 80

Looks promising.

Thanks.

pdemarco925 commented 6 years ago

Download the file I attached here and replace the /usr/lib/node_modules/homebridge-icomfort/index.js with this file (will need to rename or paste contents into the existing file). See top of file for new zoneNumber configuration parameter. Just create multiple instances of the settings in your config.json file each with a unique name and zoneNumber.

If it works, great, I'll officially update the code and publish the updates. If not, let me know what kind of errors show up.

NOTE: I had to deprecate the program mode function. Lennox changed their server implementation. To switch between heating/cooling modes (OFF, COOL, HEAT, AUTO) you have to be in manual mode and not in program mode. The author of i-comfort is looking at changing the endpoints he uses in the future to maybe bring it back.

index.txt

stevesreed commented 6 years ago

It seems to be sort of working.

I have it set up for 4 zones, and I can query the temp in all 4 zones by name, The temperatures seem off by a little (0 or 1 degrees), which may just be rounding differently then the thermostat of the app.

However, I cannot seem to set the temperature, only check it.

If I say "Set the kitchen temperature to 74 degrees", siri responds with "ok I set the kitchen to 75 degrees Fahrenheit", but the setting for that zone (or any other zone) does not change.

If I say "what is the kitchen thermostat set to", siri will reply with "The kitchen is set between 72 degrees Fahrenheit and 83 degrees Fahrenheit. It's not active because the temperature is already inside the range". but the range is actually 72 to 84.

It's really close. just off by a bit.

pdemarco925 commented 6 years ago

Can you try changing the mode and temp (move it by >3 degrees) in the Home.app? Does that work for each zone? Also, try in cool only and heat only modes. If in auto mode try telling Siri to set the thermostat range between x and y degrees.

Yeah, Apple does things in Celsius, so the conversion back and forth to Fahrenheit is never exact. Usually off by 1-2 degrees. I remember when putting this together last year that Siri and the Home.app did things slightly different which drove me nuts. If the changes work... I'll probably publish it then try to do tweaks for the accuracy.

stevesreed commented 6 years ago

I tried 4 degrees above the current temp. but it did not change.

It is running a program setting, so for the kitchen it's currently 72, with a cool to temp of 84 and a heat to of 55. When I say set the temp to 78 degree, she says okay, but nothing is changed.

the log says: Mar 26 21:19:19 raspberrypi homebridge[545]: [2018-3-26 21:19:19] [Kitchen] setTargetTemperature: 3 : 77.9

pdemarco925 commented 6 years ago

Since you are in a mode with both cool and heat settings try saying. Set the kitchen temperature range 60 to 75 degrees. Or something like that. She can be pretty finicky at times.

stevesreed commented 6 years ago

That worked, but it's pretty awkward.

Maybe if the temp requested is higher than the current temp, the range could be set to the current cool to and the new request for heat to.

If the request is cooler than the current temp, it could set the cool to temp to the new request and leave the heat to alone.

Does that make sense?

pdemarco925 commented 6 years ago

Yes Siri is a bit awkward on this one.

The case you said makes sense, only if the current heating/cooling state is off. If you are currently cooling (or heating) it doesn't work right (e.g. currently cooling, current temp is 75, current target cool temp is 73, you want to change the target cool temp to 72). I could try to pull in the current heating/cooling state. I'll try looking into it a bit more, but I recall finding it difficult to cover all possible permutations of what could happen. Especially since Siri issues commands a bit differently then the Home.app when target heating/cooling state is auto.

But other than this, the rest of the functionality works across the multiple zones?

stevesreed commented 6 years ago

Yes. It seems to control the zones correctly.

Thanks.

pdemarco925 commented 6 years ago

I just published v1.0.0. I spent some time looking for a way to make it less awkward when in auto mode for setting the temperature, but quickly remembered the mess in how various different Homekit apps and Siri have no consistency in how they issues requests when in auto mode. It's actually kind of annoying. Thanks