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.
- 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
This will briefly describe the usage. A more detailed documentation is added in the package.
You can use the DeviceDiscoveryHandler
to discover HomeWizard Energy devices on your local network through Bonjour.
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)
}
}
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")
}
}
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)
}
}
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")
}
}
}
}
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.
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.
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"]
),
]
)
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.
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
See CHANGELOG.md for a list of changes.
HWLocalAPI is available under the MIT license. See the LICENSE file for more info.