Skip to content

Commit

Permalink
Refactor: do API calls from driver (#14)
Browse files Browse the repository at this point in the history
* Refactor entire app to reduce number of API calls

* Validate

* Remove settings.json

* Add translations for app settings

* Many fixes after testing
  • Loading branch information
ChrisTerBeke authored Jan 19, 2025
1 parent 47ff2e8 commit 32838d4
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 290 deletions.
61 changes: 0 additions & 61 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,67 +77,6 @@
"id": "add_devices",
"template": "add_devices"
}
],
"settings": [
{
"type": "group",
"label": {
"en": "IP address",
"nl": "IP addres"
},
"children": [
{
"id": "discovered_address",
"label": {
"en": "Auto-discovered address",
"nl": "Automatisch ontdekte adres"
},
"type": "label",
"required": false,
"value": ""
},
{
"id": "address",
"label": {
"en": "Override IP Address",
"nl": "IP-adres overschrijven"
},
"type": "text",
"required": false,
"hint": {
"en": "Overrides the auto-discovered IP address of the device.",
"nl": "Overschrijft het automatisch ontdekte IP-adres van het apparaat."
}
}
]
},
{
"type": "group",
"label": {
"en": "Troubleshooting",
"nl": "Probleemoplossing"
},
"children": [
{
"id": "debugEnabled",
"type": "checkbox",
"label": {
"en": "Enable debug data",
"nl": "Schakel probleemverhelping in"
},
"value": false
},
{
"id": "apiData",
"type": "textarea",
"label": {
"en": "Last API response",
"nl": "Laatste API-reactie"
},
"value": "{}"
}
]
}
]
}
],
Expand Down
120 changes: 27 additions & 93 deletions drivers/uponor/device.ts
Original file line number Diff line number Diff line change
@@ -1,135 +1,69 @@
import { isIPv4 } from 'net'
import { Device, DiscoveryResultMAC } from 'homey'
import { UponorHTTPClient } from '../../lib/UponorHTTPClient'

const POLL_INTERVAL_MS = 1000 * 60 * 1
import { UponorDriver } from './driver'
import { MEASURE_TEMPERATURE_CAPABILITY, TARGET_TEMPERATURE_CAPABILITY, POLL_INTERVAL_MS, INIT_TIMEOUT_MS } from '../../lib/constants'

class UponorThermostatDevice extends Device {

private _syncInterval?: NodeJS.Timer
private _client?: UponorHTTPClient

async onInit(): Promise<void> {
this._init()
}

async onAdded(): Promise<void> {
this._init()
}

async onUninit(): Promise<void> {
this._uninit()
}

onDeleted(): void {
this.homey.clearInterval(this._syncInterval)
await this._syncCapabilities()
this.homey.setInterval(this._syncAttributes.bind(this), POLL_INTERVAL_MS)
this.homey.setTimeout(this._syncAttributes.bind(this), INIT_TIMEOUT_MS)
}

onDiscoveryResult(discoveryResult: DiscoveryResultMAC): boolean {
return this.getData().id.includes(discoveryResult.id)
}

async onDiscoveryAvailable(discoveryResult: DiscoveryResultMAC): Promise<void> {
await this._updateDiscoveredAddress(discoveryResult.address)
await this._updateAddress(discoveryResult.address, true)
await this._updateAddress(discoveryResult.address)
}

async onDiscoveryAddressChanged(discoveryResult: DiscoveryResultMAC): Promise<void> {
await this._updateDiscoveredAddress(discoveryResult.address)
await this._updateAddress(discoveryResult.address, true)
}

async onSettings({ newSettings }: { newSettings: { [key: string]: any } }): Promise<void> {
const addressUpdated = await this._updateAddress(newSettings.address as string)
if (!addressUpdated) throw new Error(`Could not connect to Uponor controller on IP address ${newSettings.address}`)
}

private _getAddress(): string | undefined {
const settingAddress = this.getSetting('address')
if (settingAddress && isIPv4(settingAddress)) return settingAddress
const storeAddress = this.getStoreValue('address')
if (storeAddress && isIPv4(storeAddress)) return storeAddress
return undefined
}

private async _updateAddress(newAddress: string, persist = false): Promise<boolean> {
if (newAddress.length === 0) {
newAddress = await this.getStoreValue('address')
}

if (!isIPv4(newAddress)) {
return false
}

if (persist) {
await this.setStoreValue('address', newAddress)
}

if (!this._client) return false
const success = await this._client.updateAddress(newAddress)
return success
}

private async _updateDiscoveredAddress(newAddress: string): Promise<void> {
if (newAddress.length === 0) return
await this.setSettings({ 'discovered_address': newAddress })
await this._updateAddress(discoveryResult.address)
}

async _init(): Promise<void> {
const address = this._getAddress()
if (!address) return this.setUnavailable('No IP address configured')
this._client = new UponorHTTPClient(address)
this._syncInterval = this.homey.setInterval(this._sync.bind(this), POLL_INTERVAL_MS)
this.homey.setTimeout(this._sync.bind(this), 2000)
private _getClient(): UponorHTTPClient {
const driver = this.driver as UponorDriver
return driver.getClient()
}

async _uninit(): Promise<void> {
this.homey.clearInterval(this._syncInterval)
this._syncInterval = undefined
this._client = undefined
private async _updateAddress(newAddress: string): Promise<boolean> {
const driver = this.driver as UponorDriver
return await driver.setIpAddress(newAddress)
}

private async _sync(): Promise<void> {
await this._syncCapabilities()
await this._syncAttributes()
private async _syncCapabilities(): Promise<void> {
await this._ensureCapability(MEASURE_TEMPERATURE_CAPABILITY)
await this._ensureCapability(TARGET_TEMPERATURE_CAPABILITY, this._setTargetTemperature.bind(this))
}

private async _syncCapabilities(): Promise<void> {
this.registerCapabilityListener('target_temperature', this._setTargetTemperature.bind(this))
private async _ensureCapability(capability: string, callback: Device.CapabilityCallback | undefined = undefined): Promise<void> {
if (!this.hasCapability(capability)) await this.addCapability(capability)
if (callback) this.registerCapabilityListener(capability, callback)
}

private async _syncAttributes(): Promise<void> {
if (!this._client) return this.setUnavailable('No Uponor client')
const canConnect = await this._client.testConnection()
if (!canConnect) return this.setUnavailable('Could not connect to Uponor controller on local network')

try {
await this._client.syncAttributes()
await this._getClient().syncAttributes()
const { controllerID, thermostatID } = this.getData()
const data = this._client.getThermostat(controllerID, thermostatID)
const data = this._getClient().getThermostat(controllerID, thermostatID)
if (!data) return this.setUnavailable('Could not find thermostat data')
this.setAvailable()
this.setCapabilityValue('measure_temperature', data.temperature)
this.setCapabilityValue('target_temperature', data.setPoint)
this.setCapabilityValue(MEASURE_TEMPERATURE_CAPABILITY, data.temperature)
this.setCapabilityValue(TARGET_TEMPERATURE_CAPABILITY, data.setPoint)
} catch (error) {
this.homey.error(error)
this.setUnavailable('Could not fetch data from Uponor controller')
}

try {
const { debugEnabled } = this.getSettings()
if (!debugEnabled) return
const debug = await this._client.debug()
this.setSettings({ apiData: JSON.stringify(debug) })
} catch (error) { }
}

private async _setTargetTemperature(value: number): Promise<void> {
if (!this._client) return this.setUnavailable('No Uponor client')
private async _setTargetTemperature(value: number, _opts: any): Promise<void> {
const { controllerID, thermostatID } = this.getData()

try {
await this._client.setTargetTemperature(controllerID, thermostatID, value)
await this._getClient().setTargetTemperature(controllerID, thermostatID, value)
} catch (error) {
this.homey.error(error)
this.setUnavailable('Could not send data to Uponor controller')
}
}
Expand Down
61 changes: 0 additions & 61 deletions drivers/uponor/driver.compose.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,66 +40,5 @@
"id": "add_devices",
"template": "add_devices"
}
],
"settings": [
{
"type": "group",
"label": {
"en": "IP address",
"nl": "IP addres"
},
"children": [
{
"id": "discovered_address",
"label": {
"en": "Auto-discovered address",
"nl": "Automatisch ontdekte adres"
},
"type": "label",
"required": false,
"value": ""
},
{
"id": "address",
"label": {
"en": "Override IP Address",
"nl": "IP-adres overschrijven"
},
"type": "text",
"required": false,
"hint": {
"en": "Overrides the auto-discovered IP address of the device.",
"nl": "Overschrijft het automatisch ontdekte IP-adres van het apparaat."
}
}
]
},
{
"type": "group",
"label": {
"en": "Troubleshooting",
"nl": "Probleemoplossing"
},
"children": [
{
"id": "debugEnabled",
"type": "checkbox",
"label": {
"en": "Enable debug data",
"nl": "Schakel probleemverhelping in"
},
"value": false
},
{
"id": "apiData",
"type": "textarea",
"label": {
"en": "Last API response",
"nl": "Laatste API-reactie"
},
"value": "{}"
}
]
}
]
}
Loading

0 comments on commit 32838d4

Please sign in to comment.