Skip to content

Commit

Permalink
Add support for Shelly Gen2 power meters
Browse files Browse the repository at this point in the history
Only ones implementing the "Switch" component for now
  • Loading branch information
Jalle19 committed Oct 13, 2023
1 parent ff75c60 commit 20cfa01
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 12 deletions.
17 changes: 16 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import {
} from './iotawatt'
import { getSensorData as getVirtualSensorData } from './virtual'
import { getSensorData as getUnmeteredSensorData } from './unmetered'
import { CharacteristicsSensorType, SensorType, UnmeteredSensor, VirtualSensor } from './sensor'
import {
CharacteristicsSensorType,
SensorType,
ShellySensor,
ShellyType,
UnmeteredSensor,
VirtualSensor,
} from './sensor'
import { Circuit, CircuitType } from './circuit'
import { Publisher, PublisherType } from './publisher'
import { InfluxDBPublisher, InfluxDBPublisherImpl } from './publisher/influxdb'
Expand All @@ -29,6 +36,14 @@ export const parseConfig = (configFileContents: string): Config => {
if (circuit.type === undefined) {
circuit.type = CircuitType.Circuit
}

if (circuit.sensor.type === SensorType.Shelly) {
// Use Gen1 as default Shelly type
const shellySensor = circuit.sensor as ShellySensor
if (shellySensor.shelly.type === undefined) {
shellySensor.shelly.type = ShellyType.Gen1
}
}
}

// Resolve parent relationships
Expand Down
6 changes: 6 additions & 0 deletions src/sensor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export enum SensorType {
Unmetered = 'unmetered',
}

export enum ShellyType {
Gen1 = 'gen1',
Gen2PM = 'gen2-pm',
}

export enum CharacteristicsSensorType {
Iotawatt = 'iotawatt',
}
Expand Down Expand Up @@ -40,6 +45,7 @@ export interface IotawattCharacteristicsSensor extends CharacteristicsSensor {

interface ShellySensorSettings {
address: string
type: undefined | ShellyType
meter: number
}

Expand Down
56 changes: 45 additions & 11 deletions src/shelly.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
import { emptySensorData, PowerSensorData, PowerSensorPollFunction, ShellySensor } from './sensor'
import { emptySensorData, PowerSensorData, PowerSensorPollFunction, ShellySensor, ShellyType } from './sensor'
import { Circuit } from './circuit'
import { getDedupedResponse } from './http/client'
import { AxiosResponse } from 'axios'

type MeterResult = {
type Gen1MeterResult = {
power: number
}

type StatusResult = {
meters: MeterResult[]
type Gen1StatusResult = {
meters: Gen1MeterResult[]
}

type Gen2GetStatusResult = {
apower: number
}

const getSensorDataUrl = (sensor: ShellySensor): string => {
const address = sensor.shelly.address
const meter = sensor.shelly.meter

// Request a different endpoint depending on what type of Shelly we're dealing with
switch (sensor.shelly.type as ShellyType) {
case ShellyType.Gen2PM:
return `http://${address}/rpc/Switch.GetStatus?id=${meter}`
case ShellyType.Gen1:
return `http://${address}/status`
}
}

const parseGen1Response = (timestamp: number, circuit: Circuit, httpResponse: AxiosResponse): PowerSensorData => {
const sensor = circuit.sensor as ShellySensor
const data = httpResponse.data as Gen1StatusResult

return {
timestamp: timestamp,
circuit: circuit,
watts: data.meters[sensor.shelly.meter].power,
}
}

const parseGen2PMResponse = (timestamp: number, circuit: Circuit, httpResponse: AxiosResponse): PowerSensorData => {
const data = httpResponse.data as Gen2GetStatusResult

return `http://${address}/status`
return {
timestamp: timestamp,
circuit: circuit,
watts: data.apower,
}
}

export const getSensorData: PowerSensorPollFunction = async (
Expand All @@ -24,13 +57,14 @@ export const getSensorData: PowerSensorPollFunction = async (
const url = getSensorDataUrl(sensor)

try {
const result = await getDedupedResponse(timestamp, url)
const data = result.data as StatusResult
const httpResponse = await getDedupedResponse(timestamp, url)

return {
timestamp: timestamp,
circuit: circuit,
watts: data.meters[sensor.shelly.meter].power,
// Parse the response differently depending on what type of Shelly we're dealing with
switch (sensor.shelly.type as ShellyType) {
case ShellyType.Gen1:
return parseGen1Response(timestamp, circuit, httpResponse)
case ShellyType.Gen2PM:
return parseGen2PMResponse(timestamp, circuit, httpResponse)
}
} catch (e) {
console.error((e as Error).message)
Expand Down

0 comments on commit 20cfa01

Please sign in to comment.