Supereg / homebridge-http-temperature-sensor

Http temperature sensor for Homebridge: https://github.com/homebridge/homebridge
ISC License
31 stars 18 forks source link

Feature request Eve app history #6

Open wickeym opened 5 years ago

wickeym commented 5 years ago

Hi, I am testing out the plugin, and everything works. Are you planning on adding history data (the eve app displays a history graph)? I see there is a project that adds the required functionality. fakegato-history

Supereg commented 5 years ago

Sounds great, I do already know this project but didn’t really check it out. I‘m gonna have a more detailed look into it in the next few days ☺️

crazy-jackrabbit commented 5 years ago

Hi, did you had a look on the fakegato Project?

Supereg commented 5 years ago

Honestly I totally forgot about it. I currently have only limited time due to my final exams.

Supereg commented 4 years ago

So to update this issue. I honestly didn't really investigate this further. I couldn't really get started with the provided api from the fakegato project. Is this plug and play or do I need to adjust anything? A proof of concept (aka minimal example project) would be pretty useful.

cjastennett commented 3 years ago

Great module! Got it working perfectly with an Arduino Nano 33 IOT hot water temperature module. Wondered whether you had had a change to reconsider the fakegato module addition - it seems fairly straightforward for someone who is used to coding modules - here is the module and guidance for inclusion : https://github.com/simont77/fakegato-history . This addition would really make it awesome!

fade2metal commented 3 years ago

I modified the plugin to use it with fakegato.

You have to install the fakegato library first: npm i fakegato-history In my case it was: sudo npm i -g fakegato-history to install it globally.

In the homebridge config.json you have to set another value for this plugin: "reference" : 65; You'll need a reference value otherwise the history will not be shown in the eve app.

Here is the modified index.js file with fakegatohistory:

"use strict";

let Service, Characteristic, api;

const _http_base = require("homebridge-http-base");
const http = _http_base.http;
const configParser = _http_base.configParser;
const PullTimer = _http_base.PullTimer;
const notifications = _http_base.notifications;
const MQTTClient = _http_base.MQTTClient;
const Cache = _http_base.Cache;
const utils = _http_base.utils;

const packageJSON = require("./package.json");

module.exports = function (homebridge) {
    var FakeGatoHistoryService = require('fakegato-history')(homebridge);
    Service = homebridge.hap.Service;
    Characteristic = homebridge.hap.Characteristic;

    api = homebridge;

    homebridge.registerAccessory("homebridge-http-temperature-sensor", "HTTP-TEMPERATURE", HTTP_TEMPERATURE);

const TemperatureUnit = Object.freeze({
   Celsius: "celsius",
   Fahrenheit: "fahrenheit"
});

function HTTP_TEMPERATURE(log, config) {
    this.log = log;
    this.name = config.name;
    this.debug = config.debug || false;
    this.reference = config.reference || 20 ;
    this.loggingService = new FakeGatoHistoryService("thermo", this, { storage: 'fs' });

    if (config.getUrl) {
        try {
            this.getUrl = configParser.parseUrlProperty(config.getUrl);
        } catch (error) {
            this.log.warn("Error occurred while parsing 'getUrl': " + error.message);
            this.log.warn("Aborting...");
            return;
        }
    }
    else {
        this.log.warn("Property 'getUrl' is required!");
        this.log.warn("Aborting...");
        return;
    }

    this.unit = utils.enumValueOf(TemperatureUnit, config.unit, TemperatureUnit.Celsius);
    if (!this.unit) {
        this.unit = TemperatureUnit.Celsius;
        this.log.warn(`${config.unit} is an unsupported temperature unit! Using default!`);
    }

    this.statusCache = new Cache(config.statusCache, 0);
    this.statusPattern = /(-?[0-9]{1,3}(\.[0-9])?)/;
    try {
        if (config.statusPattern)
            this.statusPattern = configParser.parsePattern(config.statusPattern);
    } catch (error) {
        this.log.warn("Property 'statusPattern' was given in an unsupported type. Using default one!");
    }
    this.patternGroupToExtract = 1;
    if (config.patternGroupToExtract) {
        if (typeof config.patternGroupToExtract === "number")
            this.patternGroupToExtract = config.patternGroupToExtract;
        else
            this.log.warn("Property 'patternGroupToExtract' must be a number! Using default value!");
    }

    this.homebridgeService = new Service.TemperatureSensor(this.name);
    this.homebridgeService.getCharacteristic(Characteristic.CurrentTemperature)
        .setProps({
                    minValue: -100,
                    maxValue: 100
                })
        .on("get", this.getTemperature.bind(this));

    /** @namespace config.pullInterval */
    if (config.pullInterval) {
        this.pullTimer = new PullTimer(log, config.pullInterval, this.getTemperature.bind(this), value => {
            this.homebridgeService.setCharacteristic(Characteristic.CurrentTemperature, value);
        });
        this.pullTimer.start();
    }

    /** @namespace config.notificationPassword */
    /** @namespace config.notificationID */
    notifications.enqueueNotificationRegistrationIfDefined(api, log, config.notificationID, config.notificationPassword, this.handleNotification.bind(this));

    /** @namespace config.mqtt */
    if (config.mqtt) {
        let options;
        try {
            options = configParser.parseMQTTOptions(config.mqtt);
        } catch (error) {
            this.log.error("Error occurred while parsing MQTT property: " + error.message);
            this.log.error("MQTT will not be enabled!");
        }

        if (options) {
            try {
                this.mqttClient = new MQTTClient(this.homebridgeService, options, this.log);
                this.mqttClient.connect();
            } catch (error) {
                this.log.error("Error occurred creating MQTT client: " + error.message);
            }
        }
    }
}

HTTP_TEMPERATURE.prototype = {

    identify: function (callback) {
        this.log("Identify requested!");
        callback();
    },

    getServices: function () {
        if (!this.homebridgeService)
            return [];

        const informationService = new Service.AccessoryInformation();

        informationService
            .setCharacteristic(Characteristic.Manufacturer, "Andreas Bauer")
            .setCharacteristic(Characteristic.Model, "HTTP Temperature Sensor with FakeGatoHistory")
            .setCharacteristic(Characteristic.SerialNumber, "TS01")
            .setCharacteristic(Characteristic.FirmwareRevision, packageJSON.version);

        return [informationService, this.homebridgeService, this.loggingService];
    },

    handleNotification: function(body) {
        const characteristic = utils.getCharacteristic(this.homebridgeService, body.characteristic);
        if (!characteristic) {
            this.log("Encountered unknown characteristic when handling notification (or characteristic which wasn't added to the service): " + body.characteristic);
            return;
        }

        let value = body.value;
        if (body.characteristic === "CurrentTemperature" && this.unit === TemperatureUnit.Fahrenheit)
            value = (value - 32) / 1.8;

        if (this.debug)
            this.log("Updating '" + body.characteristic + "' to new value: " + body.value);
        characteristic.updateValue(value);
    },

    getTemperature: function (callback) {
        if (!this.statusCache.shouldQuery()) {
            const value = this.homebridgeService.getCharacteristic(Characteristic.CurrentTemperature).value;
            if (this.debug)
                this.log(`getTemperature() returning cached value ${value}${this.statusCache.isInfinite()? " (infinite cache)": ""}`);

            callback(null, value);
            return;
        }

        http.httpRequest(this.getUrl, (error, response, body) => {
            if (this.pullTimer)
                this.pullTimer.resetTimer();

            if (error) {
                this.log("getTemperature() failed: %s", error.message);
                callback(error);
            }
            else if (!http.isHttpSuccessCode(response.statusCode)) {
                this.log("getTemperature() returned http error: %s", response.statusCode);
                callback(new Error("Got http error code " + response.statusCode));
            }
            else {
                let temperature;
                try {
                    temperature = utils.extractValueFromPattern(this.statusPattern, body, this.patternGroupToExtract);
                } catch (error) {
                    this.log("getTemperature() error occurred while extracting temperature from body: " + error.message);
                    callback(new Error("pattern error"));
                    return;
                }

                if (this.unit === TemperatureUnit.Fahrenheit)
                    temperature = (temperature - 32) / 1.8;

                if (this.debug)
                    this.log("Temperature is currently at %s", temperature);
                this.loggingService.addEntry({time: Math.round(new Date().valueOf() / 1000), currentTemp: temperature, setTemp: this.reference, valvePosition: 100});

                this.statusCache.queried();
                callback(null, temperature);
            }
        });
    },

};
};