From 4c43032ea9ef4a0cbecb6d41257fc1a56f9b1af9 Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Fri, 13 Oct 2023 21:50:20 +0300 Subject: [PATCH] Add support for using Shelly EM devices as characteristics sensors --- src/config.ts | 8 ++++++- src/sensor.ts | 6 +++++ src/shelly.ts | 66 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/config.ts b/src/config.ts index 1a68783..945b47f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,8 @@ import YAML from 'yaml' -import { getSensorData as getShellySensorData } from './shelly' +import { + getCharacteristicsSensorData as getShellyCharacteristicsSensorData, + getSensorData as getShellySensorData, +} from './shelly' import { getCharacteristicsSensorData as getIotawattCharacteristicsSensorData, getSensorData as getIotawattSensorData, @@ -106,6 +109,9 @@ export const parseConfig = (configFileContents: string): Config => { case CharacteristicsSensorType.Iotawatt: c.sensor.pollFunc = getIotawattCharacteristicsSensorData break + case CharacteristicsSensorType.Shelly: + c.sensor.pollFunc = getShellyCharacteristicsSensorData + break } } diff --git a/src/sensor.ts b/src/sensor.ts index 5d3f034..538fd0e 100644 --- a/src/sensor.ts +++ b/src/sensor.ts @@ -16,6 +16,7 @@ export enum ShellyType { export enum CharacteristicsSensorType { Iotawatt = 'iotawatt', + Shelly = 'shelly', } export type PowerSensorPollFunction = ( @@ -58,6 +59,11 @@ export interface ShellySensor extends PowerSensor { shelly: ShellySensorSettings } +export interface ShellyCharacteristicsSensor extends CharacteristicsSensor { + type: CharacteristicsSensorType.Shelly + shelly: ShellySensorSettings +} + interface IotawattSensorSettings { address: string name: string diff --git a/src/shelly.ts b/src/shelly.ts index ac8093a..38d37d1 100644 --- a/src/shelly.ts +++ b/src/shelly.ts @@ -1,7 +1,18 @@ -import { emptySensorData, PowerSensorData, PowerSensorPollFunction, ShellySensor, ShellyType } from './sensor' +import { + CharacteristicsSensorData, + CharacteristicsSensorPollFunction, + emptyCharacteristicsSensorData, + emptySensorData, + PowerSensorData, + PowerSensorPollFunction, + ShellyCharacteristicsSensor, + ShellySensor, + ShellyType, +} from './sensor' import { Circuit } from './circuit' import { getDedupedResponse } from './http/client' import { AxiosResponse } from 'axios' +import { Characteristics } from './characteristics' type Gen1MeterResult = { power: number @@ -17,11 +28,17 @@ type Gen2SwitchGetStatusResult = { type Gen2EMGetStatusResult = { a_act_power: number + a_voltage: number + a_freq: number b_act_power: number + b_voltage: number + b_freq: number c_act_power: number + c_voltage: number + c_freq: number } -const getSensorDataUrl = (sensor: ShellySensor): string => { +const getSensorDataUrl = (sensor: ShellySensor | ShellyCharacteristicsSensor): string => { const address = sensor.shelly.address const meter = sensor.shelly.meter @@ -105,3 +122,48 @@ export const getSensorData: PowerSensorPollFunction = async ( return emptySensorData(timestamp, circuit) } } + +export const getCharacteristicsSensorData: CharacteristicsSensorPollFunction = async ( + timestamp: number, + characteristics: Characteristics, +): Promise => { + const sensor = characteristics.sensor as ShellyCharacteristicsSensor + const url = getSensorDataUrl(sensor) + + // Only support gen2-em sensors + if (sensor.shelly.type !== ShellyType.Gen2EM) { + throw new Error(`Shelly sensor type ${sensor.shelly.type} not supported as characteristics sensor`) + } + + try { + const httpResponse = await getDedupedResponse(timestamp, url) + const data = httpResponse.data as Gen2EMGetStatusResult + + let voltage = 0 + let frequency = 0 + switch (sensor.shelly.phase) { + case 'a': + voltage = data.a_voltage + frequency = data.a_freq + break + case 'b': + voltage = data.b_voltage + frequency = data.b_freq + break + case 'c': + voltage = data.c_voltage + frequency = data.c_freq + break + } + + return { + timestamp: timestamp, + characteristics: characteristics, + voltage: voltage, + frequency: frequency, + } + } catch (e) { + console.error((e as Error).message) + return emptyCharacteristicsSensorData(timestamp, characteristics) + } +}