Skip to content

Commit

Permalink
improve
Browse files Browse the repository at this point in the history
  • Loading branch information
OlivierZal committed Jun 30, 2024
1 parent f5746e6 commit 79fe32f
Show file tree
Hide file tree
Showing 15 changed files with 146 additions and 117 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"prettier": "^3.3.2",
"ts-node": "^10.9.2",
"typedoc": "^0.26.3",
"typedoc-plugin-markdown": "^4.1.0",
"typedoc-plugin-markdown": "^4.1.1",
"typescript": "^5.5.2",
"typescript-eslint": "^8.0.0-alpha.34"
},
Expand Down
75 changes: 60 additions & 15 deletions src/facades/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,47 @@ import {
type TilesData,
type UpdateDeviceData,
type Values,
flags,
setDataMapping,
valueMapping,
} from '../types'
import { YEAR_1970, nowISO } from './utils'
import type API from '../services'
import BaseFacade from './base'
import { DeviceFacadeErv } from '.'
import type { IDeviceFacade } from './interfaces'

// @ts-expect-error: most runtimes do not support it natively
Symbol.metadata ??= Symbol('Symbol.metadata')

export const isValue = (
_target: unknown,
context: ClassGetterDecoratorContext,
): void => {
context.metadata[Symbol('value')] ??= []
;(context.metadata as string[]).push(context.name as string)
}

const jsonify = (instance: object): Record<string, unknown> => {
const values = instance.constructor[Symbol.metadata]?.[Symbol('value')] as
| string[]
| undefined
if (!values) {
throw new Error('No members marked with @values')
}
return values.reduce(
(acc, key) => ({
...acc,
[key]: instance[key as keyof typeof instance] as unknown,
}),
{},
)
}

export default abstract class<T extends keyof typeof DeviceType>
extends BaseFacade<DeviceModelAny>
implements IDeviceFacade<T>
{
public readonly type: T

protected readonly frostProtectionLocation = 'DeviceIds'

protected readonly holidayModeLocation = 'Devices'
Expand All @@ -30,34 +59,50 @@ export default abstract class<T extends keyof typeof DeviceType>

protected readonly tableName = 'DeviceLocation'

protected abstract readonly flags: Record<keyof Values[T], number>
readonly #flags: Record<keyof Values[T], number>

protected abstract readonly setDataMapping: Record<
readonly #setDataMapping: Record<
NonFlagsKeyOf<UpdateDeviceData[T]>,
keyof Values[T]
>

protected abstract readonly valueMapping: Record<
readonly #type: T

readonly #valueMapping: Record<
keyof Values[T],
NonFlagsKeyOf<UpdateDeviceData[T]>
>

public constructor(api: API, model: DeviceModel<T>) {
super(api, model as DeviceModelAny)
this.type = this.model.type as T
this.#type = model.type
this.#flags = flags[this.#type] as Record<keyof Values[T], number>
this.#setDataMapping = setDataMapping[this.#type] as Record<
NonFlagsKeyOf<UpdateDeviceData[T]>,
keyof Values[T]
>
this.#valueMapping = valueMapping[this.#type] as Record<
keyof Values[T],
NonFlagsKeyOf<UpdateDeviceData[T]>
>
}

@isValue
public get power(): boolean {
return this.data.Power
}

public get data(): ListDevice[T]['Device'] {
return this.model.data
}

public get power(): boolean {
return this.data.Power
public get values(): Values[T] {
return jsonify(this)
}

get #setData(): Omit<UpdateDeviceData[T], 'EffectiveFlags'> {
return Object.fromEntries(
Object.entries(this.data).filter(([key]) => key in this.setDataMapping),
Object.entries(this.data).filter(([key]) => key in this.#setDataMapping),
) as Omit<UpdateDeviceData[T], 'EffectiveFlags'>
}

Expand All @@ -81,7 +126,7 @@ export default abstract class<T extends keyof typeof DeviceType>
from?: string | null
to?: string | null
}): Promise<EnergyData[T]> {
if (this.type === 'Erv') {
if (this instanceof DeviceFacadeErv) {
throw new Error('Erv devices do not support energy reports')
}
return (
Expand Down Expand Up @@ -117,13 +162,13 @@ export default abstract class<T extends keyof typeof DeviceType>
const updateData = {
...Object.fromEntries(
Object.entries(values).map(([key, value]) => [
this.valueMapping[key as keyof Values[T]],
this.#valueMapping[key as keyof Values[T]],
value,
]),
),
}
const { data } = await this.api.set({
heatPumpType: this.type,
heatPumpType: this.#type,
postData: {
...this.#setData,
...updateData,
Expand All @@ -137,7 +182,7 @@ export default abstract class<T extends keyof typeof DeviceType>

#getFlags(values: Values[T]): number {
return (Object.keys(values) as (keyof Values[T])[]).reduce(
(acc, key) => Number(BigInt(this.flags[key]) | BigInt(acc)),
(acc, key) => Number(BigInt(this.#flags[key]) | BigInt(acc)),
FLAG_UNCHANGED,
)
}
Expand All @@ -150,8 +195,8 @@ export default abstract class<T extends keyof typeof DeviceType>
Object.entries(newData).filter(([key]) =>
Number(
BigInt(
this.flags[
this.setDataMapping[key as NonFlagsKeyOf<UpdateDeviceData[T]>]
this.#flags[
this.#setDataMapping[key as NonFlagsKeyOf<UpdateDeviceData[T]>]
],
) & BigInt(effectiveFlags),
),
Expand Down
23 changes: 7 additions & 16 deletions src/facades/device_ata.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,28 @@
import {
type FanSpeed,
type Horizontal,
type OperationMode,
type Vertical,
flagsAta,
setDataMappingAta,
valueMappingAta,
} from '../types'
import BaseDeviceFacade from './device'
import BaseDeviceFacade, { isValue } from './device'
import type { FanSpeed, Horizontal, OperationMode, Vertical } from '../types'

export default class extends BaseDeviceFacade<'Ata'> {
protected readonly flags = flagsAta

protected readonly setDataMapping = setDataMappingAta

protected readonly valueMapping = valueMappingAta

@isValue
public get fan(): FanSpeed {
return this.data.FanSpeed
}

@isValue
public get horizontal(): Horizontal {
return this.data.VaneHorizontalDirection
}

@isValue
public get mode(): OperationMode {
return this.data.OperationMode
}

@isValue
public get temperature(): number {
return this.data.SetTemperature
}

@isValue
public get vertical(): Vertical {
return this.data.VaneVerticalDirection
}
Expand Down
25 changes: 12 additions & 13 deletions src/facades/device_atw.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,53 @@
import {
type OperationModeZone,
flagsAtw,
setDataMappingAtw,
valueMappingAtw,
} from '../types'
import BaseDeviceFacade from './device'
import BaseDeviceFacade, { isValue } from './device'
import type { OperationModeZone } from '../types'

export default class extends BaseDeviceFacade<'Atw'> {
protected readonly flags = flagsAtw

protected readonly setDataMapping = setDataMappingAtw

protected readonly valueMapping = valueMappingAtw

@isValue
public get coolFlowTemperature(): number {
return this.data.SetCoolFlowTemperatureZone1
}

@isValue
public get coolFlowTemperatureZone2(): number {
return this.data.SetCoolFlowTemperatureZone2
}

@isValue
public get forcedHotWater(): boolean {
return this.data.ForcedHotWaterMode
}

@isValue
public get heatFlowTemperature(): number {
return this.data.SetHeatFlowTemperatureZone1
}

@isValue
public get heatFlowTemperatureZone2(): number {
return this.data.SetHeatFlowTemperatureZone2
}

@isValue
public get hotWaterTemperature(): number {
return this.data.SetTankWaterTemperature
}

@isValue
public get mode(): OperationModeZone {
return this.data.OperationModeZone1
}

@isValue
public get modeZone2(): OperationModeZone {
return this.data.OperationModeZone2
}

@isValue
public get temperature(): number {
return this.data.SetTemperatureZone1
}

@isValue
public get temperatureZone2(): number {
return this.data.SetTemperatureZone2
}
Expand Down
11 changes: 3 additions & 8 deletions src/facades/device_erv.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { flagsErv, setDataMappingErv, valueMappingErv } from '../types'
import BaseDeviceFacade from './device'
import BaseDeviceFacade, { isValue } from './device'

export default class extends BaseDeviceFacade<'Erv'> {
protected readonly flags = flagsErv

protected readonly setDataMapping = setDataMappingErv

protected readonly valueMapping = valueMappingErv

@isValue
public get fan(): number {
return this.data.SetFanSpeed
}

@isValue
public get mode(): number {
return this.data.VentilationMode
}
Expand Down
3 changes: 2 additions & 1 deletion src/facades/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
SuccessData,
TilesData,
UpdateDeviceData,
Values,
Vertical,
WifiData,
} from '../types'
Expand Down Expand Up @@ -118,5 +119,5 @@ export interface IDeviceFacade<T extends keyof typeof DeviceType>
getTiles: ((select?: false | null) => Promise<TilesData<null>>) &
((select: true | DeviceModel<T>) => Promise<TilesData<T>>)
set: (postData: UpdateDeviceData[T]) => Promise<SetDeviceData[T]>
type: T
values: Values[T]
}
7 changes: 3 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,12 @@ export {
OperationModeZone,
VentilationMode,
Vertical,
flags,
flagsAta,
flagsAtw,
flagsErv,
setData,
setDataMappingAta,
setDataMappingAtw,
setDataMappingErv,
setDataMapping,
valueMapping,
valueMappingAta,
valueMappingAtw,
valueMappingErv,
Expand Down
8 changes: 5 additions & 3 deletions src/models/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
type ListDeviceAny,
type NonFlagsKeyOf,
type UpdatedDeviceData,
setData,
setDataMapping,
} from '../types'
import BaseModel from './base'
import BuildingModel from './building'
Expand Down Expand Up @@ -51,7 +51,9 @@ export default class DeviceModel<T extends keyof typeof DeviceType>
this.#data = data
this.floorId = floorId
this.type = DeviceType[type] as T
this.#setData = setData[this.type] as NonFlagsKeyOf<UpdatedDeviceData<T>>[]
this.#setData = Object.values(setDataMapping[this.type]) as NonFlagsKeyOf<
UpdatedDeviceData<T>
>[]
}

public get area(): AreaModelAny | null {
Expand Down Expand Up @@ -119,7 +121,7 @@ export default class DeviceModel<T extends keyof typeof DeviceType>
...this.#data,
...(this.#cleanData(data) satisfies Omit<
UpdatedDeviceData<T>,
'SetFanSpeed' | 'VaneHorizontal' | 'VaneVertical'
DeviceDataAtaKeysNotInList
>),
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const setting = <T extends API, R extends string | null | undefined>(
context: ClassAccessorDecoratorContext<T, R>,
): ClassAccessorDecoratorResult<T, R> => ({
get(this: T): R {
const key = String(context.name)
const key = context.name as string
if (!isAPISetting(key)) {
throw new Error(`Invalid setting: ${key}`)
}
Expand All @@ -91,7 +91,7 @@ const setting = <T extends API, R extends string | null | undefined>(
: (this.settingManager.get(key) as R)
},
set(this: T, value: R): void {
const key = String(context.name)
const key = context.name as string
if (!isAPISetting(key)) {
throw new Error(`Invalid setting: ${key}`)
}
Expand Down
Loading

0 comments on commit 79fe32f

Please sign in to comment.