steilerDev / homebridge-openhab2-complete

A homebridge plugin for openHAB, that has the expectation to fully support all Services offered by Apple's Homekit Accessory Protocol (HAP)
GNU General Public License v3.0
52 stars 16 forks source link

Heater/Cooler Accessory with sepparate min-max temp for thresholds #109

Open kanivaloss opened 2 years ago

kanivaloss commented 2 years ago

Homekit uses heatingThresholdTempItem and coolingThresholdTempItem to set the temperature when both heatingItem and coolingItem are sellected. I wasn't able to manipulate the currentTempItem throug homekit/Siri. I also needed to be able to set heating and cooling individualy so I played aroundwith the climate.js file.

I added seperate variables ( minHeatThrTemp, minCoolThrTemp etc) and edited the addHeatingThresholdCharacteristic and addCoolingThresholdCharacteristic functions..

Not sure if this could be implimented in some future release.

I have attached the edited file below.

Thanks for all the great work you are doing!


'use strict';

const {addNumericSensorCharacteristic,
    addNumericSensorCharacteristicWithTransformation,
    addNumericSensorActorCharacteristic,
    addNumericSensorActorCharacteristicWithDistinctTransformation
} = require('./Numeric');

const CLIMATE_CONFIG = {
    waterLevelItem: "waterLevelItem",
    rotationSpeedItem: "rotationSpeedItem",
    currentHumidityItem: "currentHumidityItem",
    targetHumidityItem: "targetHumidityItem",
    currentTempItem: "currentTempItem",
    targetTempItem: "targetTempItem",
    heatingThresholdTempItem: "heatingThresholdTempItem",
    coolingThresholdTempItem: "coolingThresholdTempItem",
    dehumidifierThresholdItem: "dehumidifierThresholdItem",
    humidifierThresholdItem: "humidifierThresholdItem",
    tempUnit: "tempUnit", // 'Celsius' (default), 'Fahrenheit'
    minTemp: "minTemp",
    maxTemp: "maxTemp",
    minTempStep: "minTempStep",
    //edit
    minHeatThrTemp: "minHeatThrTemp",
    maxHeatThrTemp: "maxHeatThrTemp",
    minHeatThrTempStep: "minHeatThrTempStep",
    minCoolThrTemp: "minCoolThrTemp",
    maxCoolThrTemp: "maxCoolThrTemp",
    minCoolThrTempStep: "minCoolThrTempStep",
    //edit
    minFanSpeed: "minFanSpeed",
    maxFanSpeed: "maxFanSpeed",
    minFanStep: "minFanStep"
};

const DEFAULT_MIN_TEMP = -100;
const DEFAULT_MAX_TEMP = 200;
const DEFAULT_MIN_TEMP_STEP = 0.1;

const DEFAULT_MIN_FAN_SPEED = 0;
const DEFAULT_MAX_FAN_SPEED = 100;
const DEFAULT_MIN_FAN_STEP = 1;

function addWaterLevelCharacteristic(service, optional) {
    addNumericSensorCharacteristic.bind(this)(service, service.getCharacteristic(this.Characteristic.WaterLevel), {item: CLIMATE_CONFIG.waterLevelItem}, optional);
}

function addRotationSpeedCharacteristic(service, optional) {
    let thisMinSpeed = this._config[CLIMATE_CONFIG.minFanSpeed] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minFanSpeed]) : DEFAULT_MIN_FAN_SPEED;
    let thisMaxSpeed = this._config[CLIMATE_CONFIG.maxFanSpeed] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.maxFanSpeed]) : DEFAULT_MAX_FAN_SPEED;
    let thisMinStep = this._config[CLIMATE_CONFIG.minFanStep] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minFanStep]) : DEFAULT_MIN_FAN_STEP;

    let rotationSpeedCharacteristic = service.getCharacteristic(this.Characteristic.RotationSpeed);
    rotationSpeedCharacteristic.setProps({
        minValue: thisMinSpeed,
        maxValue: thisMaxSpeed,
        minTempStep: thisMinStep
    });

    this._log.debug(`Applied minValue ${thisMinSpeed}, maxValue ${thisMaxSpeed} and minStep ${thisMinStep} for fan speed`);

    addNumericSensorActorCharacteristic.bind(this)(service, rotationSpeedCharacteristic, {item: CLIMATE_CONFIG.rotationSpeedItem}, optional);
}

function addCurrentRelativeHumidityCharacteristic(service, optional) {
    addNumericSensorCharacteristic.bind(this)(service, service.getCharacteristic(this.Characteristic.CurrentRelativeHumidity), {item: CLIMATE_CONFIG.currentHumidityItem}, optional);
}

function addTargetRelativeHumidityCharacteristic(service, optional) {
    addNumericSensorActorCharacteristic.bind(this)(service, service.getCharacteristic(this.Characteristic.TargetRelativeHumidity), {item: CLIMATE_CONFIG.targetHumidityItem}, optional);
}

function addCurrentTemperatureCharacteristic(service, optional) {
    let thisMinTemp = this._config[CLIMATE_CONFIG.minTemp] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minTemp]) : DEFAULT_MIN_TEMP;
    let thisMaxTemp = this._config[CLIMATE_CONFIG.maxTemp] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.maxTemp]) : DEFAULT_MAX_TEMP;
    let thisMinStep = this._config[CLIMATE_CONFIG.minTempStep] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minTempStep]) : DEFAULT_MIN_TEMP_STEP;

    let transformation = this._config[CLIMATE_CONFIG.tempUnit] === "Fahrenheit" ? _convertFahrenheitToCelsius : parseFloat;

    let currentTemperatureCharacteristic = service.getCharacteristic(this.Characteristic.CurrentTemperature);
    currentTemperatureCharacteristic.setProps({
        minValue: thisMinTemp,
        maxValue: thisMaxTemp,
        minStep: thisMinStep
    });

    this._log.debug(`Applied minValue ${thisMinTemp}, maxValue ${thisMaxTemp} and minStep ${thisMinStep} for temp`);

    addNumericSensorCharacteristicWithTransformation.bind(this)(service,
        currentTemperatureCharacteristic,
        {item: CLIMATE_CONFIG.currentTempItem},
        transformation,
        optional
    );
}

function addTargetTemperatureCharacteristic(service, optional) {
    let thisMinTemp = this._config[CLIMATE_CONFIG.minTemp] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minTemp]) : DEFAULT_MIN_TEMP;
    let thisMaxTemp = this._config[CLIMATE_CONFIG.maxTemp] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.maxTemp]) : DEFAULT_MAX_TEMP;
    let thisMinStep = this._config[CLIMATE_CONFIG.minTempStep] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minTempStep]) : DEFAULT_MIN_TEMP_STEP;

    let getTransformation = this._config[CLIMATE_CONFIG.tempUnit] === "Fahrenheit" ? _convertFahrenheitToCelsius : parseFloat;
    let setTransformation = this._config[CLIMATE_CONFIG.tempUnit] === "Fahrenheit" ? _convertCelsiusToFahrenheit : parseFloat;

    let targetTemperatureCharacteristic = service.getCharacteristic(this.Characteristic.TargetTemperature);
    targetTemperatureCharacteristic.setProps({
        minValue: thisMinTemp,
        maxValue: thisMaxTemp,
        minStep: thisMinStep
    });
    addNumericSensorActorCharacteristicWithDistinctTransformation.bind(this)(service,
        targetTemperatureCharacteristic,
        {item: CLIMATE_CONFIG.targetTempItem},
        setTransformation,
        getTransformation,
        optional
    );
}

function _convertFahrenheitToCelsius(val) {
    return (((parseFloat(val)-32)*5)/9);
}

function _convertCelsiusToFahrenheit(val) {
    return (((parseFloat(val) * 9)/5) + 32);
}

//edit
function addCoolingThresholdCharacteristic(service, optional) {
    let thisMinTemp = this._config[CLIMATE_CONFIG.minCoolThrTemp] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minCoolThrTemp]) : DEFAULT_MIN_TEMP;
    let thisMaxTemp = this._config[CLIMATE_CONFIG.maxCoolThrTemp] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.maxCoolThrTemp]) : DEFAULT_MAX_TEMP;
    let thisMinStep = this._config[CLIMATE_CONFIG.minCoolThrTempStep] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minCoolThrTempStep]) : DEFAULT_MIN_TEMP_STEP;

    let getTransformation = this._config[CLIMATE_CONFIG.tempUnit] === "Fahrenheit" ? _convertFahrenheitToCelsius : parseFloat;
    let setTransformation = this._config[CLIMATE_CONFIG.tempUnit] === "Fahrenheit" ? _convertCelsiusToFahrenheit : parseFloat;

    let CoolingThresholdCharacteristic = service.getCharacteristic(this.Characteristic.CoolingThresholdTemperature);
    CoolingThresholdCharacteristic.setProps({
        minValue: thisMinTemp,
        maxValue: thisMaxTemp,
        minStep: thisMinStep
    });
        addNumericSensorActorCharacteristicWithDistinctTransformation.bind(this)(service,
        CoolingThresholdCharacteristic,
        {item: CLIMATE_CONFIG.coolingThresholdTempItem},
        setTransformation,
        getTransformation,
        optional
    );
}

function addHeatingThresholdCharacteristic(service, optional) {
    addNumericSensorActorCharacteristic.bind(this)(service, service.getCharacteristic(this.Characteristic.HeatingThresholdTemperature), {item: CLIMATE_CONFIG.heatingThresholdTempItem}, optional);
}

function addHeatingThresholdCharacteristic(service, optional) {
    let thisMinTemp = this._config[CLIMATE_CONFIG.minHeatThrTemp] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minHeatThrTemp]) : DEFAULT_MIN_TEMP;
    let thisMaxTemp = this._config[CLIMATE_CONFIG.maxHeatThrTemp] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.maxHeatThrTemp]) : DEFAULT_MAX_TEMP;
    let thisMinStep = this._config[CLIMATE_CONFIG.minHeatThrTempStep] !== undefined ? parseFloat(this._config[CLIMATE_CONFIG.minHeatThrTempStep]) : DEFAULT_MIN_TEMP_STEP;

    let getTransformation = this._config[CLIMATE_CONFIG.tempUnit] === "Fahrenheit" ? _convertFahrenheitToCelsius : parseFloat;
    let setTransformation = this._config[CLIMATE_CONFIG.tempUnit] === "Fahrenheit" ? _convertCelsiusToFahrenheit : parseFloat;

    let HeatingThresholdCharacteristic = service.getCharacteristic(this.Characteristic.HeatingThresholdTemperature);
    HeatingThresholdCharacteristic.setProps({
        minValue: thisMinTemp,
        maxValue: thisMaxTemp,
        minStep: thisMinStep
    });
        addNumericSensorActorCharacteristicWithDistinctTransformation.bind(this)(service,
        HeatingThresholdCharacteristic,
        {item: CLIMATE_CONFIG.heatingThresholdTempItem},
        setTransformation,
        getTransformation,
        optional
    );
}
//edit

function addRelativeHumidityDehumidifierThresholdCharacteristic(service, optional) {
    addNumericSensorActorCharacteristic.bind(this)(service, service.getCharacteristic(this.Characteristic.RelativeHumidityDehumidifierThreshold), {item: CLIMATE_CONFIG.dehumidifierThresholdItem}, optional);
}

function addRelativeHumidityHumidifierThresholdCharacteristic(service, optional) {
    addNumericSensorActorCharacteristic.bind(this)(service, service.getCharacteristic(this.Characteristic.RelativeHumidityHumidifierThreshold), {item: CLIMATE_CONFIG.humidifierThresholdItem}, optional);
}

function addTemperatureDisplayUnitsCharacteristic(service) {
    switch (this._config[CLIMATE_CONFIG.tempUnit]) {
        default:
        case 'Celsius':
            this._tempUnit = this.Characteristic.TemperatureDisplayUnits.CELSIUS;
            break;
        case 'Fahrenheit':
            this._tempUnit = this.Characteristic.TemperatureDisplayUnits.FAHRENHEIT;
            break;
    }

    service.getCharacteristic(this.Characteristic.TemperatureDisplayUnits)
        .setValue(this._tempUnit);
}

module.exports = {
    addWaterLevelCharacteristic,
    addRotationSpeedCharacteristic,
    addCurrentRelativeHumidityCharacteristic,
    addCurrentTemperatureCharacteristic,
    addCoolingThresholdCharacteristic,
    addHeatingThresholdCharacteristic,
    addTemperatureDisplayUnitsCharacteristic,
    addTargetRelativeHumidityCharacteristic,
    addTargetTemperatureCharacteristic,
    addRelativeHumidityDehumidifierThresholdCharacteristic,
    addRelativeHumidityHumidifierThresholdCharacteristic
};
maisun commented 2 years ago

I'm fighting with this problem for quite some time now, it only allows temp between 0 and 35 so the values must be hardcoded somewhere, recall there was also a typo for fan speed in Climate.js. Will give a try of your workaround, thanks!

steilerDev commented 2 years ago

@kanivaloss could you please open a pull request with your proposed changes? happy to review and merge upstream!

steilerDev commented 2 years ago

@maisun This is part of the default characteristics...

See here. I am not sure if we can overwrite those with arbitrary numbers or if there is another check on HK side....

maisun commented 2 years ago

@maisun This is part of the default characteristics...

See here. I am not sure if we can overwrite those with arbitrary numbers or if there is another check on HK side.... @steilerDev, I can confirm @kanivaloss's fix works for me, tested with success. Also I'd like to point a typo in line 48: rotationSpeedCharacteristic.setProps({ minValue: thisMinSpeed, maxValue: thisMaxSpeed, minTempStep: thisMinStep //Should be minStep instead });

steilerDev commented 2 years ago

@maisun @kanivaloss If this is confirmed to be working, please open a PR so I can merge it into a future release :) Thank you!