Skip to content

Commit ed32828

Browse files
committed
Add prohibit_control option to devices
1 parent c7e4ab4 commit ed32828

6 files changed

Lines changed: 45 additions & 0 deletions

File tree

at2-web/src/components/RelayGroupControl.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export const RelayGroupControl: FC<RelayGroupControlProps> = ({
200200
<Switch
201201
id={"switch-" + entity.id}
202202
checked={entity.state === "ON"}
203+
disabled={entity.prohibit_control}
203204
onCheckedChange={(checked) =>
204205
handleSwitchClick(entity, checked)
205206
}

at2-web/src/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export interface RelayEntity {
9090
localized_name: LocalizedName | null;
9191
type: string;
9292
state: RelayState | null;
93+
prohibit_control?: boolean;
9394
}
9495

9596
/**

config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ type EntityConfig struct {
5050

5151
// How the device should be represented in the UI (light, fan, etc.)
5252
Representation string `yaml:"representation"`
53+
54+
// Prohibits controlling this device from the web page
55+
ProhibitControl bool `yaml:"prohibit_control"`
5356
}
5457

5558
type RoomConfig struct {

control_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,19 @@ func TestMQTTAdapter_ControlDevice_Validation(t *testing.T) {
172172
} else if !strings.Contains(err.Error(), "not a relay") {
173173
t.Errorf("Expected 'not a relay' error, got: %v", err)
174174
}
175+
// Test 6: Prohibited Control -> Error
176+
prohibitedDev := &VirtualDevice{
177+
ID: "relay/prohibited",
178+
Type: VdevTypeRelay,
179+
MapperData: &Zigbee2MQTTMapperData{BaseTopic: "p1"},
180+
ProhibitControl: true,
181+
}
182+
mgr.AddDevices([]*VirtualDevice{prohibitedDev})
183+
184+
err = adapter.ControlDevice("relay/prohibited", "ON")
185+
if err == nil {
186+
t.Errorf("Expected error for prohibited device, got nil")
187+
} else if !strings.Contains(err.Error(), "prohibited") {
188+
t.Errorf("Expected 'prohibited' error, got: %v", err)
189+
}
175190
}

mqtt_adapter.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ type MQTTAdapter struct {
4141
// Virtual device manager extracted from previous in-struct logic.
4242
vdevMgr *VdevManager
4343
mappers []MQTTMapper
44+
45+
// deviceSettings maps device ID to its configuration (for prohibited control etc)
46+
deviceSettings map[string]EntityConfig
4447
}
4548

4649
// atomicBool (simple mutex-backed boolean) avoids importing sync/atomic for minimal usage.
@@ -65,8 +68,17 @@ func NewMQTTAdapter(cfg *Config, vdevMgr *VdevManager) (*MQTTAdapter, error) {
6568

6669
a := &MQTTAdapter{
6770

71+
6872
config: cfg,
6973
vdevMgr: vdevMgr,
74+
deviceSettings: make(map[string]EntityConfig),
75+
}
76+
77+
// Index device configurations for fast lookup
78+
for _, room := range cfg.Rooms {
79+
for _, entity := range room.Entities {
80+
a.deviceSettings[entity.ID] = entity
81+
}
7082
}
7183

7284
// Build client options first.
@@ -167,6 +179,12 @@ func (a *MQTTAdapter) handleMapperMessage(mapper MQTTMapper, topic string, paylo
167179
log.Printf("[mqtt] discovery error on topic %s: %v", topic, derr)
168180
}
169181
if len(discovered) > 0 {
182+
// Enrich discovered devices with config data
183+
for _, d := range discovered {
184+
if cfg, ok := a.deviceSettings[d.ID]; ok {
185+
d.ProhibitControl = cfg.ProhibitControl
186+
}
187+
}
170188
a.vdevMgr.AddDevices(discovered)
171189
}
172190

@@ -220,6 +238,11 @@ func (a *MQTTAdapter) ControlDevice(deviceID string, state any) error {
220238
return fmt.Errorf("device %s is not a relay (type: %s)", deviceID, targetDev.Type)
221239
}
222240

241+
// 2.5 Validation: ProhibitControl
242+
if targetDev.ProhibitControl {
243+
return fmt.Errorf("control is prohibited for device %s", deviceID)
244+
}
245+
223246
// 3. Validation: State must be ON or OFF
224247
stateStr, ok := state.(string)
225248
if !ok {

vdev_manager.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ type VirtualDevice struct {
3030
MapperData any `json:"mapper_data"`
3131
// Fresh indicates if the state is from a live update (true) or restored/initial (false).
3232
Fresh bool `json:"fresh"`
33+
// ProhibitControl indicates if this device cannot be controlled.
34+
ProhibitControl bool `json:"prohibit_control"`
3335
}
3436

3537
// DeviceStateProvider defines the interface for retrieving persisted device state.

0 commit comments

Comments
 (0)