homebridge-weatherflow-tempest
Version:
Exposes WeatherFlow Tempest Station data as Temperature Sensors, Light Sensors, Humidity Sensors and Fan Sensors (for Wind Speed).
593 lines • 26.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WeatherFlowTempestPlatformAccessory = exports.InitWeatherFlowTempestPlatform = void 0;
class TemperatureSensor {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
// Get or Add Service to Accessory
this.service = this.accessory.getService(this.platform.Service.TemperatureSensor) ||
this.accessory.addService(this.platform.Service.TemperatureSensor);
// Create handlers for required characteristics
this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature)
.onGet(this.handleCurrentTemperatureGet.bind(this));
// Set Current Temperature
this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature).updateValue(this.getCurrentTemperature());
// Update value based on user defined global interval
const interval = (this.platform.config.interval || 10) * 1000;
setInterval(() => {
this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature).updateValue(this.getCurrentTemperature());
}, interval);
}
/**
* Get the current temperature from the global observation object. Convert to F if station units_temp is F.
*/
getCurrentTemperature() {
try {
const value_key = this.accessory.context.device.temperature_properties.value_key;
const temperature = parseFloat(this.platform.observation_data[value_key]);
if (temperature > 100.00) {
this.platform.log.debug(`WeatherFlow Tempest is reporting temperatures exceeding 100C: ${temperature}C`);
return 100;
}
else if (temperature < -271.00) {
this.platform.log.debug(`WeatherFlow Tempest is reporting temperatures less than -271C: ${temperature}C`);
return -271.00;
}
else {
return temperature;
}
}
catch (exception) {
this.platform.log.error(exception);
return -270;
}
}
/**
* Handle requests to get the current value of the "Current Temperature" characteristic
*/
handleCurrentTemperatureGet() {
this.platform.log.debug('Triggered GET CurrentTemperature');
const temperature = this.getCurrentTemperature();
return temperature;
}
}
class LightSensor {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
this.service = this.accessory.getService(this.platform.Service.LightSensor) ||
this.accessory.addService(this.platform.Service.LightSensor);
// Create handlers for required characteristics
this.service.getCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel)
.onGet(this.handleCurrentAmbientLightLevelGet.bind(this));
// Set initial value
this.service.getCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel).updateValue(this.getCurrentLux());
// Update value based on user defined global interval
const interval = (this.platform.config.interval || 10) * 1000;
setInterval(() => {
this.service.getCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel).updateValue(this.getCurrentLux());
}, interval);
}
getCurrentLux() {
try {
const value_key = this.accessory.context.device.light_properties.value_key;
const lux = parseFloat(this.platform.observation_data[value_key]);
if (lux < 0.0001) {
this.platform.log.debug(`WeatherFlow Tempest is reporting lux less than 0.0001: ${lux}`);
return 0.0001;
}
else if (lux > 100000) {
this.platform.log.debug(`WeatherFlow Tempest is reporting lux greater than 100000: ${lux}`);
return 100000;
}
else {
return lux;
}
}
catch (exception) {
this.platform.log.error(exception);
return 0.0001;
}
}
/**
* Handle requests to get the current value of the "Current Ambient Light Level" characteristic
*/
handleCurrentAmbientLightLevelGet() {
this.platform.log.debug('Triggered GET CurrentAmbientLightLevel');
const lux = this.getCurrentLux();
return lux;
}
}
class HumiditySensor {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
this.service = this.accessory.getService(this.platform.Service.HumiditySensor) ||
this.accessory.addService(this.platform.Service.HumiditySensor);
// Create handlers for required characteristics
this.service.getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity)
.onGet(this.handleCurrentRelativeHumidityGet.bind(this));
// Set initial value
this.service.getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity).updateValue(this.getCurrentRelativeHumidity());
// Update value based on user defined global interval
const interval = (this.platform.config.interval || 10) * 1000;
setInterval(() => {
this.service.getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity).updateValue(this.getCurrentRelativeHumidity());
}, interval);
}
getCurrentRelativeHumidity() {
try {
const value_key = this.accessory.context.device.humidity_properties.value_key;
const relative_humidity = parseInt(this.platform.observation_data[value_key]);
if (relative_humidity > 100) {
this.platform.log.debug(`WeatherFlow Tempest is reporting relative humidity exceeding 100%: ${relative_humidity}%`);
return 100;
}
else if (relative_humidity < 0) {
this.platform.log.debug(`WeatherFlow Tempest is reporting relative humidity less than 0%: ${relative_humidity}%`);
return 0;
}
else {
return relative_humidity;
}
}
catch (exception) {
this.platform.log.error(exception);
return 0;
}
}
/**
* Handle requests to get the current value of the "Current Relative Humidity" characteristic
*/
handleCurrentRelativeHumidityGet() {
this.platform.log.debug('Triggered GET CurrentRelativeHumidity');
const relative_humidity = this.getCurrentRelativeHumidity();
return relative_humidity;
}
}
class MotionSensor {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
this.service = this.accessory.getService(this.platform.Service.MotionSensor) ||
this.accessory.addService(this.platform.Service.MotionSensor);
// Create handlers for required characteristics
this.service.getCharacteristic(this.platform.Characteristic.MotionDetected)
.onGet(this.handleMotionDetectedGet.bind(this));
// Set initial value
this.service.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(this.isMotionDetected());
// Update value based on user defined global interval
const interval = (this.platform.config.interval || 10) * 1000;
setInterval(() => {
this.service.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(this.isMotionDetected());
}, interval);
}
getMotionSensorValue() {
try {
const value_key = this.accessory.context.device.motion_properties.value_key;
let speed = parseFloat(this.platform.observation_data[value_key]);
if (this.platform.config.units === 'Metric') {
speed = Math.round(speed);
}
else {
speed = Math.round(speed * 2.236936); // convert m/s to mph
}
if (speed < 0) {
this.platform.log.debug(`WeatherFlow Tempest Motion Sensor is reporting less than 0: ${speed}`);
return 0;
}
else {
return speed;
}
}
catch (exception) {
this.platform.log.error(exception);
return 0;
}
}
isMotionDetected() {
const current_value = this.getMotionSensorValue();
let trigger_value = 1;
try {
trigger_value = this.accessory.context.device.motion_properties.trigger_value;
}
catch (exception) {
this.platform.log.error(exception);
this.platform.log.warn('Defaulting to 1 as motion trigger value.');
}
return current_value >= trigger_value;
}
/**
* Handle requests to get the current value of the "Motion Detected" characteristic
*/
handleMotionDetectedGet() {
this.platform.log.debug('Triggered GET MotionDetected');
return this.isMotionDetected();
}
}
class Fan {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
this.service = this.accessory.getService(this.platform.Service.Fan) ||
this.accessory.addService(this.platform.Service.Fan);
// The air is always moving a lil' bit. If this isn't true we are all screwed.
this.service.setCharacteristic(this.platform.Characteristic.On, true);
// Create handlers for required characteristics
this.service.getCharacteristic(this.platform.Characteristic.RotationSpeed)
.onGet(this.handleCurrentRotationSpeedGet.bind(this));
// Set initial value
this.service.getCharacteristic(this.platform.Characteristic.RotationSpeed).updateValue(this.getCurrentWindSpeed());
// Update value based on user defined global interval
const interval = (this.platform.config.interval || 10) * 1000;
setInterval(() => {
this.service.getCharacteristic(this.platform.Characteristic.RotationSpeed).updateValue(this.getCurrentWindSpeed());
}, interval);
}
getCurrentWindSpeed() {
try {
const value_key = this.accessory.context.device.fan_properties.value_key;
let speed = parseFloat(this.platform.observation_data[value_key]);
if (this.platform.config.units === 'Metric') {
speed = Math.round(speed); // round as fan % is integer value
if (speed > 45) {
this.platform.log.debug(`WeatherFlow Tempest is reporting wind speed exceeding 45 m/s: ${speed} m/s`);
speed = 45;
}
else if (speed < 0) {
this.platform.log.debug(`WeatherFlow Tempest is reporting wind speed less than 0 m/s: ${speed} m/s`);
speed = 0;
}
}
else {
speed = Math.round(speed * 2.236936); // convert m/s to mph and round as fan % is integer value
if (speed > 100) {
this.platform.log.debug(`WeatherFlow Tempest is reporting wind speed exceeding 100 mph: ${speed} mph`);
speed = 100;
}
else if (speed < 0) {
this.platform.log.debug(`WeatherFlow Tempest is reporting wind speed less than 0 mph: ${speed} mph`);
speed = 0;
}
}
return speed;
}
catch (exception) {
this.platform.log.error(exception);
return 0;
}
}
/**
* Handle requests to get the current value of the "Current Rotation Speed" characteristic
*/
handleCurrentRotationSpeedGet() {
this.platform.log.debug('Triggered GET RotationSpeed');
return this.getCurrentWindSpeed();
}
}
class OccupancySensor {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
// Restore or create Occupancy Sensor
this.service = this.accessory.getService(this.platform.Service.OccupancySensor) ||
this.accessory.addService(this.platform.Service.OccupancySensor);
// Create handlers for required characteristics
this.service.getCharacteristic(this.platform.Characteristic.OccupancyDetected)
.onGet(this.handleOccupancyDetectedGet.bind(this));
// Set initial state of Occupancy Sensor and sensor value
const sensorName = this.accessory.context.device.name;
const [sensorValue, sensorUnits, sensorTrip] = this.getOccupancySensorValue();
this.service.getCharacteristic(this.platform.Characteristic.Name).updateValue(`${sensorName}: ${sensorValue} ${sensorUnits}`);
this.service.getCharacteristic(this.platform.Characteristic.OccupancyDetected).updateValue((sensorValue >= sensorTrip));
// Update occupancy sensor state and name based on user defined global interval
const interval = (this.platform.config.interval || 10) * 1000;
setInterval(() => {
const sensorName = this.accessory.context.device.name;
const [sensorValue, sensorUnits, sensorTrip] = this.getOccupancySensorValue();
this.service.getCharacteristic(this.platform.Characteristic.Name).updateValue(`${sensorName}: ${sensorValue} ${sensorUnits}`);
this.service.getCharacteristic(this.platform.Characteristic.OccupancyDetected).updateValue((sensorValue >= sensorTrip));
}, interval);
}
getOccupancySensorValue() {
try {
const value_key = this.accessory.context.device.occupancy_properties.value_key;
let trigger_value = this.accessory.context.device.occupancy_properties.trigger_value;
let value = parseFloat(this.platform.observation_data[value_key]);
let units = '';
// check that trip_level is not less than 0
if (trigger_value < 0) {
trigger_value = 0;
}
switch (value_key) {
case 'barometric_pressure':
if (this.platform.config.units === 'Metric') {
value = Math.round(value * 1000) / 1000; // 3 decimal places
units = 'mb';
}
else {
value = Math.round(value / 33.8638 * 1000) / 1000; // convert from mb to inHg
units = 'inHg';
}
break;
case 'precip': //
if (this.platform.config.units === 'Metric') {
value = Math.round(value * 100) / 100; // 2 decimal places
units = 'mm/min';
}
else {
value = Math.round(value * 2.36 * 100) / 100; // convert mm/min to in/hr
units = 'in/hr';
}
break;
case 'precip_accum_local_day': // convert mm to in
if (this.platform.config.units === 'Metric') {
value = Math.round(value * 100) / 100; // 2 decimal places
units = 'mm';
}
else {
value = Math.round(value / 25.4 * 100) / 100; // convert mm to in
units = 'in';
}
break;
case 'solar_radiation': // no conversion needed
units = 'W/m\xB2';
break;
case 'uv': // no conversion needed
value = Math.round(value * 10) / 10; // 1 decimal place
units = ' ';
break;
case 'wind_direction': // convert from degrees to cardinal (N, S, E, W)
// eslint-disable-next-line no-case-declarations
const cat = Math.round(value % 360 / 22.5);
switch (cat) {
case 0:
units = '\xB0 N';
break;
case 1:
units = '\xB0 NNE';
break;
case 2:
units = '\xB0 NE';
break;
case 3:
units = '\xB0 ENE';
break;
case 4:
units = '\xB0 E';
break;
case 5:
units = '\xB0 ESE';
break;
case 6:
units = '\xB0 SE';
break;
case 7:
units = '\xB0 SSE';
break;
case 8:
units = '\xB0 S';
break;
case 9:
units = '\xB0 SSW';
break;
case 10:
units = '\xB0 SW';
break;
case 11:
units = '\xB0 WSW';
break;
case 12:
units = '\xB0 W';
break;
case 13:
units = '\xB0 WNW';
break;
case 14:
units = '\xB0 NW';
break;
case 15:
units = '\xB0 NNW';
break;
case 16:
units = '\xB0 N';
break;
default:
units = ' Variable';
}
break;
default:
break;
}
if (value < 0) {
value = 0;
this.platform.log.debug(`WeatherFlow Tempest ${value_key} is reporting less than 0: ${value}`);
return [value, units, trigger_value];
}
else {
this.platform.log.debug(`WeatherFlow Tempest ${value_key}: ${value} ${units}, trip_level: ${trigger_value}`);
return [value, units, trigger_value];
}
}
catch (exception) {
this.platform.log.error(exception);
return [0, '', 1000];
}
}
isOccupancyDetected() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [sensorValue, sensorUnits, sensorTrip] = this.getOccupancySensorValue();
return sensorValue >= sensorTrip;
}
/**
* Handle requests to get the current value of the "Motion Detected" characteristic
*/
handleOccupancyDetectedGet() {
this.platform.log.debug('Triggered GET MotionDetected');
return this.isOccupancyDetected();
}
}
class BatterySensor {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
// Get or Add Service to Accessory
this.service = this.accessory.getService(this.platform.Service.Battery) ||
this.accessory.addService(this.platform.Service.Battery, 'Tempest Battery');
// Create handlers for required characteristics
this.service.getCharacteristic(this.platform.Characteristic.BatteryLevel)
.onGet(this.handleCurrentBatteryLevelGet.bind(this));
// Set Current Battery Level
this.service.getCharacteristic(this.platform.Characteristic.BatteryLevel).updateValue(this.getCurrentBatteryLevel());
// Update value based on user defined global interval
const interval = (this.platform.config.interval || 10) * 1000;
setInterval(() => {
this.service.getCharacteristic(this.platform.Characteristic.BatteryLevel).updateValue(this.getCurrentBatteryLevel());
}, interval);
}
/**
* Get the current battery level from the global observation object.
*/
getCurrentBatteryLevel() {
try {
const batteryLevel = this.platform.tempest_battery_level;
if (batteryLevel > 100) {
this.platform.log.debug(`WeatherFlow Tempest is reporting battery level exceeding 100%: ${batteryLevel}%`);
return 100;
}
else if (batteryLevel < 0) {
this.platform.log.debug(`WeatherFlow Tempest is reporting battery level less than 0%: ${batteryLevel}%`);
return 0;
}
else {
return batteryLevel;
}
}
catch (exception) {
this.platform.log.error(exception);
return 0;
}
}
/**
* Handle requests to get the current value of the "Current Battery Level" characteristic
*/
handleCurrentBatteryLevelGet() {
this.platform.log.debug('Triggered GET CurrentBatteryLevel');
const batteryLevel = this.getCurrentBatteryLevel();
return batteryLevel;
}
}
class ContactSensor {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
this.state = 0;
this.service = this.accessory.getService(this.platform.Service.ContactSensor) ||
this.accessory.addService(this.platform.Service.ContactSensor);
// Create handlers for required characteristics
this.service.getCharacteristic(this.platform.Characteristic.ContactSensorState)
.onGet(this.handleCurrentStateGet.bind(this));
// Set initial value
this.setCharacteristicState(0); // CONTACT_DETECTED
// Update value based on a 1 second check interval
let tick = 0;
setInterval(() => {
tick++;
if (tick === 5) { // Reset Contact sensor every 5 seconds if CONTACT_NOT_DETECTED
tick = 0;
if (this.state === 1) {
this.setCharacteristicState(0);
}
}
this.setCharacteristicState(this.getState());
}, 1000);
}
getState() {
try {
const lightning_strike_last_epoch = this.platform.observation_data.lightning_strike_last_epoch;
const lightning_strike_last_distance = this.platform.observation_data.lightning_strike_last_distance;
const trigger_distance = this.accessory.context.device.contact_properties.trigger_distance;
const current_epoch_now = Math.floor(Date.now() / 1000);
if (lightning_strike_last_epoch > 0
&& lightning_strike_last_distance > 0
&& lightning_strike_last_distance <= trigger_distance
&& (current_epoch_now - lightning_strike_last_epoch) <= 5) {
return 1; // trigger CONTACT_NOT_DETECTED.
}
return 0;
}
catch (exception) {
this.platform.log.error(exception);
return 0;
}
}
setCharacteristicState(state) {
this.state = state;
this.service.getCharacteristic(this.platform.Characteristic.ContactSensorState).updateValue(state);
}
handleCurrentStateGet() {
this.platform.log.debug('Triggered GET handleCurrentStateGet for Contact Sensor state');
return this.getState();
}
}
/**
* Initialize Tempest Platform (only need to do once)
*/
class InitWeatherFlowTempestPlatform {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
// Configure Accessory
this.accessory.getService(this.platform.Service.AccessoryInformation)
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'WeatherFlow')
.setCharacteristic(this.platform.Characteristic.Model, 'Tempest')
.setCharacteristic(this.platform.Characteristic.SerialNumber, `${this.platform.config.station_id}`);
// Add battery sensor
new BatterySensor(this.platform, this.accessory);
}
}
exports.InitWeatherFlowTempestPlatform = InitWeatherFlowTempestPlatform;
/**
* Platform Accessory
*/
class WeatherFlowTempestPlatformAccessory {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
// Configure Accessory
this.accessory.getService(this.platform.Service.AccessoryInformation)
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'WeatherFlow')
.setCharacteristic(this.platform.Characteristic.Model, `Tempest - ${this.accessory.context.device.name}`)
.setCharacteristic(this.platform.Characteristic.SerialNumber, `${this.platform.config.station_id}`);
switch (this.accessory.context.device.sensor_type) {
case 'Temperature Sensor':
new TemperatureSensor(this.platform, this.accessory);
// Add Battery to default Temperature air_temperature sensor
if (this.accessory.context.device.temperature_properties.value_key === 'air_temperature') {
new BatterySensor(this.platform, this.accessory);
}
break;
case 'Light Sensor':
new LightSensor(this.platform, this.accessory);
break;
case 'Humidity Sensor':
new HumiditySensor(this.platform, this.accessory);
break;
case 'Motion Sensor':
new MotionSensor(this.platform, this.accessory);
break;
case 'Fan':
new Fan(this.platform, this.accessory);
break;
case 'Occupancy Sensor':
new OccupancySensor(this.platform, this.accessory);
break;
case 'Contact Sensor':
new ContactSensor(this.platform, this.accessory);
break;
}
}
}
exports.WeatherFlowTempestPlatformAccessory = WeatherFlowTempestPlatformAccessory;
//# sourceMappingURL=platformAccessory.js.map