diff --git a/src/main/angular/src/app/api/Area/Area.ts b/src/main/angular/src/app/api/Area/Area.ts new file mode 100644 index 0000000..6577d6f --- /dev/null +++ b/src/main/angular/src/app/api/Area/Area.ts @@ -0,0 +1,25 @@ +import {validateString} from "../common/validators"; + +export class Area { + + constructor( + readonly uuid: string, + readonly slug: string, + readonly name: string, + ) { + // + } + + static fromJson(json: any): Area { + return new Area( + validateString(json.uuid), + validateString(json.slug), + validateString(json.name), + ); + } + + static compareByName(a: Area, b: Area): number { + return a.name.localeCompare(b.name); + } + +} diff --git a/src/main/angular/src/app/api/Device/Device.ts b/src/main/angular/src/app/api/Device/Device.ts index 1defc1a..63bd259 100644 --- a/src/main/angular/src/app/api/Device/Device.ts +++ b/src/main/angular/src/app/api/Device/Device.ts @@ -1,9 +1,11 @@ import {Property} from "../Property/Property"; import {orNull, validateString} from "../common/validators"; +import {Area} from '../Area/Area'; export class Device { constructor( + readonly area: Area, readonly uuid: string, readonly name: string, readonly slug: string, @@ -15,6 +17,7 @@ export class Device { static fromJson(json: any): Device { return new Device( + Area.fromJson(json.area), validateString(json.uuid), validateString(json.name), validateString(json.slug), @@ -23,6 +26,20 @@ export class Device { ); } + get nameOrArea(): string { + if (this.name === '') { + return this.area.name; + } + return this.name; + } + + get nameWithArea(): string { + if (this.name === '') { + return this.area.name; + } + return this.area.name + ' ' + this.name; + } + static trackBy(index: number, device: Device) { return device.uuid; } @@ -31,4 +48,12 @@ export class Device { return a.uuid === b.uuid; } + static compareByAreaThenName(a: Device, b: Device): number { + const area = Area.compareByName(a.area, b.area); + if (area !== 0) { + return area; + } + return a.name.localeCompare(b.name); + } + } diff --git a/src/main/angular/src/app/api/Group/Group.ts b/src/main/angular/src/app/api/Group/Group.ts index 43a2828..eab309b 100644 --- a/src/main/angular/src/app/api/Group/Group.ts +++ b/src/main/angular/src/app/api/Group/Group.ts @@ -29,5 +29,9 @@ export class Group { return group.address; } + static compareByName(a: Group, b: Group): number { + return a.name.localeCompare(b.name); + } + } diff --git a/src/main/angular/src/app/api/Shutter/Shutter.ts b/src/main/angular/src/app/api/Shutter/Shutter.ts index 1197e4e..9a2946e 100644 --- a/src/main/angular/src/app/api/Shutter/Shutter.ts +++ b/src/main/angular/src/app/api/Shutter/Shutter.ts @@ -1,9 +1,12 @@ import {Property} from "../Property/Property"; import {orNull, validateString} from "../common/validators"; +import {Area} from '../Area/Area'; + export class Shutter { constructor( + readonly area: Area, readonly uuid: string, readonly name: string, readonly slug: string, @@ -15,6 +18,7 @@ export class Shutter { static fromJson(json: any): Shutter { return new Shutter( + Area.fromJson(json.area), validateString(json.uuid), validateString(json.name), validateString(json.slug), @@ -23,6 +27,20 @@ export class Shutter { ); } + get nameOrArea(): string { + if (this.name === '') { + return this.area.name; + } + return this.name; + } + + get nameWithArea(): string { + if (this.name === '') { + return this.area.name; + } + return this.area.name + ' ' + this.name; + } + static trackBy(index: number, shutter: Shutter) { return shutter.uuid; } @@ -31,4 +49,12 @@ export class Shutter { return a.uuid === b.uuid; } + static compareByAreaThenName(a: Shutter, b: Shutter): number { + const area = Area.compareByName(a.area, b.area); + if (area !== 0) { + return area; + } + return a.name.localeCompare(b.name); + } + } diff --git a/src/main/angular/src/app/api/Tunable/Tunable.ts b/src/main/angular/src/app/api/Tunable/Tunable.ts index 268d2cd..f5a48cd 100644 --- a/src/main/angular/src/app/api/Tunable/Tunable.ts +++ b/src/main/angular/src/app/api/Tunable/Tunable.ts @@ -1,9 +1,11 @@ import {Property} from "../Property/Property"; import {orNull, validateString} from "../common/validators"; +import {Area} from '../Area/Area'; export class Tunable { constructor( + readonly area: Area, readonly uuid: string, readonly name: string, readonly slug: string, @@ -19,6 +21,7 @@ export class Tunable { static fromJson(json: any): Tunable { return new Tunable( + Area.fromJson(json.area), validateString(json.uuid), validateString(json.name), validateString(json.slug), @@ -31,6 +34,20 @@ export class Tunable { ); } + get nameOrArea(): string { + if (this.name === '') { + return this.area.name; + } + return this.name; + } + + get nameWithArea(): string { + if (this.name === '') { + return this.area.name; + } + return this.area.name + ' ' + this.name; + } + static trackBy(index: number, tunable: Tunable) { return tunable.uuid; } @@ -39,4 +56,12 @@ export class Tunable { return a.uuid === b.uuid; } + static compareByAreaThenName(a: Tunable, b: Tunable): number { + const area = Area.compareByName(a.area, b.area); + if (area !== 0) { + return area; + } + return a.name.localeCompare(b.name); + } + } diff --git a/src/main/angular/src/app/shared/device-list/device-list.component.html b/src/main/angular/src/app/shared/device-list/device-list.component.html index ea4b1b1..d018b0f 100644 --- a/src/main/angular/src/app/shared/device-list/device-list.component.html +++ b/src/main/angular/src/app/shared/device-list/device-list.component.html @@ -1,11 +1,11 @@
-
+
- {{ device.name }} + {{ device.nameWithArea }}
diff --git a/src/main/angular/src/app/shared/device-list/device-list.component.ts b/src/main/angular/src/app/shared/device-list/device-list.component.ts index 0fe00af..2e452f7 100644 --- a/src/main/angular/src/app/shared/device-list/device-list.component.ts +++ b/src/main/angular/src/app/shared/device-list/device-list.component.ts @@ -49,4 +49,8 @@ export class DeviceListComponent implements OnInit, OnDestroy { }; } + sorted(): Device[] { + return this.list.sort(Device.compareByAreaThenName); + } + } diff --git a/src/main/angular/src/app/shared/knx-group-list/knx-group-list.component.html b/src/main/angular/src/app/shared/knx-group-list/knx-group-list.component.html index aacb0db..2bb9b8d 100644 --- a/src/main/angular/src/app/shared/knx-group-list/knx-group-list.component.html +++ b/src/main/angular/src/app/shared/knx-group-list/knx-group-list.component.html @@ -1,6 +1,6 @@
-
+
diff --git a/src/main/angular/src/app/shared/knx-group-list/knx-group-list.component.ts b/src/main/angular/src/app/shared/knx-group-list/knx-group-list.component.ts index f220798..ff5a601 100644 --- a/src/main/angular/src/app/shared/knx-group-list/knx-group-list.component.ts +++ b/src/main/angular/src/app/shared/knx-group-list/knx-group-list.component.ts @@ -42,4 +42,8 @@ export class KnxGroupListComponent implements OnInit, OnDestroy { this.subs.forEach(sub => sub.unsubscribe()); } + sorted(): Group[] { + return this.groupList.sort(Group.compareByName); + } + } diff --git a/src/main/angular/src/app/shared/shutter-list/shutter-list.component.html b/src/main/angular/src/app/shared/shutter-list/shutter-list.component.html index afca00c..211de5e 100644 --- a/src/main/angular/src/app/shared/shutter-list/shutter-list.component.html +++ b/src/main/angular/src/app/shared/shutter-list/shutter-list.component.html @@ -1,11 +1,11 @@
-
+
- {{ shutter.name }} + {{ shutter.nameWithArea }}
diff --git a/src/main/angular/src/app/shared/shutter-list/shutter-list.component.ts b/src/main/angular/src/app/shared/shutter-list/shutter-list.component.ts index a5a4bd4..29e0c40 100644 --- a/src/main/angular/src/app/shared/shutter-list/shutter-list.component.ts +++ b/src/main/angular/src/app/shared/shutter-list/shutter-list.component.ts @@ -43,4 +43,8 @@ export class ShutterListComponent implements OnInit, OnDestroy { this.subs.forEach(sub => sub.unsubscribe()); } + sorted(): Shutter[] { + return this.list.sort(Shutter.compareByAreaThenName); + } + } diff --git a/src/main/angular/src/app/shared/tunable-list/tunable-list.component.html b/src/main/angular/src/app/shared/tunable-list/tunable-list.component.html index 62cb35d..e4afa1c 100644 --- a/src/main/angular/src/app/shared/tunable-list/tunable-list.component.html +++ b/src/main/angular/src/app/shared/tunable-list/tunable-list.component.html @@ -1,11 +1,11 @@
-
+
- {{ tunable.name }} + {{ tunable.nameWithArea }}
diff --git a/src/main/angular/src/app/shared/tunable-list/tunable-list.component.ts b/src/main/angular/src/app/shared/tunable-list/tunable-list.component.ts index 28ce9e6..ad2f504 100644 --- a/src/main/angular/src/app/shared/tunable-list/tunable-list.component.ts +++ b/src/main/angular/src/app/shared/tunable-list/tunable-list.component.ts @@ -52,4 +52,8 @@ export class TunableListComponent implements OnInit, OnDestroy { }; } + sorted(): Tunable[] { + return this.list.sort(Tunable.compareByAreaThenName); + } + } diff --git a/src/main/java/de/ph87/home/area/Area.java b/src/main/java/de/ph87/home/area/Area.java new file mode 100644 index 0000000..8c397eb --- /dev/null +++ b/src/main/java/de/ph87/home/area/Area.java @@ -0,0 +1,36 @@ +package de.ph87.home.area; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; + +import java.util.UUID; + +@Entity +@Getter +@ToString +@NoArgsConstructor +public class Area { + + @Id + @NonNull + private String uuid = UUID.randomUUID().toString(); + + @NonNull + @Column(nullable = false) + private String name; + + @NonNull + @Column(nullable = false, unique = true) + private String slug; + + public Area(@NonNull final String name, @NonNull final String slug) { + this.name = name; + this.slug = slug; + } + +} diff --git a/src/main/java/de/ph87/home/area/AreaController.java b/src/main/java/de/ph87/home/area/AreaController.java new file mode 100644 index 0000000..7d2d362 --- /dev/null +++ b/src/main/java/de/ph87/home/area/AreaController.java @@ -0,0 +1,44 @@ +package de.ph87.home.area; + +import de.ph87.home.property.PropertyTypeMismatch; +import jakarta.annotation.Nullable; +import jakarta.servlet.http.HttpServletRequest; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import tuwien.auto.calimero.KNXFormatException; + +import java.util.List; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("Area") +public class AreaController { + + private final AreaService areaService; + + @NonNull + @GetMapping("getByUuid/{id}") + @ExceptionHandler(KNXFormatException.class) + private AreaDto getByUuid(@PathVariable final String id, @NonNull final HttpServletRequest request) { + log.debug("getByUuid: path={}", request.getServletPath()); + return areaService.getByUuidDto(id); + } + + @NonNull + @RequestMapping(value = "list", method = {RequestMethod.GET, RequestMethod.POST}) + private List list(@RequestBody(required = false) @Nullable final AreaFilter filter, @NonNull final HttpServletRequest request) throws PropertyTypeMismatch { + log.debug("list: path={} filter={}", request.getServletPath(), filter); + return areaService.list(filter); + } + + @NonNull + @GetMapping("get/{uuidOrSlug}") + private AreaDto get(@PathVariable @NonNull final String uuidOrSlug, @NonNull final HttpServletRequest request) { + log.debug("get: path={}", request.getServletPath()); + return areaService.getByUuidOrSlugDto(uuidOrSlug); + } + +} diff --git a/src/main/java/de/ph87/home/area/AreaDto.java b/src/main/java/de/ph87/home/area/AreaDto.java new file mode 100644 index 0000000..8d3c337 --- /dev/null +++ b/src/main/java/de/ph87/home/area/AreaDto.java @@ -0,0 +1,32 @@ +package de.ph87.home.area; + +import de.ph87.home.web.IWebSocketMessage; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +import java.util.List; + +@Getter +@ToString +public class AreaDto implements IWebSocketMessage { + + @ToString.Exclude + private final List websocketTopic = List.of("Area"); + + @NonNull + private final String uuid; + + @NonNull + private final String name; + + @NonNull + private final String slug; + + public AreaDto(@NonNull final Area area) { + this.uuid = area.getUuid(); + this.name = area.getName(); + this.slug = area.getSlug(); + } + +} diff --git a/src/main/java/de/ph87/home/area/AreaFilter.java b/src/main/java/de/ph87/home/area/AreaFilter.java new file mode 100644 index 0000000..eccc806 --- /dev/null +++ b/src/main/java/de/ph87/home/area/AreaFilter.java @@ -0,0 +1,17 @@ +package de.ph87.home.area; + +import de.ph87.home.common.crud.AbstractSearchFilter; +import de.ph87.home.property.PropertyTypeMismatch; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString +public class AreaFilter extends AbstractSearchFilter { + + public boolean filter(@NonNull final AreaDto dto) throws PropertyTypeMismatch { + return search(dto.getName()); + } + +} diff --git a/src/main/java/de/ph87/home/area/AreaRepository.java b/src/main/java/de/ph87/home/area/AreaRepository.java new file mode 100644 index 0000000..c7ad4c3 --- /dev/null +++ b/src/main/java/de/ph87/home/area/AreaRepository.java @@ -0,0 +1,12 @@ +package de.ph87.home.area; + +import lombok.NonNull; +import org.springframework.data.repository.ListCrudRepository; + +import java.util.Optional; + +public interface AreaRepository extends ListCrudRepository { + + Optional findByUuidOrSlug(@NonNull String uuid, @NonNull String slug); + +} diff --git a/src/main/java/de/ph87/home/area/AreaService.java b/src/main/java/de/ph87/home/area/AreaService.java new file mode 100644 index 0000000..bf8ac6e --- /dev/null +++ b/src/main/java/de/ph87/home/area/AreaService.java @@ -0,0 +1,81 @@ +package de.ph87.home.area; + +import de.ph87.home.common.crud.CrudAction; +import de.ph87.home.common.crud.EntityNotFound; +import de.ph87.home.property.PropertyTypeMismatch; +import jakarta.annotation.Nullable; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class AreaService { + + private final AreaRepository areaRepository; + + private final ApplicationEventPublisher applicationEventPublisher; + + @NonNull + public AreaDto create(@NonNull final String name, @NonNull final String slug) { + return publish(areaRepository.save(new Area(name, slug)), CrudAction.UPDATED); + } + + @NonNull + public AreaDto getByUuidOrSlugDto(final @NonNull String uuidOrSlug) { + return toDto(getByUuidOrSlug(uuidOrSlug)); + } + + @NonNull + private Area getByUuidOrSlug(@NonNull final String uuidOrSlug) { + return areaRepository.findByUuidOrSlug(uuidOrSlug, uuidOrSlug).orElseThrow(() -> new EntityNotFound("uuidOrSlug", uuidOrSlug)); + } + + @NonNull + public AreaDto toDto(@NonNull final Area area) { + return new AreaDto(area); + } + + @NonNull + public Area getByUuid(@NonNull final String uuid) { + return areaRepository.findById(uuid).orElseThrow(() -> new EntityNotFound("uuid", uuid)); + } + + @NonNull + public List list(@Nullable final AreaFilter filter) throws PropertyTypeMismatch { + final List all = areaRepository.findAll().stream().map(this::toDto).toList(); + if (filter == null) { + return all; + } + final List results = new ArrayList<>(); + for (final AreaDto dto : all) { + if (filter.filter(dto)) { + results.add(dto); + } + } + return results; + } + + @NonNull + @SuppressWarnings("SameParameterValue") + private AreaDto publish(@NonNull final Area area, @NonNull final CrudAction action) { + final AreaDto dto = toDto(area); + log.info("Area {}: {}", action, dto); + applicationEventPublisher.publishEvent(dto); + return dto; + } + + @NonNull + public AreaDto getByUuidDto(@NonNull final String uuid) { + return toDto(getByUuid(uuid)); + } + +} diff --git a/src/main/java/de/ph87/home/demo/DemoService.java b/src/main/java/de/ph87/home/demo/DemoService.java index e6f8ec9..7d4a54e 100644 --- a/src/main/java/de/ph87/home/demo/DemoService.java +++ b/src/main/java/de/ph87/home/demo/DemoService.java @@ -1,5 +1,7 @@ package de.ph87.home.demo; +import de.ph87.home.area.AreaDto; +import de.ph87.home.area.AreaService; import de.ph87.home.device.DeviceService; import de.ph87.home.knx.property.KnxPropertyService; import de.ph87.home.knx.property.KnxPropertyType; @@ -19,6 +21,7 @@ import tuwien.auto.calimero.GroupAddress; @Service @Transactional @RequiredArgsConstructor +@SuppressWarnings("SameParameterValue") public class DemoService { private final KnxPropertyService knxPropertyService; @@ -29,47 +32,68 @@ public class DemoService { private final TunableService tunableService; + private final AreaService areaService; + @EventListener(ApplicationStartedEvent.class) public void startup() { - device("fernseher", "Wohnzimmer Fernseher", 20, 4); - device("verstaerker", "Wohnzimmer Verstärker", 825, 824); - device("receiver", "Receiver", 2561, 2560); + final AreaDto eg = area("eg", "EG"); - tunable("wohnzimmer_haengelampe", "Wohnzimmer Hängelampe", 1794, 1799, null, null, null, null); - tunable("wohnzimmer_fensterdeko", "Wohnzimmer Fenster", 1823, 1822, null, null, null, null); - tunable("wohnzimmer_spots", "Wohnzimmer", 28, 828, 2344, 2343, 1825, 1824); - tunable("eg_ambiente", "EG Ambiente", 849, 848, null, null, null, null); - tunable("kueche_spots", "Küche", 2311, 2304, 2342, 2341, 2321, 2317); - tunable("arbeitszimmer_spots", "Arbeitszimmer", 2058, 2057, 2067, 2069, 2049, 2054); + final AreaDto wohnzimmer = area("wohnzimmer", "Wohnzimmer"); + device(wohnzimmer, "fernseher", "Fernseher", 20, 4); + device(wohnzimmer, "verstaerker", "Verstärker", 825, 824); + tunable(wohnzimmer, "haengelampe", "Hängelampe", 1794, 1799, null, null, null, null); + tunable(wohnzimmer, "fensterdeko", "Fenster", 1823, 1822, null, null, null, null); + tunable(wohnzimmer, "spots", "", 28, 828, 2344, 2343, 1825, 1824); + shutter(wohnzimmer, "links", "Links", 1048); + shutter(wohnzimmer, "rechts", "Rechts", 1811); - shutter("wohnzimmer_links", "Wohnzimmer Links", 1048); - shutter("wohnzimmer_rechts", "Wohnzimmer Rechts", 1811); - shutter("kueche_seite", "Küche Seite", 2316); - shutter("kueche_theke", "Küche Theke", 2320); - shutter("kueche_tuer", "Küche Tür", 2324); + final AreaDto kueche = area("kueche", "Küche"); + tunable(kueche, "kueche_spots", "", 2311, 2304, 2342, 2341, 2321, 2317); + shutter(kueche, "kueche_seite", "Seite", 2316); + shutter(kueche, "kueche_theke", "Theke", 2320); + shutter(kueche, "kueche_tuer", "Tür", 2324); + + tunable(eg, "eg_ambiente", "Ambiente", 849, 848, null, null, null, null); + + final AreaDto arbeitszimmer = area("arbeitszimmer", "Arbeitszimmer"); + tunable(arbeitszimmer, "spots", "", 2058, 2057, 2067, 2069, 2049, 2054); + + final AreaDto keller = area("keller", "Keller"); + device(keller, "receiver", "Receiver", 2561, 2560); + } + + @NonNull + private AreaDto area(@NonNull final String slug, @NonNull final String name) { + return areaService.create(name, slug); } private void device( - @NonNull final String slug, + @NonNull final AreaDto area, + @NonNull final String subSlug, @NonNull final String name, @Nullable final Integer stateRead, - @Nullable final Integer stateWrite - ) { - knxPropertyService.create(slug, KnxPropertyType.BOOLEAN, adr(stateRead), adr(stateWrite)); - deviceService.create(name, slug, slug); + @Nullable final Integer stateWrite) { + final String slug = area.getSlug() + "_" + subSlug; + final String statePropertyId = slug + "_state"; + knxPropertyService.create(statePropertyId, KnxPropertyType.BOOLEAN, adr(stateRead), adr(stateWrite)); + deviceService.create(area.getUuid(), name, slug, statePropertyId); } private void shutter( - @NonNull final String slug, + @NonNull final AreaDto area, + @NonNull final String subSlug, @NonNull final String name, @Nullable final Integer positionReadWrite ) { - knxPropertyService.create(slug, KnxPropertyType.DOUBLE, adr(positionReadWrite), adr(positionReadWrite)); - shutterService.create(name, slug, slug); + final String slug = area.getSlug() + "_" + subSlug; + final String statePropertyId = slug + "_state"; + knxPropertyService.create(statePropertyId, KnxPropertyType.DOUBLE, adr(positionReadWrite), adr(positionReadWrite)); + shutterService.create(area.getUuid(), name, slug, statePropertyId); } private void tunable( - @NonNull final String slug, + @NonNull final AreaDto area, + @NonNull final String subSlug, @NonNull final String name, @Nullable final Integer stateRead, @Nullable final Integer stateWrite, @@ -78,10 +102,11 @@ public class DemoService { @Nullable final Integer coldnessRead, @Nullable final Integer coldnessWrite ) { + final String slug = area.getSlug() + "_" + subSlug; final String stateProperty = knxProperty(slug + "_state", KnxPropertyType.BOOLEAN, stateRead, stateWrite); final String brightnessProperty = knxProperty(slug + "_brightness", KnxPropertyType.DOUBLE, brightnessRead, brightnessWrite); final String coldnessProperty = knxProperty(slug + "_coldness", KnxPropertyType.DOUBLE, coldnessRead, coldnessWrite); - tunableService.create(name, slug, stateProperty, brightnessProperty, coldnessProperty); + tunableService.create(area.getUuid(), name, slug, stateProperty, brightnessProperty, coldnessProperty); } @NonNull diff --git a/src/main/java/de/ph87/home/device/Device.java b/src/main/java/de/ph87/home/device/Device.java index 1c63e54..d94dec7 100644 --- a/src/main/java/de/ph87/home/device/Device.java +++ b/src/main/java/de/ph87/home/device/Device.java @@ -1,8 +1,10 @@ package de.ph87.home.device; +import de.ph87.home.area.Area; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; import lombok.*; import java.util.UUID; @@ -17,6 +19,10 @@ public class Device { @NonNull private String uuid = UUID.randomUUID().toString(); + @NonNull + @ManyToOne(optional = false) + private Area area; + @NonNull @Column(nullable = false) private String name; @@ -30,7 +36,8 @@ public class Device { @Column(nullable = false) private String statePropertyId; - public Device(@NonNull final String name, @NonNull final String slug, @NonNull final String statePropertyId) { + public Device(final Area area, @NonNull final String name, @NonNull final String slug, @NonNull final String statePropertyId) { + this.area = area; this.name = name; this.slug = slug; this.statePropertyId = statePropertyId; diff --git a/src/main/java/de/ph87/home/device/DeviceDto.java b/src/main/java/de/ph87/home/device/DeviceDto.java index 1b1c77c..2d250c2 100644 --- a/src/main/java/de/ph87/home/device/DeviceDto.java +++ b/src/main/java/de/ph87/home/device/DeviceDto.java @@ -1,5 +1,6 @@ package de.ph87.home.device; +import de.ph87.home.area.AreaDto; import de.ph87.home.property.PropertyDto; import de.ph87.home.property.PropertyTypeMismatch; import de.ph87.home.web.IWebSocketMessage; @@ -17,6 +18,9 @@ public class DeviceDto implements IWebSocketMessage { @ToString.Exclude private final List websocketTopic = List.of("Device"); + @NonNull + private final AreaDto area; + @NonNull private final String uuid; @@ -34,6 +38,7 @@ public class DeviceDto implements IWebSocketMessage { private final PropertyDto stateProperty; public DeviceDto(@NonNull final Device device, @Nullable final PropertyDto stateProperty) { + this.area = new AreaDto(device.getArea()); this.uuid = device.getUuid(); this.name = device.getName(); this.slug = device.getSlug(); diff --git a/src/main/java/de/ph87/home/device/DeviceService.java b/src/main/java/de/ph87/home/device/DeviceService.java index aef9caa..c5c04b5 100644 --- a/src/main/java/de/ph87/home/device/DeviceService.java +++ b/src/main/java/de/ph87/home/device/DeviceService.java @@ -1,5 +1,7 @@ package de.ph87.home.device; +import de.ph87.home.area.Area; +import de.ph87.home.area.AreaService; import de.ph87.home.common.crud.CrudAction; import de.ph87.home.common.crud.EntityNotFound; import de.ph87.home.property.*; @@ -27,9 +29,12 @@ public class DeviceService { private final ApplicationEventPublisher applicationEventPublisher; + private final AreaService areaService; + @NonNull - public DeviceDto create(@NonNull final String name, @NonNull final String slug, @NonNull final String stateProperty) { - return publish(deviceRepository.save(new Device(name, slug, stateProperty)), CrudAction.UPDATED); + public DeviceDto create(@NonNull final String areaUuid, @NonNull final String name, @NonNull final String slug, @NonNull final String stateProperty) { + final Area area = areaService.getByUuid(areaUuid); + return publish(deviceRepository.save(new Device(area, name, slug, stateProperty)), CrudAction.UPDATED); } public void setState(@NonNull final String uuidOrSlug, final boolean state) throws PropertyNotFound, PropertyNotWritable, PropertyTypeMismatch { diff --git a/src/main/java/de/ph87/home/shutter/Shutter.java b/src/main/java/de/ph87/home/shutter/Shutter.java index 79d82e9..b333c39 100644 --- a/src/main/java/de/ph87/home/shutter/Shutter.java +++ b/src/main/java/de/ph87/home/shutter/Shutter.java @@ -1,8 +1,10 @@ package de.ph87.home.shutter; +import de.ph87.home.area.Area; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; import lombok.*; import java.util.UUID; @@ -17,6 +19,10 @@ public class Shutter { @NonNull private String uuid = UUID.randomUUID().toString(); + @NonNull + @ManyToOne(optional = false) + private Area area; + @NonNull @Column(nullable = false) private String name; @@ -30,7 +36,8 @@ public class Shutter { @Column(nullable = false) private String positionPropertyId; - public Shutter(@NonNull final String name, @NonNull final String slug, @NonNull final String positionPropertyId) { + public Shutter(@NonNull final Area area, @NonNull final String name, @NonNull final String slug, @NonNull final String positionPropertyId) { + this.area = area; this.name = name; this.slug = slug; this.positionPropertyId = positionPropertyId; diff --git a/src/main/java/de/ph87/home/shutter/ShutterDto.java b/src/main/java/de/ph87/home/shutter/ShutterDto.java index 3d18913..b1f6539 100644 --- a/src/main/java/de/ph87/home/shutter/ShutterDto.java +++ b/src/main/java/de/ph87/home/shutter/ShutterDto.java @@ -1,5 +1,6 @@ package de.ph87.home.shutter; +import de.ph87.home.area.AreaDto; import de.ph87.home.property.PropertyDto; import de.ph87.home.property.PropertyTypeMismatch; import de.ph87.home.web.IWebSocketMessage; @@ -17,6 +18,8 @@ public class ShutterDto implements IWebSocketMessage { @ToString.Exclude private final List websocketTopic = List.of("Shutter"); + @NonNull + private final AreaDto area; @NonNull private final String uuid; @@ -34,6 +37,7 @@ public class ShutterDto implements IWebSocketMessage { private final PropertyDto positionProperty; public ShutterDto(@NonNull final Shutter shutter, @Nullable final PropertyDto positionProperty) { + this.area = new AreaDto(shutter.getArea()); this.uuid = shutter.getUuid(); this.name = shutter.getName(); this.slug = shutter.getSlug(); diff --git a/src/main/java/de/ph87/home/shutter/ShutterService.java b/src/main/java/de/ph87/home/shutter/ShutterService.java index 1b9f49e..5ac238d 100644 --- a/src/main/java/de/ph87/home/shutter/ShutterService.java +++ b/src/main/java/de/ph87/home/shutter/ShutterService.java @@ -1,5 +1,7 @@ package de.ph87.home.shutter; +import de.ph87.home.area.Area; +import de.ph87.home.area.AreaService; import de.ph87.home.common.crud.CrudAction; import de.ph87.home.common.crud.EntityNotFound; import de.ph87.home.property.*; @@ -21,6 +23,8 @@ import java.util.List; @RequiredArgsConstructor public class ShutterService { + private final AreaService areaService; + private final PropertyService propertyService; private final ShutterRepository shutterRepository; @@ -28,8 +32,9 @@ public class ShutterService { private final ApplicationEventPublisher applicationEventPublisher; @NonNull - public ShutterDto create(@NonNull final String name, @NonNull final String slug, @NonNull final String positionProperty) { - return publish(shutterRepository.save(new Shutter(name, slug, positionProperty)), CrudAction.CREATED); + public ShutterDto create(@NonNull final String areaUuid, @NonNull final String name, @NonNull final String slug, @NonNull final String positionProperty) { + final Area area = areaService.getByUuid(areaUuid); + return publish(shutterRepository.save(new Shutter(area, name, slug, positionProperty)), CrudAction.CREATED); } public void setPosition(@NonNull final String uuidOrSlug, final double position) throws PropertyNotFound, PropertyNotWritable, PropertyTypeMismatch { diff --git a/src/main/java/de/ph87/home/tunable/Tunable.java b/src/main/java/de/ph87/home/tunable/Tunable.java index b781cb2..1930e55 100644 --- a/src/main/java/de/ph87/home/tunable/Tunable.java +++ b/src/main/java/de/ph87/home/tunable/Tunable.java @@ -1,8 +1,10 @@ package de.ph87.home.tunable; +import de.ph87.home.area.Area; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; import lombok.*; import java.util.UUID; @@ -17,6 +19,10 @@ public class Tunable { @NonNull private String uuid = UUID.randomUUID().toString(); + @NonNull + @ManyToOne(optional = false) + private Area area; + @NonNull @Column(nullable = false) private String name; @@ -40,7 +46,8 @@ public class Tunable { @Column(nullable = false) private String coldnessPropertyId; - public Tunable(@NonNull final String name, @NonNull final String slug, @NonNull final String statePropertyId, @NonNull final String brightnessPropertyId, @NonNull final String coldnessPropertyId) { + public Tunable(@NonNull final Area area, @NonNull final String name, @NonNull final String slug, @NonNull final String statePropertyId, @NonNull final String brightnessPropertyId, @NonNull final String coldnessPropertyId) { + this.area = area; this.name = name; this.slug = slug; this.statePropertyId = statePropertyId; diff --git a/src/main/java/de/ph87/home/tunable/TunableDto.java b/src/main/java/de/ph87/home/tunable/TunableDto.java index 6e4c0e0..6d41659 100644 --- a/src/main/java/de/ph87/home/tunable/TunableDto.java +++ b/src/main/java/de/ph87/home/tunable/TunableDto.java @@ -1,5 +1,6 @@ package de.ph87.home.tunable; +import de.ph87.home.area.AreaDto; import de.ph87.home.property.PropertyDto; import de.ph87.home.property.PropertyTypeMismatch; import de.ph87.home.web.IWebSocketMessage; @@ -17,6 +18,9 @@ public class TunableDto implements IWebSocketMessage { @ToString.Exclude private final List websocketTopic = List.of("Tunable"); + @NonNull + private final AreaDto area; + @NonNull private final String uuid; @@ -48,6 +52,7 @@ public class TunableDto implements IWebSocketMessage { private final PropertyDto coldnessProperty; public TunableDto(@NonNull final Tunable tunable, @Nullable final PropertyDto stateProperty, @Nullable final PropertyDto brightnessProperty, @Nullable final PropertyDto coldnessProperty) { + this.area = new AreaDto(tunable.getArea()); this.uuid = tunable.getUuid(); this.name = tunable.getName(); this.slug = tunable.getSlug(); diff --git a/src/main/java/de/ph87/home/tunable/TunableService.java b/src/main/java/de/ph87/home/tunable/TunableService.java index 45bdc4f..b67ff6e 100644 --- a/src/main/java/de/ph87/home/tunable/TunableService.java +++ b/src/main/java/de/ph87/home/tunable/TunableService.java @@ -1,5 +1,7 @@ package de.ph87.home.tunable; +import de.ph87.home.area.Area; +import de.ph87.home.area.AreaService; import de.ph87.home.common.crud.CrudAction; import de.ph87.home.common.crud.EntityNotFound; import de.ph87.home.property.*; @@ -27,9 +29,12 @@ public class TunableService { private final ApplicationEventPublisher applicationEventPublisher; + private final AreaService areaService; + @NonNull - public TunableDto create(@NonNull final String name, @NonNull final String slug, @NonNull final String stateProperty, @NonNull final String brightnessProperty, @NonNull final String coldnessProperty) { - return publish(tunableRepository.save(new Tunable(name, slug, stateProperty, brightnessProperty, coldnessProperty)), CrudAction.UPDATED); + public TunableDto create(@NonNull final String areaUuid, @NonNull final String name, @NonNull final String slug, @NonNull final String stateProperty, @NonNull final String brightnessProperty, @NonNull final String coldnessProperty) { + final Area area = areaService.getByUuid(areaUuid); + return publish(tunableRepository.save(new Tunable(area, name, slug, stateProperty, brightnessProperty, coldnessProperty)), CrudAction.UPDATED); } @NonNull diff --git a/src/main/java/de/ph87/home/tvheadend/TvheadendConfig.java b/src/main/java/de/ph87/home/tvheadend/TvheadendConfig.java index fae60aa..6bea1e7 100644 --- a/src/main/java/de/ph87/home/tvheadend/TvheadendConfig.java +++ b/src/main/java/de/ph87/home/tvheadend/TvheadendConfig.java @@ -13,7 +13,7 @@ import java.time.Duration; public class TvheadendConfig { @NonNull - private String receiver = "receiver"; + private String receiver = "keller_receiver"; @NonNull private Duration BEFORE_RECORDING = Duration.ofMinutes(10);