Skip to content

Commit

Permalink
new API
Browse files Browse the repository at this point in the history
  • Loading branch information
OlivierZal committed Nov 20, 2023
1 parent c9da7f8 commit 2f75032
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 100 deletions.
35 changes: 35 additions & 0 deletions .homeycompose/capabilities/operation_mode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"type": "enum",
"title": {
"en": "Operation mode"
},
"getable": true,
"setable": true,
"uiComponent": "picker",
"values": [
{
"id": "green",
"title": {
"en": "Green"
}
},
{
"id": "comfort",
"title": {
"en": "Comfort"
}
},
{
"id": "fast",
"title": {
"en": "Fast"
}
},
{
"id": "auto",
"title": {
"en": "Automatic (i-Memory)"
}
}
]
}
31 changes: 18 additions & 13 deletions app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { App } from 'homey' // eslint-disable-line import/no-extraneous-dependencies
import axios from 'axios'
import { wrapper } from 'axios-cookiejar-support'
import { CookieJar } from 'tough-cookie'
import withAPI, { getErrorMessage } from './mixins/withAPI'
import type {
LoginCredentials,
Expand All @@ -9,8 +11,9 @@ import type {
HomeySettingValue,
} from './types'

axios.defaults.baseURL = 'https://www.ariston-net.remotethermo.com/api/v2'
axios.defaults.headers.common['Content-Type'] = 'application/json'
wrapper(axios)
axios.defaults.jar = new CookieJar()
axios.defaults.baseURL = 'https://www.ariston-net.remotethermo.com'

export = class AristonApp extends withAPI(App) {
#loginTimeout!: NodeJS.Timeout
Expand All @@ -35,21 +38,23 @@ export = class AristonApp extends withAPI(App) {
return false
}
const postData: LoginPostData = {
usr: username,
pwd: password,
email: username,
password,
rememberMe: true,
}
const { data } = await this.api.post<LoginData>(
'/accounts/login',
'/R2/Account/Login',
postData,
)
const { token } = data
this.setSettings({
username,
password,
token,
})
this.refreshLogin(loginCredentials)
return true
const { ok } = data
if (ok) {
this.setSettings({
username,
password,
})
this.refreshLogin(loginCredentials)
}
return ok
} catch (error: unknown) {
throw new Error(getErrorMessage(error))
}
Expand Down
75 changes: 46 additions & 29 deletions drivers/nuos/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ import type NuosDriver from './driver'
import type AristonApp from '../../app'
import withAPI from '../../mixins/withAPI'
import type {
Success,
Failure,
CapabilityValue,
DeviceDetails,
PlantData,
Settings,
} from '../../types'

const pathSuffixMapping: Record<string, string> = {
onoff: 'switch',
measure_temperature: 'waterTemp',
target_temperature: 'procReqTemp',
enum OperationMode {
green = 0,
comfort = 1,
fast = 2,
auto = 3,
}

export = class NuosDevice extends withAPI(Device) {
Expand Down Expand Up @@ -117,11 +116,27 @@ export = class NuosDevice extends withAPI(Device) {
if ((this.getSetting('always_on') as boolean) && !(value as boolean)) {
await this.setWarning(this.homey.__('warnings.always_on'))
} else {
await this.setPlantData(pathSuffixMapping[capability], value)
await this.plantData({
// @ts-expect-error: `on` is not a valid key
data: { plantData: { on: value as boolean } },
})
}
break
case 'operation_mode':
await this.plantData({
data: {
// @ts-expect-error: `on` is not a valid key
plantData: {
opMode: OperationMode[value as keyof typeof OperationMode],
},
},
})
break
case 'target_temperature':
await this.setPlantData(pathSuffixMapping[capability], value)
await this.plantData({
// @ts-expect-error: `on` is not a valid key
data: { plantData: { comfortTemp: value as number } },
})
break
default:
}
Expand Down Expand Up @@ -168,39 +183,41 @@ export = class NuosDevice extends withAPI(Device) {

private async updateCapabilities(): Promise<void> {
try {
const data: PlantData | null = await this.getPlantData()
const data: PlantData | null = await this.plantData()
if (!data) {
return
}
const { on, waterTemp, comfortTemp } = data
await this.setCapabilityValue('target_temperature', comfortTemp)
const { on, opMode, comfortTemp, waterTemp } = data.data.plantData
await this.setCapabilityValue('measure_temperature', waterTemp)
await this.setCapabilityValue('onoff', on)
await this.setCapabilityValue('operation_mode', OperationMode[opMode])
await this.setCapabilityValue('target_temperature', comfortTemp)
} catch (error: unknown) {
// Logged by `withAPI`
}
}

private async getPlantData(): Promise<PlantData | null> {
private async plantData(
postData?: Partial<PlantData>,
): Promise<PlantData | null> {
try {
const { data } = await this.api.get<PlantData>(
`/velis/slpPlantData/${this.id}`,
)
return data
} catch (error: unknown) {
return null
}
}

private async setPlantData(
pathSuffix: string,
postData: CapabilityValue,
): Promise<Failure | Success | null> {
try {
const { data } = await this.api.post<Failure | Success>(
`/velis/slpPlantData/${this.id}/${pathSuffix}`,
{ a: postData },
const method: 'get' | 'post' = postData ? 'post' : 'get'
const methodPath: 'GetData' | 'SetData' = postData ? 'SetData' : 'GetData'
const url: URL = new URL(
`/R2/PlantHomeSlp/${methodPath}/${this.id}`,
this.api.defaults.baseURL,
)
if (!postData) {
url.search = new URLSearchParams({
fetchSettings: 'true',
fetchTimeProg: 'false',
}).toString()
}
const { data } = await this.api<PlantData>({
method,
url: url.toString(),
data: postData,
})
return data
} catch (error: unknown) {
return null
Expand Down
7 changes: 6 additions & 1 deletion drivers/nuos/driver.compose.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
"name": {
"en": "Nuos"
},
"capabilities": ["measure_temperature", "onoff", "target_temperature"],
"capabilities": [
"measure_temperature",
"onoff",
"operation_mode",
"target_temperature"
],
"capabilitiesOptions": {
"target_temperature": {
"min": 40,
Expand Down
2 changes: 1 addition & 1 deletion drivers/nuos/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export = class NuosDriver extends withAPI(Driver) {

private async discoverDevices(): Promise<DeviceDetails[]> {
try {
const { data } = await this.api.get<Plant[]>('/velis/plants')
const { data } = await this.api.get<Plant[]>('/api/v2/velis/plants')
return data.map(
({ gw, name }): DeviceDetails => ({
name,
Expand Down
18 changes: 4 additions & 14 deletions mixins/withAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,13 @@ import axios, {
type AxiosResponse,
type InternalAxiosRequestConfig,
} from 'axios'
import type { Failure, HomeyClass, HomeySettings } from '../types'
import type { HomeyClass } from '../types'

type APIClass = new (...args: any[]) => {
readonly api: AxiosInstance
}

function getAPIErrorMessage(error: AxiosError): string {
const { data } = error.response ?? {}
if (data !== undefined) {
const errorMessage: string = (data as Failure).Message ?? ''
if (errorMessage) {
return errorMessage
}
}
return error.message
}

Expand Down Expand Up @@ -63,15 +56,12 @@ export default function withAPI<T extends HomeyClass>(base: T): APIClass & T {
private handleRequest(
config: InternalAxiosRequestConfig,
): InternalAxiosRequestConfig {
const updatedConfig: InternalAxiosRequestConfig = { ...config }
updatedConfig.headers['ar.authToken'] =
(this.homey.settings.get('token') as HomeySettings['token']) ?? ''
this.log(
'Sending request:',
updatedConfig.url,
updatedConfig.method === 'post' ? updatedConfig.data : '',
config.url,
config.method === 'post' ? config.data : '',
)
return updatedConfig
return config
}

private handleResponse(response: AxiosResponse): AxiosResponse {
Expand Down
Loading

0 comments on commit 2f75032

Please sign in to comment.