Skip to content

homewizard/swift-homewizard-energy

Repository files navigation

HomeWizard Energy: HWLocalAPI

Swift library to communicate with HomeWizard Energy devices using their local API.

This package is aimed at basic control of the device. Initial setup and configuration is assumed to be done with the official HomeWizard Energy app.

Swift 5.10 iOS macOS watchOS tvOS Linux Release

Features

  • Discover HomeWizard Energy devices on your local network (not supported for Linux)
  • Connect to a device and fetch it's latest measurement readings
  • Control the power state of Energy Sockets
  • A DeviceMonitor that allows you to fetch all measurement data of one or more devices repetitively using a specified update interval

Usage

This will briefly describe the usage. A more detailed documentation is added in the package.

Discovery

You can use the DeviceDiscoveryHandler to discover HomeWizard Energy devices on your local network through Bonjour.

Monitor

To continuously monitor your network, you can implement its delegate:

final class Monitor: DeviceDiscoveryDelegate {
    private(set) var discoveredDevices: [DiscoveredDevice] = []
    private var handler: DeviceDiscoveryHandler!

    init() {
        handler = DeviceDiscoveryHandler(delegate: self)
        handler.start()
    }

    deinit {
        handler.stop()
    }

    func deviceDiscoveryHandler(_ handler: DeviceDiscoveryHandler, didDiscover device: DiscoveredDevice) {
        discoveredDevices.append(device)
    }

    func deviceDiscoveryHandler(_ handler: DeviceDiscoveryHandler, didLoose device: DiscoveredDevice) {
        discoveredDevices
            .removeAll(where: {
                $0.serial == device.serial
            })
    }

    func deviceDiscoveryHandler(_ handler: DeviceDiscoveryHandler, didUpdate device: DiscoveredDevice, from oldDevice: DiscoveredDevice) {
        discoveredDevices
            .removeAll(where: {
                $0.serial == oldDevice.serial
            })
        discoveredDevices.append(device)
    }
}

Quick Lookup

Alternatively you can use a quick lookup to scan the network for just a couple of seconds and get the list with discovered devices:

final class Preferences {
    func usedDevice() async throws -> (any Device)? {
        guard let serial = UserDefaults
            .standard
            .string(forKey: "usedDevice") else {
            return nil
        }

        let discovered = await DeviceDiscoveryHandler.quickLookup()
        if let device = discovered.first(where: { $0.serial == serial }) {
            return try await device.load()
        } else {
            return nil
        }
    }

    func setUsedDevice(_ device: (any Device)?) {
        UserDefaults.standard.set(device?.serial, forKey: "usedDevice")
    }
}

Devices

Simple Data and State

Once you have discovered a device, or know its IP address, you can load basic information of the device, as well as getting its latest measurement data:

if let device = try await DeviceLoader.load("192.168.0.10") as? P1Meter {
    let measurement = try await device.fetchData()
    print("Current power usage: \(measurement.activePower) watt")
}

For Energy Sockets you can also get and set their state (power on/off, LED brightness, switch lock):

if let socket = try await DeviceLoader.load("192.168.0.10") as? EnergySocket {
    if try await socket.isPoweredOn {
        print("The socket was powered on. I will turn it off now...")
        try await socket.setPoweredOn(false)
    }
}

Monitoring

Besides getting measurement data manually as described above, you may also use the DeviceMonitor to continuously update the data of one or more devices (with a specified update interval).

final class MyMonitor: Sendable {
    /// Devices to monitor
    private let devices: [any Device]

    init(devices: [any Device]) {
        self.devices = devices

        // Register the observer
        NotificationCenter
            .default
            .addObserver(
                self,
                selector: #selector(dataReceived(_:)),
                name: DeviceMonitor.didUpdate,
                object: nil
            )

        // Add the devices to monitor
        DeviceMonitor.default.add(devices)
        // Start the monitor if needed
        if !DeviceMonitor.default.isRunning {
            DeviceMonitor.default.start()
        }
    }

    deinit {
        DeviceMonitor.default.remove(self.devices)
        NotificationCenter.default.removeObserver(self)
    }

    @objc private func dataReceived(_ notification: Notification) {
        if let data = notification.userInfo?[DeviceMonitor.UserInfo.data] as? P1MeterData,
           let power = data.activePower {

            if power < 0 {
                print("Your main connection is now delivering \(power) watt back into the grid")
            } else {
                print("Your main connection is now using \(power) watt from the grid")
            }

        }
    }
}

Documentation

The package is documented using DocC.

When added as a dependency to your project, use Product > Build Documentation (or ⌃⇧⌘D) once to add it to your Developer Documentation.

For more details about the Local API itself, you may take a look at our API documentation.

Installation

Swift Package Manager (SPM)

Swift Package Manager is a tool for managing Swift packages, both executables as libraries. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.

Package File

Add HWLocalAPI as a package to your Package.swift file and then specify it as a dependency of the Target in which you wish to use it.

// swift-tools-version: 5.10
import PackageDescription

let package = Package(
    name: "MyProject",
    platforms: [
       .macOS(.v13)
    ],
    dependencies: [
        .package(url: "https://github.com/HomeWizard/swift-homewizard-energy", from: "1.0.0")
    ],
    targets: [
        .target(
            name: "MyProject",
            dependencies: [
                .product(name: "HWLocalAPI", package: "swift-homewizard-energy")
            ]
        ),
        .testTarget(
            name: "MyProjectTests",
            dependencies: ["MyProject"]
        ),
    ]
)

Xcode

To add HWLocalAPI as a dependency to your Xcode project, select File > Add Package Dependency and enter the repository URL in the field top right.

Development and contribution

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Release Notes

See CHANGELOG.md for a list of changes.

License

HWLocalAPI is available under the MIT license. See the LICENSE file for more info.