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.*; import jakarta.annotation.Nullable; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; 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 DeviceService { private final PropertyService propertyService; private final DeviceRepository deviceRepository; private final ApplicationEventPublisher applicationEventPublisher; private final AreaService areaService; @NonNull 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 { log.debug("setState: uuidOrSlug={}, state={}", uuidOrSlug, state); final Device device = getByUuidOrSlug(uuidOrSlug); propertyService.write(device.getStatePropertyId(), state, Boolean.class); } @NonNull public DeviceDto getByUuidOrSlugDto(final @NonNull String uuidOrSlug) { return toDto(getByUuidOrSlug(uuidOrSlug)); } @NonNull private Device getByUuidOrSlug(@NonNull final String uuidOrSlug) { return deviceRepository.findByUuidOrSlug(uuidOrSlug, uuidOrSlug).orElseThrow(() -> new EntityNotFound("uuidOrSlug", uuidOrSlug)); } @NonNull public DeviceDto toDto(@NonNull final Device device) { final PropertyDto state = propertyService.dtoByIdAndTypeOrNull(device.getStatePropertyId(), Boolean.class); return new DeviceDto(device, state); } @NonNull private Device getByUuid(@NonNull final String uuid) { return deviceRepository.findById(uuid).orElseThrow(() -> new EntityNotFound("uuid", uuid)); } @NonNull public List list(@Nullable final DeviceFilter filter) throws PropertyTypeMismatch { final List all = deviceRepository.findAll().stream().map(this::toDto).toList(); if (filter == null) { return all; } final List results = new ArrayList<>(); for (final DeviceDto dto : all) { if (filter.filter(dto)) { results.add(dto); } } return results; } @EventListener(PropertyDto.class) public void onPropertyChange(@NonNull final PropertyDto dto) { deviceRepository.findAllByStatePropertyId(dto.getId()).forEach(device -> publish(device, CrudAction.CREATED)); } @NonNull private DeviceDto publish(@NonNull final Device device, @NonNull final CrudAction action) { final DeviceDto dto = toDto(device); log.info("Device {}: {}", action, dto); applicationEventPublisher.publishEvent(dto); return dto; } @NonNull public DeviceDto getByUuidDto(@NonNull final String uuid) { return toDto(getByUuid(uuid)); } }