bitbee / php-dashboard-module-device-manager
IoT device management module for php-modular-dashboard: logging, automation, and dashboard for home automation
Package info
github.com/yoodoohai/php-dashboard-module-device-manager
pkg:composer/bitbee/php-dashboard-module-device-manager
Requires
- php: >=8.1
Suggests
- bitbee/php-modular-dashboard: For web dashboard integration with user management
This package is auto-updated.
Last update: 2026-03-24 22:05:19 UTC
README
A standalone PHP package for managing smart home devices with support for data collection, control commands, and solar-excess-based automation. Originally designed for solar power systems, it can be used with any devices that support HTTP APIs.
Features
- 🔌 Multi-Device Support - Manage Solax inverters, Go-E wallboxes, Shelly switches, Fröling heating systems, Midea ACs
- ⚡ Solar Automation - Automatic load shifting based on excess solar power and battery state
- 📊 Data Collection - Periodic device polling with JSON logging
- 🎮 Control Interface - Send commands to devices via HTTP APIs
- 🔒 Permission System - Granular device-level access control
- 🌍 Multi-language - Device labels and UI text in English and German
- 📝 Command Logging - Full audit trail of all device commands
Requirements
- PHP 8.0 or higher
- cURL extension (recommended) or allow_url_fopen
- Network access to devices
Installation
As Part of Dashboard
The device_manager is pre-installed in the modules/ directory of the main dashboard. No additional installation needed.
Standalone Usage
# Copy the device_manager folder to your project cp -r device_manager /path/to/your/project/ # Include bootstrap and use require_once 'device_manager/bootstrap.php'; require_once 'device_manager/src/DeviceManager.php'; $manager = new \Devicemanager\DeviceManager();
Standalone logging
When running standalone, logs default to device_manager/logs/.
You can override the log directory by setting either:
DEVICE_MANAGER_LOG_BASE=/absolute/path/to/logsLOG_BASE_PATH=/absolute/path/to/logs
Standalone automation (CLI)
# optional: force standalone mode even if embedded
DEVICE_MANAGER_STANDALONE=1 php automate.php
Standalone setup UI (web)
You can run the setup UI without the main dashboard:
cd device_manager DEVICE_MANAGER_LOG_BASE="$(pwd)/logs" php -S 127.0.0.1:8089 -t public
Then open http://127.0.0.1:8089/setup.php in your browser.
Setup UI (Dashboard + Standalone)
- Dashboard module:
?module=DeviceSetup&method=edit&id=<deviceId> - Standalone setup page (within the module directory):
public/setup.php
Both UIs use the shared helpers in src/SetupHelper.php and store configuration in config/devices.registry.json.
Quick Start
1. Configure Devices
Copy the example registry:
cp config/devices.registry.example.json config/devices.registry.json
Edit config/devices.registry.json to add your devices:
{
"devices": [
{
"id": "my_device",
"type": "http_switch",
"name": "My Smart Switch",
"host": "192.168.1.100",
"enabled": true
}
]
}
2. Collect Data
require_once 'bootstrap.php'; require_once 'src/DeviceManager.php'; $manager = new \Devicemanager\DeviceManager(); // Collect from single device $data = $manager->collect('my_device'); // Collect from all enabled devices $errors = $manager->collectAllDevices(true); // true = CLI output
3. Control Devices
// Send a control command $result = $manager->control('my_device', 'power', 'on'); if ($result['success']) { echo "Device turned on!"; }
4. Run Automation (CLI)
php automate.php
Supported Device Types
| Type | Class | Description |
|---|---|---|
solax |
SolaxDevice | Solax hybrid inverters via Modbus TCP |
goe_charger |
GoeChargerDevice | Go-E Charger wallboxes (API v2) |
http_switch |
HttpSwitchDevice | Generic HTTP-controlled relays (Shelly, etc.) |
froeling |
FroelingDevice | Fröling heating systems via cloud API |
midea_ac |
MideaACDevice | Midea air conditioners via local control |
Device Configuration
Common Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique device identifier |
type |
string | Device type (see table above) |
name |
string | Human-readable name |
host |
string | Device IP or hostname |
enabled |
boolean | Enable/disable device |
role |
string | producer, consumer, heating, cooling |
logging |
object | Log file settings |
automation |
object | Automation configuration |
capabilities |
object | Data points and metadata |
Example: Shelly Switch
{
"id": "shelly_heater",
"type": "http_switch",
"name": "Water Heater",
"enabled": true,
"role": "consumer",
"collector": {
"host": "192.168.1.50",
"url": "http://{host}/status",
"method": "GET",
"mapping": {
"status_key": ["relays", 0, "ison"],
"power_key": {"path": ["meters", 0, "power"]}
}
},
"actions": {
"power": {
"values": {
"On": {"url": "http://{host}/relay/0?turn=on", "color": "#4CAF50"},
"Off": {"url": "http://{host}/relay/0?turn=off", "color": "#9E9E9E"}
}
}
},
"automation": {
"enabled": true,
"min_watt": 2000,
"turn_on_action": "power",
"turn_on_value": "On",
"turn_off_action": "power",
"turn_off_value": "Off"
}
}
Example: Go-E Wallbox
{
"id": "wallbox",
"type": "goe_charger",
"name": "Garage Wallbox",
"host": "192.168.1.60",
"enabled": true,
"role": "consumer",
"state_file": "wallbox_state.json",
"automation": {
"enabled": true,
"battery_discharge_policy": true,
"zoe_mode": true
}
}
Automation System
The automation system allocates excess solar power to consuming devices based on priority.
Configuration
{
"settings": {
"automation": {
"power_source_device_id": "solax_main",
"surplus_mode": "export_plus_divertable_battery",
"battery_min_soc_start": 20,
"battery_start_hour": 6,
"battery_threshold": 90,
"battery_target_hour": 13,
"reserve_power": 100,
"min_power": 300
}
}
}
| Setting | Description |
|---|---|
power_source_device_id |
Device ID used as system power source (must provide grid_power, battery_power, battery_soc in raw data, or "Grid W"/"Batt W"/"Batt %" in formatted logs) |
surplus_mode |
export_only or export_plus_divertable_battery |
battery_min_soc_start |
SOC (%) threshold at battery_start_hour |
battery_start_hour |
Start hour for dynamic SOC ramp |
battery_threshold |
Target SOC (%) by battery_target_hour |
battery_target_hour |
Target hour for dynamic SOC ramp |
reserve_power |
Power (W) to always keep available |
min_power |
Minimum excess before enabling any device |
Device Automation Options
{
"automation": {
"enabled": true,
"priority": 10,
"min_watt": 1500,
"turn_on_action": "power",
"turn_on_value": "On",
"turn_off_action": "power",
"turn_off_value": "Off",
"manual_override_permission": "device.my_device.automate"
}
}
Devices with lower priority numbers are powered first.
Logging
Device Logs
Each device writes to its configured log file in JSON format:
{
"timestamp": "2026-01-13 10:30:00",
"status": "on",
"power": 2500,
"energy": 123.45
}
Command Log
All control commands are logged to automation.commands.json:
{
"timestamp": "2026-01-13 10:30:00",
"device_id": "shelly_heater",
"action": "power",
"value": "On",
"success": true,
"context": {"source": "automation"}
}
Automation Log
Each automation run is logged to automation.json:
{
"timestamp": "2026-01-13 10:30:00",
"excess_power": 3500,
"battery_soc": 85,
"available_power": 3000,
"executed_actions": {...}
}
API Reference
DeviceManager
// Initialize $manager = new DeviceManager(?string $registryPath = null); // Device operations $manager->collect(string $deviceId): ?array $manager->control(string $deviceId, string $action, $value = null): ?array $manager->automate(string $deviceId, int $excessPower): ?array // Batch operations $manager->collectAllDevices(bool $isCli = false): array $manager->runAutomations(?array $config = null): array // Configuration $manager->getDevice(string $deviceId): ?BaseDevice $manager->getConfig(string $deviceId): ?array $manager->listDeviceIds(bool $onlyEnabled = true): array $manager->setLocale(string $locale): void // Automation state $manager->setAutomationOverride(string $deviceId, bool $enabled): bool $manager->getAutomationOverrideStates(?string $deviceId = null) // Logging $manager->getLatestLogEntry(string $deviceId): ?array $manager->writeLog(string $deviceId, array $data): bool
BaseDevice
All device classes extend BaseDevice:
$device->collect(): ?array $device->control(string $action, $value = null): array $device->formatForLog(array $data): array $device->getCapabilities(): array $device->getDashboardControls(array $deviceData = []): array $device->getFieldLabel(string $fieldName): string
Adding Custom Devices
- Create a new directory:
src/devices/mydevice/ - Create the device class:
<?php namespace Devicemanager\devices; use Devicemanager\BaseDevice; class MyDeviceDevice extends BaseDevice { public function collect(): ?array { $url = "http://{$this->config['host']}/status"; $response = $this->httpRequest($url); if (!$response) { $this->setLastError('Connection failed'); return null; } return json_decode($response, true); } public function control(string $action, $value = null): array { // Implement control logic return $this->createSuccessResult(); } }
- Add language files (optional):
src/devices/mydevice/lang/en.php
The device will be auto-discovered and available as type my_device.
Dashboard Integration
The Device Manager integrates with the main dashboard via modules in dashcore/:
| Module | Purpose |
|---|---|
DeviceDashboardModule |
Device status and control UI |
DeviceSetupModule |
Device configuration interface |
DevicePermissionsModule |
Per-device access control |
Permissions
Device permissions are stored in config/permissions.json:
{
"devices": {
"wallbox": {
"view": true,
"automate": true,
"control": ["enable", "disable", "set_current"]
}
}
}
Or inject a permission checker from the parent application:
$manager->setPermissionChecker(function(string $deviceId, string $action): bool { return checkUserPermission("device.{$deviceId}.{$action}"); });
License
MIT License - See LICENSE file for details.
Troubleshooting
Device Not Responding
- Check network connectivity:
ping <device_ip> - Verify device is enabled in registry
- Check logs for error messages
- Increase timeout in device config
Automation Not Working
- Verify
automate: trueorautomation.enabled: truein device config - Check that solax_main device is collecting power data
- Review automation.json for decision logs
- Ensure battery threshold conditions are met
Permission Denied
- Check
config/permissions.jsonsettings - If using external checker, verify callback logic
- Review user's device permissions in parent application