Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supported "Heat To" / "Heating To" and several other bug fixes #26

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@
"description": "Minimum temperature to be used by HomeKit slider. Use same units as configured above.",
"type": "number",
"required": true,
"default": "120"
"default": "98"
},
"maximumTemperature": {
"title": "Maximum Temperature",
"description": "Maximum temperature to be used by HomeKit slider. Use same units as configured above.",
"type": "number",
"required": true,
"default": "140"
"default": "120"
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"displayName": "Homebridge Rinnai Control-R",
"name": "homebridge-rinnai-controlr",
"version": "1.0.24",
"version": "1.0.27",
"description": "Integrates with Rinnai Control-R for HomeKit control of water heaters",
"license": "Apache-2.0",
"repository": {
Expand Down
25 changes: 21 additions & 4 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,16 @@ export const API_KEY_SET_PRIORITY_STATUS = 'set_priority_status';
export const API_KEY_RECIRCULATION_DURATION = 'recirculation_duration';
export const API_KEY_SET_RECIRCULATION_ENABLED = 'set_recirculation_enabled';
export const API_KEY_SET_TEMPERATURE = 'set_domestic_temperature';
export const API_KEY_DO_MAINTENANCE_RETRIEVAL = 'do_maintenance_retrieval';
export const API_VALUE_TRUE = 'true';
export const API_VALUE_FALSE = 'false';

export const API_POLL_THROTTLE_MILLIS = 1000;
export const API_POLL_THROTTLE_MILLIS = 30000;
export const SET_STATE_WAIT_TIME_MILLIS = 5000;

export const MAINTENANCE_RETRIEVAL_RUNNING_THROTTLE_MILLIS = 30000; // 30 secs when heater is running
export const MAINTENANCE_RETRIEVAL_IDLING_THROTTLE_MILLIS = 600000; // 10 mins when heater is idling

export const MANUFACTURER = 'Rinnai';
export const UNKNOWN = 'Unknown';

Expand All @@ -273,8 +277,21 @@ export enum TemperatureUnits {
F = 'F',
}

export const THERMOSTAT_STEP_VALUE = 0.5;
// All numbers below use metric units (C), as required by HomeKit APIs
export const THERMOSTAT_TARGET_TEMP_STEP_VALUE = 1;

export const THERMOSTAT_CURRENT_TEMP_STEP_VALUE = 0.5;
export const THERMOSTAT_CURRENT_TEMP_MAX_VALUE = 65; // 60C for residential water heater based on the manual, adding 5 as buffer
export const THERMOSTAT_CURRENT_TEMP_MIN_VALUE = 0; // 0C, frozen pipe...

/**
* For water heater controllers with the imperial unit,
* the steps are 98/100/102/.../108/110/105/110/115/120/...
*/
export const WATER_HEATER_SMALL_STEP_VALUE_IN_F = 2;
export const WATER_HEATER_BIG_STEP_START_IN_F = 110;
export const WATER_HEATER_BIG_STEP_VALUE_IN_F = 5;

// Increment only for breaking service changes to remove and re-add devices
export const PREVIOUS_UUID_SUFFICES = [''];
export const UUID_SUFFIX = '-1';
export const PREVIOUS_UUID_SUFFICES = ['-1'];
export const UUID_SUFFIX = '-2';
45 changes: 30 additions & 15 deletions src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export class RinnaiControlrHomebridgePlatform implements DynamicPlatformPlugin {

// this is used to track restored cached accessories
public readonly accessories: PlatformAccessory[] = [];
private initializedAccessories: string[] = [];

constructor(
public readonly log: Logger,
Expand Down Expand Up @@ -78,7 +79,7 @@ export class RinnaiControlrHomebridgePlatform implements DynamicPlatformPlugin {
configureAccessory(accessory: PlatformAccessory) {
this.log.debug('Loading accessory from cache:', accessory.displayName);
if (this.removeBrokenAccessories(accessory.context)) {
this.log.warn('');
this.log.warn('Removed accessory:', accessory.displayName);
} else {
this.accessories.push(accessory);
}
Expand Down Expand Up @@ -109,14 +110,6 @@ export class RinnaiControlrHomebridgePlatform implements DynamicPlatformPlugin {
}
}

throttledPoll() {
const throttle = _.throttle(() => {
this.pollDeviceStatus();
}, API_POLL_THROTTLE_MILLIS);

throttle();
}

getConfig(): RinnaiControlrConfig {
return this.config as RinnaiControlrConfig;
}
Expand Down Expand Up @@ -169,10 +162,12 @@ export class RinnaiControlrHomebridgePlatform implements DynamicPlatformPlugin {
this.log.debug(`Found ${devices.length} Rinnai devices.`);
// loop over the discovered devices and register each one if it has not already been registered
for (const device of devices) {
this.log.debug(`Processing device: ${JSON.stringify(device, null, 2)}`);

this.removeBrokenAccessories(device);

this.log.debug(`Generating UUID from DSN ${device.dsn}`);
const uuid = this.api.hap.uuid.generate(`${device.dsn}${UUID_SUFFIX}`);
this.log.debug(`Generating UUID from S/N ${device.id}`);
const uuid = this.generateAccessoryUuid(device, UUID_SUFFIX);

// see if an accessory with the same uuid has already been registered and restored from
// the cached devices we stored in the `configureAccessory` method above
Expand All @@ -186,9 +181,12 @@ export class RinnaiControlrHomebridgePlatform implements DynamicPlatformPlugin {
accessory.context = device;
this.api.updatePlatformAccessories([accessory]);

// create the accessory handler for the restored accessory
// this is imported from `platformAccessory.ts`
new RinnaiControlrPlatformAccessory(this, accessory);
if (!this.initializedAccessories.find(accessoryUuid => accessoryUuid === uuid)) {
// create the accessory handler for the restored accessory
// this is imported from `platformAccessory.ts`
new RinnaiControlrPlatformAccessory(this, accessory);
this.initializedAccessories.push(accessory.UUID);
}
} else {
// the accessory does not yet exist, so we need to create it
this.log.debug(`Adding new accessory because ${accessory} was not restored from cache:`, device.device_name);
Expand All @@ -208,6 +206,7 @@ export class RinnaiControlrHomebridgePlatform implements DynamicPlatformPlugin {

// link the accessory to your platform
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
this.initializedAccessories.push(accessory.UUID);
}
}

Expand All @@ -221,10 +220,26 @@ export class RinnaiControlrHomebridgePlatform implements DynamicPlatformPlugin {
});
}

public throttledPoll = _.throttle(async () => {
await this.pollDeviceStatus();
}, API_POLL_THROTTLE_MILLIS);

generateAccessoryUuid(device, uuidSuffix: string): string {
switch (uuidSuffix) {
case '-1':
return this.api.hap.uuid.generate(`${device.dsn}${uuidSuffix}`);
case '-2':
return this.api.hap.uuid.generate(`${device.id}${uuidSuffix}`);
}

throw new Error('Unknown suffix');
return 'bad-uuid';
}

removeBrokenAccessories(device): boolean {
let removed = false;
PREVIOUS_UUID_SUFFICES.forEach(uuidSuffix => {
const oldUuid = this.api.hap.uuid.generate(`${device.dsn}${uuidSuffix}`);
const oldUuid = this.generateAccessoryUuid(device, uuidSuffix);
const oldAccessory = this.accessories.find(accessory => accessory.UUID === oldUuid);
if (oldAccessory) {
this.log.info(`Removing existing accessory from cache because of breaking change: ${oldAccessory.displayName}`);
Expand Down
Loading