UNPKG

homebridge-rachio-irrigation

Version:

Rachio Irrigation System platform plugin for [Homebridge](https://github.com/nfarina/homebridge).

379 lines (377 loc) 29.6 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ export default class Rachio { platform; log; config; Service; Characteristic; constructor(platform, log = platform.log, config = platform.config) { this.platform = platform; this.log = log; this.config = config; this.Service = platform.Service; this.Characteristic = platform.Characteristic; } async updateService(irrigationSystemService, activeService, jsonBody) { const index = this.platform.valveServices.findIndex(valve => valve.subtype === activeService.subtype); if (jsonBody.resourceType == 'IRRIGATION_CONTROLLER' || jsonBody.resourceType == 'VALVE' || jsonBody.resourceType == 'PROGRAM') { //webhook v2 messages /*********************************************************** Event Type options from webhook info "SCHEDULE_STARTED_EVENT", "SCHEDULE_STOPPED_EVENT", "SCHEDULE_COMPLETED_EVENT", "DEVICE_ZONE_RUN_STARTED_EVENT", "DEVICE_ZONE_RUN_PAUSED_EVENT", "DEVICE_ZONE_RUN_STOPPED_EVENT", "DEVICE_ZONE_RUN_COMPLETED_EVENT", "CLIMATE_SKIP_NOTIFICATION_EVENT", "FREEZE_SKIP_NOTIFICATION_EVENT", "RAIN_SKIP_NOTIFICATION_EVENT", "WIND_SKIP_NOTIFICATION_EVENT", "NO_SKIP_NOTIFICATION_EVENT" "VALVE_RUN_START_EVENT", "VALVE_RUN_END_EVENT" "PROGRAM_RAIN_SKIP_CREATED_EVENT", "PROGRAM_RAIN_SKIP_CANCELED_EVENT" ************************************************************/ try { //version 2 /***************************** Possible states Active InUse HomeKit Shows False False Off True False Idle True True Running False True Stopping ******************************/ switch (jsonBody.eventType) { case 'DEVICE_ZONE_RUN_STARTED_EVENT': this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, started for a duration of ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes.`); this.log.debug(`${activeService.getCharacteristic(this.Characteristic.Name).value} started watering at ${new Date(jsonBody.payload.startTime).toLocaleTimeString()}`); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.ACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); activeService.getCharacteristic(this.Characteristic.RemainingDuration).updateValue(jsonBody.payload.durationSeconds); this.platform.endTime[index] = jsonBody.payload.endTime; break; case 'DEVICE_ZONE_RUN_STOPPED_EVENT': if (jsonBody.payload.durationSeconds < 60) { this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, stopped after ${jsonBody.payload.durationSeconds} seconds.`); } else { this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, stopped after ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes.`); } this.log.debug(`${activeService.getCharacteristic(this.Characteristic.Name).value} stopped watering at ${new Date(jsonBody.payload.endTime).toLocaleTimeString()} after ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes`); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.INACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.RemainingDuration).updateValue(jsonBody.payload.durationSeconds); break; case 'DEVICE_ZONE_RUN_PAUSED_EVENT': { clearTimeout(this.platform.localWebhook); const pauseDuration = Math.round((Date.parse(jsonBody.payload.endTime) - Date.now() - (Date.parse(this.platform.endTime[index]) - Date.now())) / 1000); const pauseDurationInMinutes = Math.round(jsonBody.payload.durationSeconds / 60); this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, paused for a duration of ${pauseDurationInMinutes} minutes.`); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.ACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.RemainingDuration).updateValue(pauseDuration); this.platform.endTime[index] = jsonBody.payload.endTime; } break; case 'DEVICE_ZONE_RUN_COMPLETED_EVENT': { if (jsonBody.payload.durationSeconds < 60) { this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, completed after ${jsonBody.payload.durationSeconds} seconds.`); } else { this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, completed after ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes.`); } this.log.debug(`${activeService.getCharacteristic(this.Characteristic.Name).value} completed watering at ${new Date(jsonBody.payload.endTime).toLocaleTimeString()} after ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes`); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.INACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); } break; case 'SCHEDULE_STARTED_EVENT': this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType} schedule started for ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes`); this.log.debug(`${activeService.getCharacteristic(this.Characteristic.Name).value} started schedule at ${new Date(jsonBody.payload.startTime).toLocaleTimeString()}`); if (this.Service.IrrigationSystem.UUID != activeService.UUID) { activeService.getCharacteristic(this.Characteristic.On).updateValue(true); } irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); break; case 'SCHEDULE_STOPPED_EVENT': this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType} schedule stopped after ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes`); this.log.debug(`${activeService.getCharacteristic(this.Characteristic.Name).value} stopped schedule at ${new Date(jsonBody.payload.endTime).toLocaleTimeString()}`); if (this.Service.IrrigationSystem.UUID != activeService.UUID) { activeService.getCharacteristic(this.Characteristic.On).updateValue(false); } irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); break; case 'SCHEDULE_COMPLETED_EVENT': this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType} schedule completed after ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes`); this.log.debug(`${activeService.getCharacteristic(this.Characteristic.Name).value} completed schedule at ${new Date(jsonBody.payload.endTime).toLocaleTimeString()}`); if (this.Service.IrrigationSystem.UUID != activeService.UUID) { activeService.getCharacteristic(this.Characteristic.On).updateValue(false); } irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); break; case 'VALVE_RUN_START_EVENT': jsonBody.payload.endTime = new Date(Date.parse(jsonBody.payload.startTime) + jsonBody.payload.durationSeconds * 1000); // need to add to json, missing jsonBody.payload.endTime this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, started for a duration of ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes.`); this.log.debug(`${activeService.getCharacteristic(this.Characteristic.Name).value} started watering at ${new Date(jsonBody.payload.startTime).toLocaleTimeString()}`); //valveSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE) activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.ACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); activeService.getCharacteristic(this.Characteristic.RemainingDuration).updateValue(jsonBody.payload.durationSeconds); this.platform.endTime[index] = jsonBody.payload.endTime; break; case 'VALVE_RUN_END_EVENT': jsonBody.payload.endTime = new Date(Date.parse(jsonBody.payload.startTime) + jsonBody.payload.durationSeconds * 1000); // need to add to json, missing jsonBody.payload.endTime if (jsonBody.payload.durationSeconds < 60) { this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, stopped after ${jsonBody.payload.durationSeconds} seconds.`); } else { this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}, stopped after ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes.`); } this.log.debug(`${activeService.getCharacteristic(this.Characteristic.Name).value} stopped watering at ${new Date(jsonBody.payload.endTime).toLocaleTimeString()} after ${Math.round(jsonBody.payload.durationSeconds / 60)} minutes`); //valveSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE) activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.INACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); break; case 'PROGRAM_RAIN_SKIP_CREATED_EVENT': this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}`); activeService.getCharacteristic(this.Characteristic.On).updateValue(true); break; case 'PROGRAM_RAIN_SKIP_CANCELED_EVENT': this.log.info(`<${jsonBody.externalId}> ${jsonBody.eventType}`); activeService.getCharacteristic(this.Characteristic.On).updateValue(false); break; } } catch (err) { this.log.error(`Error updating service ${err}`); } } else { //webhook v1 messages /********************************************** Possiible responses from webhooks Type : DEVICE_STATUS Subtype: OFFLINE ONLINE OFFLINE_NOTIFICATION COLD_REBOOT SLEEP_MODE_ON SLEEP_MODE_OFF BROWNOUT_VALVE RAIN_SENSOR_DETECTION_ON RAIN_SENSOR_DETECTION_OFF RAIN_DELAY_ON RAIN_DELAY_OFF Type : SCHEDULE_STATUS Subtype: SCHEDULE_STARTED SCHEDULE_STOPPED SCHEDULE_COMPLETED WEATHER_INTELLIGENCE_NO_SKIP WEATHER_INTELLIGENCE_SKIP WEATHER_INTELLIGENCE_CLIMATE_SKIP WEATHER_INTELLIGENCE_FREEZE Type : ZONE_STATUS Subtype: ZONE_STARTED ZONE_STOPPED ZONE_COMPLETED ZONE_PAUSED ZONE_CYCLING ZONE_CYCLING_COMPLETED Type : DEVICE_DELTA Subtype : DEVICE_DELTA Type : ZONE_DELTA Subtype : ZONE_DELTA Type : SCHEDULE_DELTA Subtype : SCHEDULE_DELTA **********************************************/ try { //version 1 switch (jsonBody.type) { case 'ZONE_STATUS': this.log.debug('Zone Status Update'); /***************************** Possible states Active InUse HomeKit Shows False False Off True False Idle True True Running False True Stopping ******************************/ switch (jsonBody.subType) { case 'ZONE_STARTED': this.log.info(`<${jsonBody.externalId}> ${jsonBody.title}, started for a duration of ${jsonBody.durationInMinutes} minutes.`); this.log.debug(jsonBody.summary); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.ACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); activeService.getCharacteristic(this.Characteristic.RemainingDuration).updateValue(jsonBody.duration); this.platform.endTime[index] = jsonBody.endTime; break; case 'ZONE_STOPPED': if (jsonBody.duration < 60) { this.log.info(`<${jsonBody.externalId}> ${jsonBody.title}, stopped after ${jsonBody.duration} seconds.`); } else { this.log.info(`<${jsonBody.externalId}> ${jsonBody.title}, stopped after ${jsonBody.durationInMinutes} minutes.`); } this.log.debug(jsonBody.summary); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.INACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.RemainingDuration).updateValue(jsonBody.duration); this.platform.endTime[index] = jsonBody.endTime; break; case 'ZONE_PAUSED': { clearTimeout(this.platform.localWebhook); const pauseDuration = Math.round((Date.parse(jsonBody.endTime) - Date.now() - (Date.parse(this.platform.endTime[index]) - Date.now())) / 1000); const pauseDurationInMinutes = Math.round(pauseDuration / 60); this.log.info(`<${jsonBody.externalId}> ${jsonBody.title}, paused for a duration of ${pauseDurationInMinutes} minutes.`); this.log.debug(jsonBody.summary); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.ACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.RemainingDuration).updateValue(pauseDuration); this.platform.endTime[index] = jsonBody.endTime; } break; case 'ZONE_CYCLING': clearTimeout(this.platform.localWebhook); this.log.info(`<${jsonBody.externalId}> ${jsonBody.title}, cycling for duration ${jsonBody.durationInMinutes} minutes.`); this.log.debug(jsonBody.summary); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.ACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.RemainingDuration).updateValue(jsonBody.duration); this.platform.endTime[index] = jsonBody.endTime; break; case 'ZONE_COMPLETED': this.log.info(`<${jsonBody.externalId}> ${jsonBody.title}, completed after ${jsonBody.durationInMinutes} minutes.`); this.log.debug(jsonBody.summary); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.INACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); break; case 'ZONE_CYCLING_COMPLETED': this.log.info(`<${jsonBody.externalId}> ${jsonBody.title}, cycling completed after ${jsonBody.durationInMinutes} minutes.`); this.log.debug(jsonBody.summary); irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); activeService.getCharacteristic(this.Characteristic.Active).updateValue(this.Characteristic.Active.INACTIVE); activeService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); break; } break; case 'DEVICE_STATUS': { //check if active serivce is ok this.log.debug('Device status update'); const index = this.platform.accessories.findIndex(accessory => accessory.UUID === jsonBody.deviceId); const irrigationAccessory = this.platform.accessories[index]; const switchService = irrigationAccessory.getServiceById(this.Service.Switch, this.platform.genUUID(jsonBody.deviceName + ' Standby')); switch (jsonBody.subType) { case 'ONLINE': this.log.info(`<${jsonBody.externalId}> device ${jsonBody.deviceId} connected at ${new Date(jsonBody.timestamp).toString()}`); this.log.debug(`Device ${jsonBody.subType.toLowerCase()}`); irrigationAccessory.services.forEach((service) => { if (this.Service.AccessoryInformation.UUID != service.UUID) { service.getCharacteristic(this.Characteristic.StatusFault).updateValue(this.Characteristic.StatusFault.NO_FAULT); } }); break; case 'OFFLINE': this.log.info(`<${jsonBody.externalId}> device ${jsonBody.deviceId} disconnected at ${jsonBody.timestamp}`); this.log.warn(`${jsonBody.deviceId} device disconnected at ${new Date(jsonBody.timestamp).toString()}! This will show as non-responding in Homekit until the connection is restored.`); this.log.debug(`Device ${jsonBody.subType.toLowerCase()}`); irrigationAccessory.services.forEach((service) => { if (this.Service.AccessoryInformation.UUID != service.UUID) { service.getCharacteristic(this.Characteristic.StatusFault).updateValue(this.Characteristic.StatusFault.GENERAL_FAULT); } }); break; case 'COLD_REBOOT': this.log.info(`<${jsonBody.externalId}> device,${jsonBody.deviceName} connected at ${new Date(jsonBody.timestamp).toString()} from a ${jsonBody.title}`); this.log.debug(jsonBody.summary); irrigationAccessory.services.forEach((service) => { if (this.Service.AccessoryInformation.UUID != service.UUID) { service.getCharacteristic(this.Characteristic.StatusFault).updateValue(this.Characteristic.StatusFault.NO_FAULT); } }); break; case 'SLEEP_MODE_ON': //ProgramMode 0 this.log.info(`<${jsonBody.externalId}> ${jsonBody.title} ${jsonBody.deviceName} ${jsonBody.summary}`); irrigationSystemService.getCharacteristic(this.Characteristic.ProgramMode).updateValue(this.Characteristic.ProgramMode.NO_PROGRAM_SCHEDULED); if (this.platform.showStandby) { switchService.getCharacteristic(this.Characteristic.On).updateValue(true); } break; case 'SLEEP_MODE_OFF': //ProgramMode 2 this.log.info(`<${jsonBody.externalId}> ${jsonBody.title} ${jsonBody.deviceName} ${jsonBody.summary}`); irrigationSystemService.getCharacteristic(this.Characteristic.ProgramMode).updateValue(this.Characteristic.ProgramMode.PROGRAM_SCHEDULED_MANUAL_MODE); if (this.platform.showStandby) { switchService.getCharacteristic(this.Characteristic.On).updateValue(false); } break; default: //ProgramMode 1 this.log.info(`<${jsonBody.externalId}> ${jsonBody.deviceId} ??? mode`); irrigationSystemService.getCharacteristic(this.Characteristic.ProgramMode).updateValue(this.Characteristic.ProgramMode.PROGRAM_SCHEDULED); if (this.platform.showStandby) { switchService.getCharacteristic(this.Characteristic.On).updateValue(false); } break; } } break; case 'SCHEDULE_STATUS': this.log.debug('Schedule status update'); switch (jsonBody.subType) { case 'SCHEDULE_STARTED': this.log.info(`<${jsonBody.externalId}> ${jsonBody.title} ${jsonBody.summary}`); if (this.Service.IrrigationSystem.UUID != activeService.UUID) { activeService.getCharacteristic(this.Characteristic.On).updateValue(true); } irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.IN_USE); break; case 'SCHEDULE_STOPPED': this.log.info(`<${jsonBody.externalId}> ${jsonBody.title} ${jsonBody.summary}`); if (this.Service.IrrigationSystem.UUID != activeService.UUID) { activeService.getCharacteristic(this.Characteristic.On).updateValue(false); } irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); break; case 'SCHEDULE_COMPLETED': this.log.info(`<${jsonBody.externalId}> ${jsonBody.title} ${jsonBody.summary}`); if (this.Service.IrrigationSystem.UUID != activeService.UUID) { activeService.getCharacteristic(this.Characteristic.On).updateValue(false); } else if (this.config.showRunall) { const index = this.platform.accessories.findIndex(accessory => accessory.UUID === jsonBody.deviceId); const irrigationAccessory = this.platform.accessories[index]; const switchService = irrigationAccessory.getServiceById(this.Service.Switch, this.platform.genUUID(jsonBody.deviceName + ' Quick Run-All')); switchService.getCharacteristic(this.Characteristic.On).updateValue(false); } irrigationSystemService.getCharacteristic(this.Characteristic.InUse).updateValue(this.Characteristic.InUse.NOT_IN_USE); break; } break; } return; } catch (err) { this.log.error(`Error updating service ${err}`); } } } } //# sourceMappingURL=rachioupdate.js.map