Skip to content

Latest commit

 

History

History
213 lines (157 loc) · 6.37 KB

File metadata and controls

213 lines (157 loc) · 6.37 KB

NextGenPluginDemo

A complete demo showing how to:

  • Control TP-Link/Kasa smart bulbs and plugs via an Azure Functions app (.NET 8 isolated)
  • Share typed models between services and plugins
  • Call the Functions from Dataverse plugins (net462) with managed identity, environment variables, and HTTP helpers
  • Optionally run a local Node/TypeScript REST service that talks to devices on your LAN

This repo contains three main projects:

  • Functions: Functions/NextGenDemo.Functions.csproj
  • Plugins (Dataverse): Plugins/NextGenDemo.Plugins.csproj
  • Shared models: Shared/NextGenDemo.Shared.csproj
  • Optional local device API: KasaLightAPI/ (Node 18+)

Quick start

Prerequisites

  • Windows with PowerShell (recommended), or any OS with a terminal
  • .NET 8 SDK
  • Node.js 18+ (only if you plan to run the local Kasa API)
  • Azure Functions Core Tools (optional if you prefer func start)

Clone and restore

git clone https://github.com/mikefactorial/NextGenPluginDemo.git
cd NextGenPluginDemo
dotnet restore

Run the local Kasa device API (optional but recommended)

The Functions app calls a REST API that talks to your bulbs/plugs on the LAN. You can run the included Node service or point to an existing endpoint (e.g., ngrok).

  1. Configure KasaLightAPI
  • Copy KasaLightAPI/.env.example to KasaLightAPI/.env
  • Set API_KEY to a secret value (match this in the Functions settings)
  1. Install and start
cd KasaLightAPI
npm i
npm run dev
# Service starts on http://localhost:3000

Key endpoints (see KasaLightAPI/README.md for full list):

  • GET /api/health
  • GET /api/devices
  • GET /api/test/:ip/on|off|red|green|blue|yellow|purple|white
  • POST /api/bulb/:ip/hex body: { hex: "#FF0000" }
  • POST /api/bulb/:ip/color body: { hue, saturation, brightness? }
  • POST /api/bulb/:ip/temperature body: { kelvin }
  • POST /api/bulb/:ip/alias body: { alias }

Configure and run the Azure Functions app

Configuration lives in Functions/local.settings.json and is read as flat keys in Functions/Program.cs:

  • BulbApiBaseUrl — e.g., http://localhost:3000/api (for the local Node API) or your ngrok URL
  • BulbApiKey — must match the API_KEY used by the device API if authentication is enabled

Example (Functions/local.settings.json):

{
	"IsEncrypted": false,
	"Values": {
		"AzureWebJobsStorage": "UseDevelopmentStorage=true",
		"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
		"BulbApiBaseUrl": "http://localhost:3000/api",
		"BulbApiKey": "your-local-api-key"
	}
}

Start the Functions app

# Default launch profile runs on port 7210 (see Functions/Properties/launchSettings.json)
dotnet run --project Functions/NextGenDemo.Functions.csproj -- --port 7210

# Base URL: http://localhost:7210/api

Main files

  • Functions entry: Functions/Program.cs
  • HTTP functions: Functions/BulbFunctions.cs
  • Service + settings: Functions/Services/BulbControlService.cs, Functions/Services/BulbApiSettings.cs
  • Models: Shared/Models/BulbControlRequest.cs, Shared/Models/DeviceInfo.cs

HTTP endpoints (Functions)

Implemented in Functions/BulbFunctions.cs.

  1. POST /api/BulbQuickAction
  • Body: { "bulbIP": "<ip>", "action": "on|off|red|green|blue|yellow|purple|white" }
  1. POST /api/ControlBulb
  • Body uses strongly-typed models with enum strings for pattern and transition; examples in Functions/README_API_Examples.json.
  • Minimal example:
{
	"bulbIP": "192.168.1.50",
	"colors": [
		{ "hex": "#FF0000", "durationMs": 1000 },
		{ "hex": "#0000FF", "durationMs": 1000 }
	],
	"pattern": { "type": "Sequential", "repeatCount": 1, "transition": "Fade", "transitionDurationMs": 500 }
}
  1. GET /api/ListDevices
  • Returns array of devices discovered by the backend API
  1. POST /api/devices/{deviceIp}/alias
  • Body: { "alias": "Living Room Light" }

More payloads and curl examples: Functions/README_API_Examples.json

Local test scripts (PowerShell)

Run from the Functions folder while the app is running:

cd Functions
./test-bulb-functions.ps1
./test-device-functions.ps1

Troubleshooting tips and known fixes: Functions/TROUBLESHOOTING.md Device management notes: Functions/DEVICE_MANAGEMENT.md

Dataverse Plugins (net462)

Core classes

  • Base: Plugins/PluginBase.cs
  • Example plugin that calls the Functions: Plugins/BulbApi.cs
  • Environment variables helper: Plugins/Types/EnvironmentVariableService.cs
  • HTTP and identity helpers: Plugins/Integration/HttpClientWrapper.cs, Plugins/Integration/JwtTokenProvider.cs, Plugins/Integration/AzureVariables.cs

Environment variable schema names used by the plugins (Dataverse):

  • mf_devicelisturl — Device list Function URL
  • mf_bulbquickactionurl — Quick action Function URL
  • mf_bulbcontrolurl — Control Function URL
  • mf_bulbip — Optional default device IP (if relevant)
  • mf_azurefunctionauthscope — Azure AD scope for acquiring tokens
  • Storage (optional): mf_storagecontainername, mf_storageendpoint

Build, pack, and sign

# Build
dotnet build Plugins/NextGenDemo.Plugins.csproj

# Pack a NuGet (uses Microsoft.PowerApps.MSBuild.Plugin)
dotnet pack Plugins/NextGenDemo.Plugins.csproj -c Release

# Note: The csproj defines a signing step that runs in Visual Studio after Pack
# (see PostBuild target). You can sign manually with: dotnet nuget sign ...

See Plugins/NextGenDemo.Plugins.csproj for package metadata and the PostBuild signing target.

CI

GitHub Actions builds and tests on push/PR to master: .github/workflows/dotnet.yml.

Project structure (high level)

Functions/
	Program.cs
	BulbFunctions.cs
	Services/
		BulbControlService.cs
		BulbApiSettings.cs
	README_API_Examples.json
	DEVICE_MANAGEMENT.md
	TROUBLESHOOTING.md
Plugins/
	PluginBase.cs
	BulbApi.cs
	Integration/
	Types/
Shared/
	Models/
		BulbControlRequest.cs
		DeviceInfo.cs
KasaLightAPI/
	README.md
	package.json

Notes

  • Enum values in JSON must be strings (e.g., "Fade", "Sequential") — see Functions/TROUBLESHOOTING.md
  • Default Functions dev port is 7210 via Functions/Properties/launchSettings.json
  • Keep secrets out of source control; use local.settings.json and .env
  • The local API expects the X-API-Key header if API_KEY is set; the Functions client sends this header when BulbApiKey is configured

License

This repository is provided for demo purposes. Add your preferred license if needed.