Skip to content

Commit 2edc8c6

Browse files
committed
Added ambient humidity and probe temperature. Added low battery (<30%) and fault (offline) states for each service.
1 parent 92100d9 commit 2edc8c6

2 files changed

Lines changed: 125 additions & 34 deletions

File tree

src/platform.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,8 @@ export class TempStickHomebridgePlatform implements DynamicPlatformPlugin {
7171
const res = await fetch(this.tempstickApiUrl + 'sensors/all', {
7272
headers: headers,
7373
});
74-
const sensors: [Sensor] = (await res.json()).data.items;
75-
// TODO undocumented `tcTemp` in readings for probe temp
76-
// TODO add humidity sensors
74+
const jsonData = (await res.json());
75+
const sensors: [Sensor] = jsonData.data.items;
7776

7877
// loop over the discovered devices and register each one if it has not already been registered
7978
sensors.forEach(sensor => {
@@ -91,9 +90,6 @@ export class TempStickHomebridgePlatform implements DynamicPlatformPlugin {
9190

9291
// if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. e.g.:
9392
existingAccessory.context.device = sensor;
94-
this.log.info(`Updating accessory ${sensor.sensor_name}. ` +
95-
`It is ${sensor.offline ? 'offline' : 'online'} with and the last reading was ${sensor.last_temp}°C ` +
96-
`with a battery level at ${sensor.battery_pct}%`);
9793
this.api.updatePlatformAccessories([existingAccessory]);
9894

9995
// create the accessory handler for the restored accessory
@@ -106,10 +102,6 @@ export class TempStickHomebridgePlatform implements DynamicPlatformPlugin {
106102
// this.log.info('Removing existing accessory from cache:', existingAccessory.displayName);
107103
} else {
108104
// the accessory does not yet exist, so we need to create it
109-
this.log.info(`Adding new accessory ${sensor.sensor_name}. ` +
110-
`It is ${sensor.offline ? 'offline' : 'online'} with and the last reading was ${sensor.last_temp}°C ` +
111-
`with a battery level at ${sensor.battery_pct}%`);
112-
113105
// create a new accessory
114106
const accessory = new this.api.platformAccessory(sensor.sensor_name, uuid);
115107

@@ -124,6 +116,13 @@ export class TempStickHomebridgePlatform implements DynamicPlatformPlugin {
124116
// link the accessory to your platform
125117
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
126118
}
119+
120+
// TODO use calibrated settings: probe_temp_offset, humidity_offset, temp_offset
121+
this.log.info(`Updated accessory ${sensor.sensor_name}. ` +
122+
`It is ${parseInt(sensor.offline) ? 'offline' : 'online'} with the last ambient temp was ${sensor.last_temp}°C ` +
123+
`and ambient humidity of ${sensor.last_humidity}% ` +
124+
`${sensor.last_tcTemp ? `and a probe temp of ${sensor.last_tcTemp}°C ` : ''}` +
125+
`with a battery level at ${sensor.battery_pct}%`);
127126
});
128127

129128
} catch (err) {
@@ -136,6 +135,7 @@ export class TempStickHomebridgePlatform implements DynamicPlatformPlugin {
136135
}
137136
}
138137

138+
// TODO use calibrated settings: probe_temp_offset, humidity_offset, temp_offset
139139
export interface Sensor {
140140
version: string;
141141
sensor_id: string;
@@ -145,8 +145,8 @@ export interface Sensor {
145145
// type: "DHT"
146146
// alert_interval: "1800"
147147
send_interval: string;
148-
last_temp: number;
149-
last_humidity: number;
148+
last_temp: number; // ambient sensor temp
149+
last_humidity: number; // ambient sensor humidity
150150
// last_voltage: 3
151151
battery_pct: number;
152152
// wifi_connect_time: 1
@@ -166,4 +166,5 @@ export interface Sensor {
166166
// connection_sensitivity: "3"
167167
// use_alert_interval: 0
168168
// use_offset: "0",
169+
last_tcTemp?: string; // undocumented probe temperature
169170
}

src/platformAccessory.ts

Lines changed: 112 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {CharacteristicValue, PlatformAccessory, Service} from 'homebridge';
22

3-
import {Sensor, TempStickHomebridgePlatform} from './platform.js';
3+
import {TempStickHomebridgePlatform} from './platform.js';
44

55
/**
66
* Platform Accessory
@@ -11,19 +11,18 @@ export class TempStickAccessory {
1111
private service: Service;
1212

1313
/**
14-
* These are just used to create a working example
14+
* TODO These are just used to create a working example
1515
* You should implement your own code to track the state of your accessory
1616
*/
17-
private thermostatStates = {
18-
CurrentTemp: 0.0,
19-
BatteryPct: 100,
17+
private sensorStates = {
18+
Active: false,
19+
LowBattery: true,
2020
};
2121

2222
constructor(
2323
private readonly platform: TempStickHomebridgePlatform,
2424
private readonly accessory: PlatformAccessory,
2525
) {
26-
2726
// set accessory information
2827
this.accessory.getService(this.platform.Service.AccessoryInformation)!
2928
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'Ideal Sciences, Inc.')
@@ -34,8 +33,31 @@ export class TempStickAccessory {
3433

3534
// get the LightBulb service if it exists, otherwise create a new LightBulb service
3635
// you can create multiple services for each accessory
37-
this.service = this.accessory.getService(this.platform.Service.TemperatureSensor)
38-
|| this.accessory.addService(this.platform.Service.TemperatureSensor);
36+
/**
37+
* Creating multiple services of the same type.
38+
*
39+
* To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
40+
* when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
41+
* this.accessory.getService('NAME') || this.accessory.addService(this.platform.Service.Lightbulb, 'NAME', 'USER_DEFINED_SUBTYPE_ID');
42+
*
43+
* The USER_DEFINED_SUBTYPE must be unique to the platform accessory (if you platform exposes multiple accessories, each accessory
44+
* can use the same subtype id.)
45+
*/
46+
47+
// Example: add two "motion sensor" services to the accessory
48+
const ambientTemperatureService = this.accessory.getService('Ambient Temperature Sensor') ||
49+
this.accessory.addService(this.platform.Service.TemperatureSensor, 'Ambient Temperature Sensor', 'AmbientTemperatureSensor');
50+
51+
// undocumented in V1 API, TODO: test if this exists without a probe
52+
const probe = !!accessory.context.device.last_tcTemp;
53+
let probeTemperatureService: Service | undefined = undefined;
54+
if (probe) {
55+
probeTemperatureService = this.accessory.getService('Probe Temperature Sensor') ||
56+
this.accessory.addService(this.platform.Service.TemperatureSensor, 'Probe Temperature Sensor', 'ProbeTemperatureSensor');
57+
}
58+
59+
this.service = this.accessory.getService(this.platform.Service.HumiditySensor)
60+
|| this.accessory.addService(this.platform.Service.HumiditySensor);
3961

4062
// set the service name, this is what is displayed as the default name on the Home app
4163
// in this example we are using the name we stored in the `accessory.context` in the `discoverDevices` method.
@@ -45,8 +67,24 @@ export class TempStickAccessory {
4567
// see https://developers.homebridge.io/#/service/Lightbulb
4668

4769
// register handlers for the Current Temperature Characteristic
48-
this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature)
49-
.onGet(this.handleCurrentTemperatureGet.bind(this)); // GET - bind to the `getOn` method below
70+
ambientTemperatureService.getCharacteristic(this.platform.Characteristic.CurrentTemperature)
71+
.onGet(this.handleAmbientCurrentTemperatureGet.bind(this));
72+
if (probeTemperatureService) {
73+
probeTemperatureService.getCharacteristic(this.platform.Characteristic.CurrentTemperature)
74+
.onGet(this.handleProbeCurrentTemperatureGet.bind(this));
75+
}
76+
this.service.getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity)
77+
.onGet(this.handleCurrentRelativeHumidityGet.bind(this));
78+
let services: Service[];
79+
if (probeTemperatureService) {
80+
services = [this.service, ambientTemperatureService, probeTemperatureService];
81+
} else {
82+
services = [this.service, ambientTemperatureService];
83+
}
84+
services.forEach(service => {
85+
service.getCharacteristic(this.platform.Characteristic.StatusFault).onGet(this.handleStatusFaultGet.bind(this));
86+
service.getCharacteristic(this.platform.Characteristic.StatusLowBattery).onGet(this.handleLowBatteryGet.bind(this));
87+
});
5088

5189
/**
5290
* Updating characteristics values asynchronously.
@@ -63,21 +101,45 @@ export class TempStickAccessory {
63101
const headers = new Headers();
64102
headers.append('X-API-KEY', this.platform.config.apiKey);
65103
headers.append('Content-Type', 'text/plain');
66-
const res = await fetch(this.platform.tempstickApiUrl + `sensor/${this.accessory.context.device.sensor_id}/readings`, {
104+
const res = await fetch(this.platform.tempstickApiUrl + `sensor/${this.accessory.context.device.sensor_id}`, {
67105
headers: headers,
68106
});
69107
const sensor = (await res.json()).data;
70108

71-
this.platform.log.debug('response code ' + res.status);
72-
if (res.status === 200) {
73-
this.service.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, sensor.last_temp);
109+
if (res.status === 200 && sensor) {
110+
ambientTemperatureService.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, sensor.last_temp);
111+
if (probeTemperatureService && sensor.last_tcTemp) {
112+
probeTemperatureService.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, sensor.last_tcTemp);
113+
}
74114
if (sensor.battery_pct < 30) {
75-
this.service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery,
76-
this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW);
115+
services.forEach(service => {
116+
service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery,
117+
this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW);
118+
});
119+
} else {
120+
services.forEach(service => {
121+
service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery,
122+
this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL);
123+
});
124+
}
125+
if (parseInt(sensor.offline)) {
126+
services.forEach(service => {
127+
service.updateCharacteristic(this.platform.Characteristic.StatusFault,
128+
this.platform.Characteristic.StatusFault.GENERAL_FAULT);
129+
});
77130
} else {
78-
this.service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery,
79-
this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL);
131+
services.forEach(service => {
132+
service.updateCharacteristic(this.platform.Characteristic.StatusFault,
133+
this.platform.Characteristic.StatusFault.NO_FAULT);
134+
});
80135
}
136+
this.service.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, sensor.last_humidity);
137+
// TODO use calibrated settings: probe_temp_offset, humidity_offset, temp_offset
138+
this.platform.log.info(`Updated accessory ${sensor.sensor_name}. ` +
139+
`It is ${parseInt(sensor.offline) ? 'offline' : 'online'} with the last ambient temp was ${sensor.last_temp}°C ` +
140+
`and ambient humidity of ${sensor.last_humidity}% ` +
141+
`${sensor.last_tcTemp ? `and a probe temp of ${sensor.last_tcTemp}°C ` : ''}` +
142+
`with a battery level at ${sensor.battery_pct}%`);
81143
}
82144

83145
} catch (err) {
@@ -87,7 +149,8 @@ export class TempStickAccessory {
87149
}
88150
}
89151
})();
90-
}, 1000);//parseInt(this.accessory.context.device.send_interval) * 1000);
152+
// seconds to milliseconds - at worst case will be delayed by how often the sensor should wake up and send readings
153+
}, parseInt(this.accessory.context.device.send_interval) * 1000);
91154
}
92155

93156
/**
@@ -103,9 +166,36 @@ export class TempStickAccessory {
103166
* @example
104167
* this.service.updateCharacteristic(this.platform.Characteristic.On, true)
105168
*/
106-
async handleCurrentTemperatureGet(): Promise<CharacteristicValue> {
107-
this.platform.log.debug('Triggered GET CurrentTemperature');
108-
169+
handleAmbientCurrentTemperatureGet(): CharacteristicValue {
109170
return this.accessory.context.device.last_temp;
110171
}
172+
173+
handleProbeCurrentTemperatureGet(): CharacteristicValue {
174+
if (this.accessory.context.device.last_tcTemp) {
175+
return this.accessory.context.device.last_tcTemp;
176+
} else {
177+
this.platform.log.error('Error getting probe temperature that no longer exists.');
178+
return -100000; // should never be called if probe is false
179+
}
180+
}
181+
182+
handleCurrentRelativeHumidityGet(): CharacteristicValue {
183+
return this.accessory.context.device.last_humidity;
184+
}
185+
186+
handleStatusFaultGet(): CharacteristicValue {
187+
if (parseInt(this.accessory.context.device.offline)) {
188+
return this.platform.Characteristic.StatusFault.GENERAL_FAULT;
189+
} else {
190+
return this.platform.Characteristic.StatusFault.NO_FAULT;
191+
}
192+
}
193+
194+
handleLowBatteryGet(): CharacteristicValue {
195+
if (this.accessory.context.device.battery_pct < 30) {
196+
return this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
197+
} else {
198+
return this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
199+
}
200+
}
111201
}

0 commit comments

Comments
 (0)