homebridge-nest-accfactory 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,43 +6,38 @@
6
6
 
7
7
  // Define our modules
8
8
  import HomeKitDevice from '../HomeKitDevice.js';
9
+ import { processCommonData, adjustTemperature, scaleValue } from '../utils.js';
9
10
 
10
- const LOWBATTERYLEVEL = 10; // Low battery level percentage
11
+ // Define constants
12
+ import { LOW_BATTERY_LEVEL, DATA_SOURCE, PROTOBUF_RESOURCES, DEVICE_TYPE } from '../consts.js';
11
13
 
12
14
  export default class NestTemperatureSensor extends HomeKitDevice {
13
15
  static TYPE = 'TemperatureSensor';
14
- static VERSION = '2025.06.11';
16
+ static VERSION = '2025.08.04'; // Code version
15
17
 
16
18
  batteryService = undefined;
17
19
  temperatureService = undefined;
18
20
 
19
- constructor(accessory, api, log, eventEmitter, deviceData) {
20
- super(accessory, api, log, eventEmitter, deviceData);
21
- }
22
-
23
21
  // Class functions
24
- setupDevice() {
25
- // Setup temperature service if not already present on the accessory
26
- this.temperatureService = this.addHKService(this.hap.Service.TemperatureSensor, '', 1);
22
+ onAdd() {
23
+ // Setup temperature service if not already present on the accessory and link it to the Eve app if configured to do so
24
+ this.temperatureService = this.addHKService(this.hap.Service.TemperatureSensor, '', 1, {});
27
25
  this.temperatureService.setPrimaryService();
28
26
 
29
27
  // Setup battery service if not already present on the accessory
30
28
  this.batteryService = this.addHKService(this.hap.Service.Battery, '', 1);
31
29
  this.batteryService.setHiddenService(true);
30
+ this.temperatureService.addLinkedService(this.batteryService);
31
+ }
32
32
 
33
- // Setup linkage to EveHome app if configured todo so
34
- if (
35
- this.deviceData?.eveHistory === true &&
36
- this.temperatureService !== undefined &&
37
- typeof this.historyService?.linkToEveHome === 'function'
38
- ) {
39
- this.historyService.linkToEveHome(this.temperatureService, {
40
- description: this.deviceData.description,
41
- });
42
- }
33
+ onRemove() {
34
+ this.accessory.removeService(this.temperatureService);
35
+ this.accessory.removeService(this.batteryService);
36
+ this.temperatureService = undefined;
37
+ this.batteryService = undefined;
43
38
  }
44
39
 
45
- updateDevice(deviceData) {
40
+ onUpdate(deviceData) {
46
41
  if (typeof deviceData !== 'object' || this.temperatureService === undefined || this.batteryService === undefined) {
47
42
  return;
48
43
  }
@@ -70,26 +65,155 @@ export default class NestTemperatureSensor extends HomeKitDevice {
70
65
  this.batteryService.updateCharacteristic(this.hap.Characteristic.BatteryLevel, deviceData.battery_level);
71
66
  this.batteryService.updateCharacteristic(
72
67
  this.hap.Characteristic.StatusLowBattery,
73
- deviceData.battery_level > LOWBATTERYLEVEL
68
+ deviceData.battery_level > LOW_BATTERY_LEVEL
74
69
  ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL
75
70
  : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW,
76
71
  );
77
72
  this.batteryService.updateCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE);
78
73
 
79
74
  // If we have the history service running and temperature has changed to previous in past 5mins
80
- if (
81
- deviceData.current_temperature !== this.deviceData.current_temperature &&
82
- this.temperatureService !== undefined &&
83
- typeof this.historyService?.addHistory === 'function'
84
- ) {
85
- this.historyService.addHistory(
86
- this.temperatureService,
87
- {
88
- time: Math.floor(Date.now() / 1000),
89
- temperature: deviceData.current_temperature,
90
- },
91
- 300,
92
- );
93
- }
75
+ this.history(
76
+ this.temperatureService,
77
+ {
78
+ temperature: deviceData.current_temperature,
79
+ },
80
+ { timegap: 300, force: true },
81
+ );
94
82
  }
95
83
  }
84
+
85
+ // Function to process our RAW Nest or Google for this device type
86
+ export function processRawData(log, rawData, config, deviceType = undefined) {
87
+ if (
88
+ rawData === null ||
89
+ typeof rawData !== 'object' ||
90
+ rawData?.constructor !== Object ||
91
+ typeof config !== 'object' ||
92
+ config?.constructor !== Object
93
+ ) {
94
+ return;
95
+ }
96
+
97
+ // Process data for any temperature sensors we have in the raw data
98
+ // We do this using any thermostat data
99
+ let devices = {};
100
+ Object.entries(rawData)
101
+ .filter(
102
+ ([key, value]) =>
103
+ key.startsWith('device.') === true ||
104
+ (key.startsWith('DEVICE_') === true && PROTOBUF_RESOURCES.THERMOSTAT.includes(value.value?.device_info?.typeName) === true),
105
+ )
106
+ .forEach(([object_key, value]) => {
107
+ try {
108
+ if (
109
+ value?.source === DATA_SOURCE.GOOGLE &&
110
+ value.value?.configuration_done?.deviceReady === true &&
111
+ Array.isArray(value.value?.remote_comfort_sensing_settings?.associatedRcsSensors) === true
112
+ ) {
113
+ value.value.remote_comfort_sensing_settings.associatedRcsSensors.forEach((sensor) => {
114
+ if (typeof rawData?.[sensor?.deviceId?.resourceId]?.value === 'object') {
115
+ let sensorData = rawData[sensor.deviceId.resourceId].value;
116
+ let tempDevice = processCommonData(
117
+ sensor.deviceId.resourceId,
118
+ {
119
+ type: DEVICE_TYPE.TEMPSENSOR,
120
+ model: 'Temperature Sensor',
121
+ softwareVersion: NestTemperatureSensor.VERSION, // We'll use our class version here now
122
+ serialNumber: sensorData.device_identity.serialNumber,
123
+ description: String(sensorData?.label?.label ?? ''),
124
+ location: String(
125
+ [
126
+ ...Object.values(
127
+ rawData?.[sensorData?.device_info?.pairerId?.resourceId]?.value?.located_annotations?.predefinedWheres || {},
128
+ ),
129
+ ...Object.values(
130
+ rawData?.[sensorData?.device_info?.pairerId?.resourceId]?.value?.located_annotations?.customWheres || {},
131
+ ),
132
+ ].find((where) => where?.whereId?.resourceId === sensorData?.device_located_settings?.whereAnnotationRid?.resourceId)
133
+ ?.label?.literal ?? '',
134
+ ),
135
+ // Guessing battery minimum voltage is 2v??
136
+ battery_level: scaleValue(Number(sensorData.battery.assessedVoltage.value), 2.0, 3.0, 0, 100),
137
+ current_temperature: adjustTemperature(sensorData.current_temperature.temperatureValue.temperature.value, 'C', 'C', true),
138
+ online:
139
+ isNaN(sensorData?.last_updated_beacon?.lastBeaconTime?.seconds) === false &&
140
+ Math.floor(Date.now() / 1000) - Number(sensorData.last_updated_beacon.lastBeaconTime.seconds) < 3600 * 4,
141
+ associated_thermostat: object_key,
142
+ active_sensor:
143
+ value.value?.remote_comfort_sensing_settings?.activeRcsSelection?.activeRcsSensor?.resourceId ===
144
+ sensor.deviceId.resourceId,
145
+ },
146
+ config,
147
+ );
148
+
149
+ if (
150
+ Object.entries(tempDevice).length !== 0 &&
151
+ typeof devices[tempDevice.serialNumber] === 'undefined' &&
152
+ (deviceType === undefined || (typeof deviceType === 'string' && deviceType !== '' && tempDevice.type === deviceType))
153
+ ) {
154
+ let deviceOptions = config?.devices?.find(
155
+ (device) => device?.serialNumber?.toUpperCase?.() === tempDevice?.serialNumber?.toUpperCase?.(),
156
+ );
157
+ // Insert any extra options we've read in from configuration file for this device
158
+ tempDevice.eveHistory = config.options.eveHistory === true || deviceOptions?.eveHistory === true;
159
+ devices[tempDevice.serialNumber] = tempDevice; // Store processed device
160
+ }
161
+ }
162
+ });
163
+ }
164
+
165
+ if (
166
+ value?.source === DATA_SOURCE.NEST &&
167
+ Array.isArray(rawData?.['rcs_settings.' + value.value?.serial_number]?.value?.associated_rcs_sensors) === true
168
+ ) {
169
+ rawData['rcs_settings.' + value.value.serial_number].value.associated_rcs_sensors.forEach((sensor) => {
170
+ if (
171
+ typeof rawData[sensor]?.value === 'object' &&
172
+ typeof rawData?.['where.' + rawData?.[sensor]?.value?.structure_id] === 'object'
173
+ ) {
174
+ let sensorData = rawData[sensor].value;
175
+ let tempDevice = processCommonData(
176
+ sensor,
177
+ {
178
+ type: DEVICE_TYPE.TEMPSENSOR,
179
+ model: 'Temperature Sensor',
180
+ softwareVersion: NestTemperatureSensor.VERSION, // We'll use our class version here now
181
+ serialNumber: sensorData.serial_number,
182
+ battery_level: scaleValue(Number(sensorData.battery_level), 0, 100, 0, 100),
183
+ current_temperature: adjustTemperature(sensorData.current_temperature, 'C', 'C', true),
184
+ online: Math.floor(Date.now() / 1000) - sensorData.last_updated_at < 3600 * 4,
185
+ associated_thermostat: object_key,
186
+ description: String(sensorData.description?.trim() ?? ''),
187
+ location: String(
188
+ rawData?.['where.' + sensorData.structure_id]?.value?.wheres?.find((where) => where?.where_id === sensorData.where_id)
189
+ ?.name ?? '',
190
+ ),
191
+ active_sensor:
192
+ rawData?.['rcs_settings.' + value.value.serial_number]?.value?.active_rcs_sensors?.includes?.(object_key) === true,
193
+ },
194
+ config,
195
+ );
196
+
197
+ if (
198
+ Object.entries(tempDevice).length !== 0 &&
199
+ typeof devices[tempDevice.serialNumber] === 'undefined' &&
200
+ (deviceType === undefined || (typeof deviceType === 'string' && deviceType !== '' && tempDevice.type === deviceType))
201
+ ) {
202
+ let deviceOptions = config?.devices?.find(
203
+ (device) => device?.serialNumber?.toUpperCase?.() === tempDevice?.serialNumber?.toUpperCase?.(),
204
+ );
205
+ // Insert any extra options we've read in from configuration file for this device
206
+ tempDevice.eveHistory = config.options.eveHistory === true || deviceOptions?.eveHistory === true;
207
+ devices[tempDevice.serialNumber] = tempDevice; // Store processed device
208
+ }
209
+ }
210
+ });
211
+ }
212
+ // eslint-disable-next-line no-unused-vars
213
+ } catch (error) {
214
+ log?.debug?.('Error processing temperature sensor data for "%s"', object_key);
215
+ }
216
+ });
217
+
218
+ return devices;
219
+ }