Skip to content

Commit

Permalink
Feature: thermostat heating mode (#12)
Browse files Browse the repository at this point in the history
* Implement picker UI for heating mode

* Remove debug statement

* Use correct response value for frost protection mode
  • Loading branch information
ChrisTerBeke authored Jul 15, 2024
1 parent fefc01d commit 0fd7c10
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 13 deletions.
29 changes: 26 additions & 3 deletions .homeycompose/capabilities/mode.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
{
"type": "string",
"type": "enum",
"title": {
"en": "Mode",
"nl": "Modus"
},
"uiComponent": "sensor",
"uiComponent": "picker",
"getable": true,
"setable": false
"setable": true,
"values": [
{
"id": "off",
"title": {
"en": "Off (anti-frost)",
"nl": "Uit (anti-vorst)"
}
},
{
"id": "auto",
"title": {
"en": "Auto (schedule)",
"nl": "Automatisch (schema)"
}
},
{
"id": "manual",
"title": {
"en": "Manual",
"nl": "Handmatig"
}
}
]
}
29 changes: 26 additions & 3 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,37 @@
"insights": true
},
"mode": {
"type": "string",
"type": "enum",
"title": {
"en": "Mode",
"nl": "Modus"
},
"uiComponent": "sensor",
"uiComponent": "picker",
"getable": true,
"setable": false
"setable": true,
"values": [
{
"id": "off",
"title": {
"en": "Off (anti-frost)",
"nl": "Uit (anti-vorst)"
}
},
{
"id": "auto",
"title": {
"en": "Auto (schedule)",
"nl": "Automatisch (schema)"
}
},
{
"id": "manual",
"title": {
"en": "Manual",
"nl": "Handmatig"
}
}
]
},
"target_temperature_water": {
"type": "number",
Expand Down
17 changes: 13 additions & 4 deletions drivers/remeha/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class RemehaThermostatDevice extends Device {
await this.addCapability('measure_pressure')
await this.addCapability('alarm_water')
await this.addCapability('mode')
this.registerCapabilityListener('mode', this._setMode.bind(this))

// optional capabilities
await this._addOrRemoveCapability('measure_temperature_water', capabilities.hotWaterZone)
Expand Down Expand Up @@ -123,6 +124,18 @@ class RemehaThermostatDevice extends Device {
}
}

private async _setMode(value: string): Promise<void> {
await this._refreshAccessToken()
if (!this._client) return this.setUnavailable('No Remeha Home client')
const { id } = this.getData()

try {
await this._client.setMode(id, value)
} catch (error) {
this.setUnavailable('Could not set operating mode')
}
}

private async _setFireplaceMode(value: boolean): Promise<void> {
await this._refreshAccessToken()
if (!this._client) return this.setUnavailable('No Remeha Home client')
Expand All @@ -135,10 +148,6 @@ class RemehaThermostatDevice extends Device {
}
}

private async _setTargetWaterTemperature(value: number): Promise<void> {
// TODO
}

private async _refreshAccessToken(): Promise<void> {
const authorizer = new RemehaAuth()
const { accessToken, refreshToken } = this.getStore()
Expand Down
40 changes: 37 additions & 3 deletions lib/RemehaMobileApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type DeviceCapabilities = {
fireplaceMode: boolean
outdoorTemperature: boolean
hotWaterZone: boolean
multiSchedule: boolean
}

export type DeviceData = {
Expand All @@ -18,6 +19,7 @@ export type DeviceData = {
waterTemperature?: number
targetWaterTemperature?: number
fireplaceMode?: boolean
heatingProgramId?: number
}

type ResponseClimateZone = {
Expand All @@ -28,6 +30,7 @@ type ResponseClimateZone = {
zoneMode: string
capabilityFirePlaceMode: boolean
firePlaceModeActive?: boolean
activeHeatingClimateTimeProgramNumber?: number
}

type ResponseHotWaterZone = {
Expand All @@ -44,6 +47,7 @@ type ResponseAppliance = {
waterPressureOK: boolean
capabilityOutdoorTemperature: boolean
outdoorTemperature?: number
capabilityMultiSchedule: boolean
}

type DashboardResponse = {
Expand Down Expand Up @@ -89,19 +93,38 @@ export class RemehaMobileApi {
fireplaceMode: appliance.climateZones[0].capabilityFirePlaceMode,
outdoorTemperature: appliance.capabilityOutdoorTemperature,
hotWaterZone: appliance.hotWaterZones.length > 0,
multiSchedule: appliance.capabilityMultiSchedule,
}
}

public async setTargetTemperature(climateZoneID: string, roomTemperatureSetPoint: number): Promise<void> {
const device = await this.device(climateZoneID)
if (!device) return
if (device.mode == "Manual") {

if (device.mode == 'manual') {
await this._call(`/climate-zones/${climateZoneID}/modes/manual`, 'POST', { roomTemperatureSetPoint })
} else {
await this._call(`/climate-zones/${climateZoneID}/modes/temporary-override`, 'POST', { roomTemperatureSetPoint })
}
}

public async setMode(climateZoneID: string, mode: string): Promise<void> {
const capabilities = await this.capabilities(climateZoneID)
if (!capabilities) return
const device = await this.device(climateZoneID)
if (!device) return

if (mode == 'manual') {
const roomTemperatureSetPoint = device.targetTemperature
await this._call(`/climate-zones/${climateZoneID}/modes/manual`, 'POST', { roomTemperatureSetPoint })
} else if (mode == 'auto') {
const heatingProgramId = capabilities.multiSchedule && device.heatingProgramId ? device.heatingProgramId : 1
await this._call(`/climate-zones/${climateZoneID}/modes/schedule`, 'POST', { heatingProgramId })
} else if (mode == 'off') {
await this._call(`/climate-zones/${climateZoneID}/modes/anti-frost`, 'POST')
}
}

public async setFireplaceMode(climateZoneID: string, fireplaceModeActive: boolean): Promise<void> {
await this._call(`/climate-zones/${climateZoneID}/modes/fireplacemode`, 'POST', { fireplaceModeActive })
}
Expand Down Expand Up @@ -139,11 +162,12 @@ export class RemehaMobileApi {
const deviceData: DeviceData = {
id: appliance.climateZones[0].climateZoneId,
name: appliance.climateZones[0].name,
mode: appliance.climateZones[0].zoneMode,
mode: RemehaMobileApi._mapResponseModeToHomeyMode(appliance.climateZones[0].zoneMode),
temperature: appliance.climateZones[0].roomTemperature,
targetTemperature: appliance.climateZones[0].setPoint,
waterPressure: appliance.waterPressure,
waterPressureOK: appliance.waterPressureOK,
heatingProgramId: appliance.climateZones[0].activeHeatingClimateTimeProgramNumber,
}

// not every installation supports outdoor temperature
Expand All @@ -156,12 +180,22 @@ export class RemehaMobileApi {
deviceData.fireplaceMode = appliance.climateZones[0].firePlaceModeActive
}

// not every installation has a hot water zone, for example in Hybrid heat pumps
// not every installation has a hot water zone, for example in hybrid heat pumps
if (appliance.hotWaterZones.length > 0) {
deviceData.waterTemperature = appliance.hotWaterZones[0].dhwTemperature
deviceData.targetWaterTemperature = appliance.hotWaterZones[0].targetSetpoint
}

return deviceData
}

private static _mapResponseModeToHomeyMode(mode: string): string {
switch (mode) {
case 'Manual': return 'manual'
case 'TemporaryOverride': return 'manual'
case 'Scheduling': return 'auto'
case 'FrostProtection': return 'off'
default: return 'off'
}
}
}

0 comments on commit 0fd7c10

Please sign in to comment.