diff --git a/internal/provider/acctest/resource_setting_lcm_test.go b/internal/provider/acctest/resource_setting_lcm_test.go new file mode 100644 index 0000000..c21be4b --- /dev/null +++ b/internal/provider/acctest/resource_setting_lcm_test.go @@ -0,0 +1,120 @@ +package acctest + +import ( + "fmt" + pt "github.com/filipowm/terraform-provider-unifi/internal/provider/testing" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "regexp" + "sync" + "testing" +) + +var settingLcmLock = &sync.Mutex{} + +func TestAccSettingLcm(t *testing.T) { + AcceptanceTest(t, AcceptanceTestCase{ + Lock: settingLcmLock, + Steps: []resource.TestStep{ + { + // Test creating with LCM enabled and all optional fields set + Config: testAccSettingLcmConfig(true, 75, 300, true, true), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("unifi_setting_lcd_monitor.test", "id"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "site", "default"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "enabled", "true"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "brightness", "75"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "idle_timeout", "300"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "sync", "true"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "touch_event", "true"), + ), + ConfigPlanChecks: pt.CheckResourceActions("unifi_setting_lcd_monitor.test", plancheck.ResourceActionCreate), + }, + pt.ImportStepWithSite("unifi_setting_lcd_monitor.test"), + { + // Test updating with different values + Config: testAccSettingLcmConfig(true, 50, 600, false, true), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("unifi_setting_lcd_monitor.test", "id"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "site", "default"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "enabled", "true"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "brightness", "50"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "idle_timeout", "600"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "sync", "false"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "touch_event", "true"), + ), + ConfigPlanChecks: pt.CheckResourceActions("unifi_setting_lcd_monitor.test", plancheck.ResourceActionUpdate), + }, + { + // Test disabling LCM (all optional fields should be removed) + Config: testAccSettingLcmConfigDisabled(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("unifi_setting_lcd_monitor.test", "id"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "site", "default"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "enabled", "false"), + resource.TestCheckNoResourceAttr("unifi_setting_lcd_monitor.test", "brightness"), + resource.TestCheckNoResourceAttr("unifi_setting_lcd_monitor.test", "idle_timeout"), + resource.TestCheckNoResourceAttr("unifi_setting_lcd_monitor.test", "sync"), + resource.TestCheckNoResourceAttr("unifi_setting_lcd_monitor.test", "touch_event"), + ), + ConfigPlanChecks: pt.CheckResourceActions("unifi_setting_lcd_monitor.test", plancheck.ResourceActionUpdate), + }, + { + // Test re-enabling LCM with different values + Config: testAccSettingLcmConfig(true, 100, 3600, true, false), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("unifi_setting_lcd_monitor.test", "id"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "site", "default"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "enabled", "true"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "brightness", "100"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "idle_timeout", "3600"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "sync", "true"), + resource.TestCheckResourceAttr("unifi_setting_lcd_monitor.test", "touch_event", "false"), + ), + ConfigPlanChecks: pt.CheckResourceActions("unifi_setting_lcd_monitor.test", plancheck.ResourceActionUpdate), + }, + }, + }) +} + +// Test that validation errors are raised when trying to set fields with LCM disabled +func TestAccSettingLcmValidation(t *testing.T) { + AcceptanceTest(t, AcceptanceTestCase{ + Lock: settingLcmLock, + Steps: []resource.TestStep{ + { + Config: testAccSettingLcmConfigInvalid(), + ExpectError: regexp.MustCompile(`any of those attributes must not be configured`), + }, + }, + }) +} + +func testAccSettingLcmConfig(enabled bool, brightness, idleTimeout int, sync, touchEvent bool) string { + return fmt.Sprintf(` +resource "unifi_setting_lcd_monitor" "test" { + enabled = %t + brightness = %d + idle_timeout = %d + sync = %t + touch_event = %t +} +`, enabled, brightness, idleTimeout, sync, touchEvent) +} + +func testAccSettingLcmConfigDisabled() string { + return ` +resource "unifi_setting_lcd_monitor" "test" { + enabled = false +} +` +} + +func testAccSettingLcmConfigInvalid() string { + return ` +resource "unifi_setting_lcd_monitor" "test" { + enabled = false + brightness = 50 +} +` +} diff --git a/internal/provider/provider_v2.go b/internal/provider/provider_v2.go index 1ceaa7f..efcd4e3 100644 --- a/internal/provider/provider_v2.go +++ b/internal/provider/provider_v2.go @@ -176,10 +176,14 @@ func (p *unifiProvider) Resources(_ context.Context) []func() resource.Resource dns.NewDnsRecordResource, settings.NewAutoSpeedtestResource, settings.NewCountryResource, + //settings.NewDpiResource, + //settings.NewIpsResource, + settings.NewLcmResource, settings.NewLocaleResource, settings.NewMagicSiteToSiteVpnResource, settings.NewNetworkOptimizationResource, settings.NewNtpResource, + //settings.NewRsyslogdResource, settings.NewSslInspectionResource, settings.NewTeleportResource, settings.NewUsgResource, diff --git a/internal/provider/settings/resource_setting_lcm.go b/internal/provider/settings/resource_setting_lcm.go new file mode 100644 index 0000000..0db0f9b --- /dev/null +++ b/internal/provider/settings/resource_setting_lcm.go @@ -0,0 +1,150 @@ +package settings + +import ( + "context" + + "github.com/filipowm/terraform-provider-unifi/internal/provider/validators" + + "github.com/filipowm/go-unifi/unifi" + "github.com/filipowm/terraform-provider-unifi/internal/provider/base" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type lcmModel struct { + base.Model + Enabled types.Bool `tfsdk:"enabled"` + Brightness types.Int64 `tfsdk:"brightness"` + IdleTimeout types.Int64 `tfsdk:"idle_timeout"` + Sync types.Bool `tfsdk:"sync"` + TouchEvent types.Bool `tfsdk:"touch_event"` +} + +func (d *lcmModel) AsUnifiModel(_ context.Context) (interface{}, diag.Diagnostics) { + diags := diag.Diagnostics{} + + model := &unifi.SettingLcm{ + ID: d.ID.ValueString(), + Enabled: d.Enabled.ValueBool(), + } + + // Only set optional fields if LCM is enabled + if d.Enabled.ValueBool() { + if !d.Brightness.IsNull() { + model.Brightness = int(d.Brightness.ValueInt64()) + } + if !d.IdleTimeout.IsNull() { + model.IDleTimeout = int(d.IdleTimeout.ValueInt64()) + } + if !d.Sync.IsNull() { + model.Sync = d.Sync.ValueBool() + } + if !d.TouchEvent.IsNull() { + model.TouchEvent = d.TouchEvent.ValueBool() + } + } + + return model, diags +} + +func (d *lcmModel) Merge(_ context.Context, other interface{}) diag.Diagnostics { + diags := diag.Diagnostics{} + + model, ok := other.(*unifi.SettingLcm) + if !ok { + diags.AddError("Cannot merge", "Cannot merge type that is not *unifi.SettingLcm") + return diags + } + + d.ID = types.StringValue(model.ID) + d.Enabled = types.BoolValue(model.Enabled) + + // Only set optional fields if LCM is enabled + if model.Enabled { + d.Brightness = types.Int64Value(int64(model.Brightness)) + d.IdleTimeout = types.Int64Value(int64(model.IDleTimeout)) + d.Sync = types.BoolValue(model.Sync) + d.TouchEvent = types.BoolValue(model.TouchEvent) + } else { + d.Brightness = types.Int64Null() + d.IdleTimeout = types.Int64Null() + d.Sync = types.BoolNull() + d.TouchEvent = types.BoolNull() + } + + return diags +} + +var ( + _ base.ResourceModel = &lcmModel{} + _ resource.Resource = &lcmResource{} + _ resource.ResourceWithConfigure = &lcmResource{} + _ resource.ResourceWithImportState = &lcmResource{} + _ resource.ResourceWithConfigValidators = &lcmResource{} +) + +type lcmResource struct { + *BaseSettingResource[*lcmModel] +} + +func (r *lcmResource) ConfigValidators(_ context.Context) []resource.ConfigValidator { + return []resource.ConfigValidator{ + validators.RequiredNoneIf(path.MatchRoot("enabled"), types.BoolValue(false), path.MatchRoot("brightness"), path.MatchRoot("idle_timeout"), path.MatchRoot("sync"), path.MatchRoot("touch_event")), + } +} + +func (r *lcmResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Manages LCD Monitor (LCM) settings for UniFi devices with built-in displays, such as the UniFi Dream Machine Pro (UDM Pro) and UniFi Network Video Recorder (UNVR).", + Attributes: map[string]schema.Attribute{ + "id": base.ID(), + "site": base.SiteAttribute(), + "enabled": schema.BoolAttribute{ + MarkdownDescription: "Whether the LCD display is enabled.", + Required: true, + }, + "brightness": schema.Int64Attribute{ + MarkdownDescription: "The brightness level of the LCD display. Valid values are 1-100.", + Optional: true, + Validators: []validator.Int64{ + int64validator.Between(1, 100), + }, + }, + "idle_timeout": schema.Int64Attribute{ + MarkdownDescription: "The time in seconds after which the display turns off when idle. Valid values are 10-3600.", + Optional: true, + Validators: []validator.Int64{ + int64validator.Between(10, 3600), + }, + }, + "sync": schema.BoolAttribute{ + MarkdownDescription: "Whether to synchronize display settings across multiple devices.", + Optional: true, + }, + "touch_event": schema.BoolAttribute{ + MarkdownDescription: "Whether touch interactions with the display are enabled.", + Optional: true, + }, + }, + } +} + +func NewLcmResource() resource.Resource { + r := &lcmResource{} + r.BaseSettingResource = NewBaseSettingResource( + "unifi_setting_lcd_monitor", + func() *lcmModel { return &lcmModel{} }, + func(ctx context.Context, client *base.Client, site string) (interface{}, error) { + return client.GetSettingLcm(ctx, site) + }, + func(ctx context.Context, client *base.Client, site string, body interface{}) (interface{}, error) { + return client.UpdateSettingLcm(ctx, site, body.(*unifi.SettingLcm)) + }, + ) + return r +}