├── .gitignore
├── .travis.yml
├── Dockerfile
├── src
└── main
│ ├── resources
│ └── OH-INF
│ │ ├── i18n
│ │ └── synologysurveillancestation_de.properties
│ │ ├── addon
│ │ └── addon.xml
│ │ └── thing
│ │ ├── bridge.xml
│ │ └── camera.xml
│ ├── history
│ └── dependencies.xml
│ ├── feature
│ └── feature.xml
│ └── java
│ └── org
│ └── openhab
│ └── binding
│ └── synologysurveillancestation
│ ├── internal
│ ├── webapi
│ │ ├── response
│ │ │ ├── SimpleResponse.java
│ │ │ ├── InfoResponse.java
│ │ │ ├── HomeModeResponse.java
│ │ │ ├── AuthResponse.java
│ │ │ ├── LiveUriResponse.java
│ │ │ ├── CameraEventResponseObject.java
│ │ │ ├── CameraEventResponse.java
│ │ │ ├── EventResponse.java
│ │ │ ├── SynoApiResponse.java
│ │ │ └── CameraResponse.java
│ │ ├── error
│ │ │ ├── ErrorCode.java
│ │ │ └── WebApiAuthErrorCodes.java
│ │ ├── SynoWebApi.java
│ │ ├── request
│ │ │ ├── SynoApiConfig.java
│ │ │ ├── SynoApiLiveUri.java
│ │ │ ├── SynoApiExternalEvent.java
│ │ │ ├── SynoApiInfo.java
│ │ │ ├── SynoApiEvent.java
│ │ │ ├── SynoApiHomeMode.java
│ │ │ ├── SynoApiExternalRecording.java
│ │ │ ├── SynoApi.java
│ │ │ ├── SynoApiAuth.java
│ │ │ ├── SynoApiCameraEvent.java
│ │ │ ├── SynoApiRequest.java
│ │ │ ├── SynoApiCamera.java
│ │ │ └── SynoApiPTZ.java
│ │ ├── WebApiException.java
│ │ ├── SynoEvent.java
│ │ └── SynoWebApiHandler.java
│ ├── thread
│ │ ├── SynoApiThreadHomeMode.java
│ │ ├── SynoApiThreadCameraEvent.java
│ │ ├── SynoApiThreadSnapshot.java
│ │ ├── SynoApiThreadLiveUri.java
│ │ ├── SynoApiThreadCamera.java
│ │ ├── SynoApiThreadEvent.java
│ │ └── SynoApiThread.java
│ ├── discovery
│ │ ├── SynoDynamicStateDescriptionProvider.java
│ │ ├── BridgeMdnsDiscoveryService.java
│ │ └── CameraDiscoveryService.java
│ ├── SynoCameraConfig.java
│ ├── SynoConfig.java
│ └── SynoHandlerFactory.java
│ ├── handler
│ ├── SynoHandler.java
│ └── SynoBridgeHandler.java
│ └── SynoBindingConstants.java
├── docker-compose.yml
├── NOTICE
├── pom.xml
├── .project
├── .classpath
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | services:
4 | - docker
5 |
6 | script:
7 | - docker-compose run build-plugin
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8
2 |
3 | RUN apt-get update \
4 | && apt-get install -y maven \
5 | && apt-get clean
--------------------------------------------------------------------------------
/src/main/resources/OH-INF/i18n/synologysurveillancestation_de.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nibi79/synologysurveillancestation/HEAD/src/main/resources/OH-INF/i18n/synologysurveillancestation_de.properties
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.2"
2 |
3 | services:
4 | build-plugin:
5 | image: plugin-sdk
6 | build:
7 | dockerfile: Dockerfile
8 | context: .
9 | volumes:
10 | - .:${PWD}
11 | - ${M2_HOME:-~/.m2}:/root/.m2
12 | working_dir: ${PWD}
13 | command: mvn clean install
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | This content is produced and maintained by the openHAB project contributors.
2 |
3 | * Project home: https://www.openhab.org
4 |
5 | == Declared Project Licenses
6 |
7 | This program and the accompanying materials are made available under the terms
8 | of the Eclipse Public License 2.0 which is available at
9 | https://www.eclipse.org/legal/epl-2.0/.
10 |
11 | == Source Code
12 |
13 | https://github.com/nibi79/synologysurveillancestation
14 |
--------------------------------------------------------------------------------
/src/main/resources/OH-INF/addon/addon.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | binding
7 | SynologySurveillanceStation Binding
8 | This is the binding for SynologySurveillanceStation.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/history/dependencies.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | openhab-runtime-base
5 | wrap
6 | mvn:org.openhab.addons.bundles/org.openhab.binding.synologysurveillancestation/4.3.0-SNAPSHOT
7 | wrap:mvn:org.lastnpe.eea/eea-all/2.4.0
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/main/feature/feature.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
4 |
5 |
6 | openhab-runtime-base
7 | mvn:org.openhab.addons.bundles/org.openhab.binding.synologysurveillancestation/${project.version}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | 4.0.0
6 |
7 |
8 | org.openhab.addons.bundles
9 | org.openhab.addons.reactor.bundles
10 | 4.3.0-SNAPSHOT
11 |
12 |
13 | org.openhab.binding.synologysurveillancestation
14 |
15 | openHAB Add-ons :: Bundles :: Synology Surveillance Station Binding
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/SimpleResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | /**
18 | * {@link SimpleResponse} is a simplest implementation of an API response
19 | *
20 | * @author Nils - Initial contribution
21 | */
22 | @NonNullByDefault
23 | public class SimpleResponse extends SynoApiResponse {
24 |
25 | /**
26 | * @param jsonResponse
27 | */
28 | public SimpleResponse(String jsonResponse) {
29 | super(jsonResponse);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | org.openhab.binding.synologysurveillancestation
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.pde.ManifestBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.pde.SchemaBuilder
20 |
21 |
22 |
23 |
24 | org.eclipse.pde.ds.core.builder
25 |
26 |
27 |
28 |
29 | org.eclipse.m2e.core.maven2Builder
30 |
31 |
32 |
33 |
34 |
35 | org.eclipse.m2e.core.maven2Nature
36 | org.eclipse.jdt.core.javanature
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/InfoResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | import com.google.gson.JsonElement;
18 |
19 | /**
20 | * {@link InfoResponse} provides information about current camera setup
21 | *
22 | * @author Nils - Initial contribution
23 | * @author Pavion - Contribution
24 | */
25 | @NonNullByDefault
26 | public class InfoResponse extends SimpleResponse {
27 |
28 | /**
29 | * @param jsonResponse
30 | */
31 | public InfoResponse(String jsonResponse) {
32 | super(jsonResponse);
33 | }
34 |
35 | public JsonElement getCameras() {
36 | return getData().get("cameras");
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/HomeModeResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | /**
18 | * {@link HomeModeResponse} provides response for Home Mode
19 | *
20 | * @author Pavion - Initial contribution
21 | */
22 | @NonNullByDefault
23 | public class HomeModeResponse extends SimpleResponse {
24 | /**
25 | * @param jsonResponse
26 | */
27 | public HomeModeResponse(String jsonResponse) {
28 | super(jsonResponse);
29 | }
30 |
31 | /**
32 | * @return
33 | */
34 | public boolean isHomeMode() {
35 | return getData().get("on").getAsBoolean();
36 | }
37 |
38 | public int getReason() {
39 | return getData().get("reason").getAsInt();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/AuthResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 | import org.openhab.binding.synologysurveillancestation.internal.thread.SynoApiThread;
17 |
18 | /**
19 | * {@link SynoApiThread} handles authentication response
20 | *
21 | * @author Nils - Initial contribution
22 | * @author Pavion - Contribution
23 | */
24 | @NonNullByDefault
25 | public class AuthResponse extends SimpleResponse {
26 |
27 | /**
28 | * @param jsonResponse
29 | */
30 | public AuthResponse(String jsonResponse) {
31 | super(jsonResponse);
32 | }
33 |
34 | /**
35 | * @return Session ID
36 | */
37 | public String getSid() {
38 | return getData().get("sid").getAsString();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/handler/SynoHandler.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.handler;
14 |
15 | import java.util.concurrent.ScheduledExecutorService;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.eclipse.jdt.annotation.Nullable;
19 | import org.openhab.binding.synologysurveillancestation.internal.webapi.SynoWebApiHandler;
20 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
21 |
22 | /**
23 | * The {@link SynoHandler} is a generic handler
24 | *
25 | * @author Nils - Initial contribution
26 | * @author Pavion - Contribution
27 | */
28 | @NonNullByDefault
29 | public interface SynoHandler {
30 |
31 | public ScheduledExecutorService getScheduler();
32 |
33 | public @Nullable SynoWebApiHandler getSynoWebApiHandler();
34 |
35 | public boolean reconnect(boolean forceLogout) throws WebApiException;
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/error/ErrorCode.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.error;
14 |
15 | /**
16 | * The {@link ErrorCode} is an interface for error codes
17 | *
18 | * @author Nils - Initial contribution
19 | * @author Pavion - Contribution
20 | */
21 | public interface ErrorCode {
22 |
23 | /**
24 | * @return
25 | */
26 | public int getCode();
27 |
28 | /**
29 | * @return
30 | */
31 | public String getMsg();
32 |
33 | /**
34 | *
35 | * Lookup ApiErrorCode.
36 | *
37 | * @param e
38 | * @param code
39 | * @return
40 | */
41 | static WebApiAuthErrorCodes lookup(int code) {
42 | for (WebApiAuthErrorCodes w : WebApiAuthErrorCodes.class.getEnumConstants()) {
43 | if (w.getCode() == code)
44 | return w;
45 | }
46 | return WebApiAuthErrorCodes.NO_ERROR;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/SynoWebApi.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.SimpleResponse;
17 |
18 | /**
19 | * The {@link SynoWebApi} is an interface for the Web API
20 | *
21 | * @author Nils - Initial contribution
22 | * @author Pavion - Contribution
23 | */
24 | @NonNullByDefault
25 | public interface SynoWebApi {
26 | /**
27 | * Establish connection to Surveillance Station Web API
28 | *
29 | * @return
30 | * @throws WebApiException
31 | */
32 | public boolean connect(boolean forceLogout) throws WebApiException;
33 |
34 | /**
35 | *
36 | * @return
37 | * @throws WebApiException
38 | */
39 | public SimpleResponse disconnect() throws WebApiException;
40 |
41 | // ----------------------------
42 |
43 | /**
44 | * Returns true if connected and sid != null
45 | *
46 | * @return
47 | */
48 | public boolean isConnected();
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiConfig.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | /**
18 | * API configuration parameters
19 | *
20 | * @author Nils - Initial contribution
21 | * @author Pavion - Contribution
22 | *
23 | */
24 | @NonNullByDefault
25 | public class SynoApiConfig {
26 |
27 | private final String name;
28 | private final String version;
29 | private final String scriptpath;
30 |
31 | /**
32 | * @param name
33 | * @param version
34 | * @param scriptpath
35 | */
36 | public SynoApiConfig(String name, String version, String scriptpath) {
37 | this.name = name;
38 | this.version = version;
39 | this.scriptpath = scriptpath;
40 | }
41 |
42 | /**
43 | *
44 | * @return
45 | */
46 | public final String getName() {
47 | return name;
48 | }
49 |
50 | /**
51 | * @return
52 | */
53 | public final String getVersion() {
54 | return version;
55 | }
56 |
57 | /**
58 | * @return
59 | */
60 | public final String getScriptpath() {
61 | return scriptpath;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/LiveUriResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | import com.google.gson.JsonArray;
18 | import com.google.gson.JsonElement;
19 | import com.google.gson.JsonObject;
20 |
21 | /**
22 | * {@link LiveUriResponse} is a response for live URIs
23 | *
24 | * @author Pavion - Initial contribution
25 | */
26 | @NonNullByDefault
27 | public class LiveUriResponse extends SimpleResponse {
28 |
29 | /**
30 | * @param jsonResponse
31 | */
32 | public LiveUriResponse(String jsonResponse) {
33 | super(jsonResponse);
34 | }
35 |
36 | public JsonArray getUris() {
37 | return getDataAsJsonArray();
38 | }
39 |
40 | /**
41 | * Return rtsp URI
42 | *
43 | */
44 | public String getRtsp() {
45 | for (JsonElement jUri : getUris()) {
46 | if (jUri.isJsonObject()) {
47 | JsonObject uri = jUri.getAsJsonObject();
48 | return uri.get("rtspPath").getAsString();
49 | }
50 | }
51 | return "";
52 | }
53 |
54 | /**
55 | * Return mjpeg over http URI
56 | *
57 | */
58 | public String getMjpegHttp() {
59 | for (JsonElement jUri : getUris()) {
60 | if (jUri.isJsonObject()) {
61 | JsonObject uri = jUri.getAsJsonObject();
62 | return uri.get("mjpegHttpPath").getAsString();
63 | }
64 | }
65 | return "";
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiLiveUri.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.io.IOException;
16 | import java.net.URISyntaxException;
17 | import java.util.HashMap;
18 | import java.util.Map;
19 |
20 | import org.eclipse.jdt.annotation.NonNullByDefault;
21 | import org.eclipse.jetty.client.HttpClient;
22 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
23 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
24 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.LiveUriResponse;
25 |
26 | /**
27 | * SYNO.SurveillanceStation.LiveUri
28 | *
29 | * This API provides a set of methods to acquire camera live feed URIs
30 | *
31 | *
32 | * @author Pavion - Initial contribution
33 | */
34 | @NonNullByDefault
35 | public class SynoApiLiveUri extends SynoApiRequest {
36 | // API configuration
37 | private static final String API_NAME = "SYNO.SurveillanceStation.Camera";
38 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_09, API_SCRIPT_ENTRY);
39 |
40 | /**
41 | * @param config
42 | */
43 | public SynoApiLiveUri(SynoConfig config, HttpClient httpClient) {
44 | super(API_CONFIG, config, httpClient);
45 | }
46 |
47 | /**
48 | * Get live URIs of the selected camera's live feed
49 | *
50 | * @throws WebApiException
51 | * @throws IOException
52 | * @throws UnsupportedOperationException
53 | * @throws URISyntaxException
54 | *
55 | */
56 | public LiveUriResponse getLiveUriResponse(String cameraId) throws WebApiException {
57 | Map params = new HashMap<>();
58 | params.put("idList", cameraId);
59 |
60 | return callApi(METHOD_LIVEVIEWPATH, params);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiExternalEvent.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import org.eclipse.jdt.annotation.NonNullByDefault;
19 | import org.eclipse.jetty.client.HttpClient;
20 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
22 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.CameraResponse;
23 |
24 | /**
25 | * SYNO.SurveillanceStation.ExternalEvent
26 | *
27 | * External event related WebAPI. The method “Trigger” is implemented.
28 | *
29 | * @author Pavion - Initial Contribution
30 | *
31 | */
32 | @NonNullByDefault
33 | public class SynoApiExternalEvent extends SynoApiRequest {
34 | // private final Logger logger = LoggerFactory.getLogger(SynoApiExternalEvent.class);
35 |
36 | // API configuration
37 | private static final String API_NAME = "SYNO.SurveillanceStation.ExternalEvent";
38 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_01, API_SCRIPT_ENTRY);
39 |
40 | /**
41 | * @param config
42 | */
43 | public SynoApiExternalEvent(SynoConfig config, HttpClient httpClient) {
44 | super(API_CONFIG, config, httpClient);
45 | }
46 |
47 | /**
48 | * Triggers the external event 1 to 10
49 | *
50 | * @param event External event to be triggered (1 to 10)
51 | * @return
52 | * @throws WebApiException
53 | */
54 | public boolean triggerEvent(int event) throws WebApiException {
55 | Map params = new HashMap<>();
56 |
57 | // API parameters
58 | params.put("eventId", String.valueOf(event));
59 | return callApi(METHOD_TRIGGER, params).isSuccess();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiInfo.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 | import org.eclipse.jetty.client.HttpClient;
17 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
18 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
19 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
20 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.InfoResponse;
21 |
22 | /**
23 | * SYNO.SurveillanceStation.Info
24 | *
25 | * This API provides a method to acquire Surveillance Station related information such as package version, package UI
26 | * path, and the total number of camera and installed licenses.
27 | *
28 | * Method:
29 | * - GetInfo
30 | *
31 | * @author Nils - Initial contribution
32 | * @author Pavion - Contribution
33 | */
34 | @NonNullByDefault
35 | public class SynoApiInfo extends SynoApiRequest {
36 |
37 | // API Configuration
38 | private static final String API_NAME = "SYNO.SurveillanceStation.Info";
39 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_05, API_SCRIPT_ENTRY);
40 |
41 | /**
42 | * @param config
43 | */
44 | public SynoApiInfo(SynoConfig config, HttpClient httpClient) {
45 | super(API_CONFIG, config, httpClient);
46 | }
47 |
48 | /**
49 | * Get Surveillance Station related general information.
50 | *
51 | * @return
52 | * @throws WebApiException
53 | */
54 | public InfoResponse getInfo() throws WebApiException {
55 | InfoResponse response = callApi(METHOD_GETINFO);
56 |
57 | if (!response.isSuccess()) {
58 | throw new WebApiException(WebApiAuthErrorCodes.getByCode(response.getErrorcode()));
59 | }
60 |
61 | return response;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiEvent.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import org.eclipse.jdt.annotation.NonNullByDefault;
19 | import org.eclipse.jetty.client.HttpClient;
20 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
22 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.EventResponse;
23 |
24 | /**
25 | * SYNO.SurveillanceStation.SynoApiEvent
26 | *
27 | * This API provides a method to acquire Surveillance Station event information as displayed in Timeline
28 | *
29 | * @author Pavion - Initial contribution
30 | */
31 | @NonNullByDefault
32 | public class SynoApiEvent extends SynoApiRequest {
33 |
34 | // API Configuration
35 | private static final String API_NAME = "SYNO.SurveillanceStation.Event";
36 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_05, API_SCRIPT_ENTRY);
37 |
38 | /**
39 | * @param config
40 | */
41 | public SynoApiEvent(SynoConfig config, HttpClient httpClient) {
42 | super(API_CONFIG, config, httpClient);
43 | }
44 |
45 | /**
46 | * Get API events
47 | *
48 | * @return
49 | * @throws WebApiException
50 | */
51 | public EventResponse getEventResponse(String cameraId, long lastEventTime, int reason) {
52 | Map params = new HashMap<>();
53 |
54 | params.put("cameraIds", cameraId);
55 | // params.put("fromTime", String.valueOf(lastEventTime));
56 | params.put("blIncludeSnapshot", API_FALSE);
57 | params.put("limit", "1");
58 | params.put("reason", String.valueOf(reason));
59 |
60 | try {
61 | return callApi(METHOD_LIST, params);
62 | } catch (WebApiException e) {
63 | return new EventResponse("{\"data\":{},\"success\":false}");
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/WebApiException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi;
14 |
15 | import java.util.Objects;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.ErrorCode;
19 |
20 | /**
21 | * The {@link WebApiException} is a class for handling the binding exceptions
22 | *
23 | * @author Nils - Initial contribution
24 | * @author Pavion - Contribution
25 | */
26 | @NonNullByDefault
27 | public class WebApiException extends Exception {
28 |
29 | private static final long serialVersionUID = 1L;
30 |
31 | private static final int UNKNOWN = 0;
32 |
33 | private final int errorCode;
34 | private final String errorMsg;
35 |
36 | public WebApiException(int errorCode, String errorMsg, Throwable cause) {
37 | super(errorMsg, cause);
38 | this.errorCode = errorCode;
39 | this.errorMsg = errorMsg;
40 | }
41 |
42 | public WebApiException(int errorCode, String errorMsg) {
43 | super(errorMsg);
44 | this.errorCode = errorCode;
45 | this.errorMsg = errorMsg;
46 | }
47 |
48 | public WebApiException(ErrorCode errorCode) {
49 | super();
50 | this.errorCode = errorCode.getCode();
51 | this.errorMsg = errorCode.getMsg();
52 | }
53 |
54 | public WebApiException(String errorMsg, Throwable cause) {
55 | super(errorMsg);
56 | this.errorCode = UNKNOWN;
57 | this.errorMsg = errorMsg;
58 | }
59 |
60 | public WebApiException(String errorMsg) {
61 | super(errorMsg);
62 | this.errorCode = UNKNOWN;
63 | this.errorMsg = errorMsg;
64 | }
65 |
66 | public WebApiException(Throwable cause) {
67 | super(cause.getMessage(), cause);
68 | this.errorCode = UNKNOWN;
69 | this.errorMsg = Objects.requireNonNullElse(cause.getMessage(), "");
70 | }
71 |
72 | public int getErrorCode() {
73 | return errorCode;
74 | }
75 |
76 | public String getErrorMsg() {
77 | return errorMsg;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiHomeMode.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import org.eclipse.jdt.annotation.NonNullByDefault;
19 | import org.eclipse.jetty.client.HttpClient;
20 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
22 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.HomeModeResponse;
23 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.SimpleResponse;
24 |
25 | /**
26 | * SYNO.SurveillanceStation.SynoApiHomeMode
27 | *
28 | * This API provides a method to acquire Surveillance Station Home Mode state
29 | *
30 | * @author Pavion - Initial contribution
31 | */
32 | @NonNullByDefault
33 | public class SynoApiHomeMode extends SynoApiRequest {
34 | private static final String API_NAME = "SYNO.SurveillanceStation.HomeMode";
35 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_01, API_SCRIPT_ENTRY);
36 |
37 | /**
38 | * @param config
39 | */
40 | public SynoApiHomeMode(SynoConfig config, HttpClient httpClient) {
41 | super(API_CONFIG, config, httpClient);
42 | }
43 |
44 | /**
45 | * Get API events
46 | *
47 | * @return
48 | * @throws WebApiException
49 | */
50 | public HomeModeResponse getHomeModeResponse() {
51 | try {
52 | Map params = new HashMap<>();
53 | return callApi(METHOD_GETINFO, params);
54 | } catch (WebApiException e) {
55 | return new HomeModeResponse("{\"data\":{},\"success\":false}");
56 | }
57 | }
58 |
59 | /**
60 | *
61 | * @param mode
62 | * @return
63 | * @throws WebApiException
64 | */
65 | public SimpleResponse setHomeMode(boolean mode) throws WebApiException {
66 | Map params = new HashMap<>();
67 | params.put("on", mode ? "true" : "false");
68 | return callApi(METHOD_SWITCH, params);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/thread/SynoApiThreadHomeMode.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.thread;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.CHANNEL_HOMEMODE;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.openhab.binding.synologysurveillancestation.handler.SynoBridgeHandler;
19 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
20 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.HomeModeResponse;
22 | import org.openhab.core.library.types.OnOffType;
23 | import org.openhab.core.thing.Channel;
24 |
25 | /**
26 | * Thread for getting Surveillance Station Home Mode state
27 | *
28 | * @author Pavion - Initial contribution
29 | */
30 | @NonNullByDefault
31 | public class SynoApiThreadHomeMode extends SynoApiThread {
32 | // private final Logger logger = LoggerFactory.getLogger(SynoApiThreadHomeMode.class);
33 |
34 | public SynoApiThreadHomeMode(SynoBridgeHandler handler, int refreshRate) {
35 | super(SynoApiThread.THREAD_HOMEMODE, handler, refreshRate);
36 | }
37 |
38 | @Override
39 | public boolean isNeeded() {
40 | return (getSynoHandler().isLinked(CHANNEL_HOMEMODE));
41 | }
42 |
43 | @Override
44 | public boolean refresh() throws Exception {
45 | SynoBridgeHandler bridgeHandler = getSynoHandler();
46 | HomeModeResponse response = bridgeHandler.getSynoWebApiHandler().getApiHomeMode().getHomeModeResponse();
47 | if (response.isSuccess()) {
48 | if (getSynoHandler().isLinked(CHANNEL_HOMEMODE)) {
49 | Channel channel = bridgeHandler.getThing().getChannel(CHANNEL_HOMEMODE);
50 | getSynoHandler().updateState(channel.getUID(), response.isHomeMode() ? OnOffType.ON : OnOffType.OFF);
51 | }
52 | return true;
53 | } else if (response.getErrorcode() == 119) {
54 | throw new WebApiException(WebApiAuthErrorCodes.INSUFFICIENT_USER_PRIVILEGE);
55 | } else {
56 | return false;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiExternalRecording.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import org.eclipse.jdt.annotation.NonNullByDefault;
19 | import org.eclipse.jetty.client.HttpClient;
20 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
22 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.SimpleResponse;
23 |
24 | /**
25 | * SYNO.SurveillanceStation.ExternalRecording
26 | *
27 | * This API provides methods to start or stop external recording of a camera.
28 | *
29 | * Method:
30 | * - Record
31 | *
32 | * @author Nils - Initial contribution
33 | * @author Pavion - Contribution
34 | */
35 | @NonNullByDefault
36 | public class SynoApiExternalRecording extends SynoApiRequest {
37 |
38 | // API configuration
39 | private static final String API_NAME = "SYNO.SurveillanceStation.ExternalRecording";
40 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_02, API_SCRIPT_ENTRY);
41 |
42 | /**
43 | * @param config
44 | */
45 | public SynoApiExternalRecording(SynoConfig config, HttpClient httpClient) {
46 | super(API_CONFIG, config, httpClient);
47 | }
48 |
49 | /**
50 | * @param method
51 | * @param cameraId
52 | * @param action
53 | * @return
54 | * @throws WebApiException
55 | */
56 | private SimpleResponse call(String method, String cameraId, String action) throws WebApiException {
57 | Map params = new HashMap<>();
58 |
59 | // API parameters
60 | params.put("cameraId", cameraId);
61 | params.put("action", action);
62 |
63 | return callApi(method, params);
64 | }
65 |
66 | /**
67 | * Toggle external recording of a camera.
68 | *
69 | * @param cameraId
70 | * @return
71 | * @throws WebApiException
72 | */
73 | public SimpleResponse toggleRecording(String cameraId, boolean on) throws WebApiException {
74 | return call(METHOD_RECORD, cameraId, on ? "start" : "stop");
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/SynoEvent.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | /**
18 | * {@link SynoEvent} stores events
19 | *
20 | * @author Pavion - Initial contribution
21 | */
22 | @NonNullByDefault
23 | public class SynoEvent {
24 | public static final int EVENT_REASON_CONTINUOUS = 1;
25 | public static final int EVENT_REASON_MOTION = 2;
26 | public static final int EVENT_REASON_ALARM = 3;
27 | public static final int EVENT_REASON_CUSTOM = 4;
28 | public static final int EVENT_REASON_MANUAL = 5;
29 | public static final int EVENT_REASON_EXTERNAL = 6;
30 | public static final int EVENT_REASON_ANALYTICS = 7;
31 | public static final int EVENT_REASON_EDGE = 8;
32 | public static final int EVENT_REASON_ACTIONRULE = 9;
33 |
34 | private boolean eventCompleted = true;
35 | private long eventId = -1;
36 | private final int reason;
37 |
38 | /**
39 | * Constructor for OH2 side
40 | *
41 | * @param reason
42 | */
43 | public SynoEvent(int reason) {
44 | this.reason = reason;
45 | }
46 |
47 | /**
48 | * Constructor for API side
49 | *
50 | * @param eventCompleted
51 | * @param eventId
52 | * @param reason
53 | */
54 | public SynoEvent(long eventId, boolean eventCompleted, int reason) {
55 | this.eventCompleted = eventCompleted;
56 | this.eventId = eventId;
57 | this.reason = reason;
58 | }
59 |
60 | /**
61 | * @return the eventCompleted
62 | */
63 | public boolean isEventCompleted() {
64 | return eventCompleted;
65 | }
66 |
67 | /**
68 | * @param eventCompleted the eventCompleted to set
69 | */
70 | public void setEventCompleted(boolean eventCompleted) {
71 | this.eventCompleted = eventCompleted;
72 | }
73 |
74 | /**
75 | * @return the eventId
76 | */
77 | public long getEventId() {
78 | return eventId;
79 | }
80 |
81 | /**
82 | * @param eventId the eventId to set
83 | */
84 | public void setEventId(long eventId) {
85 | this.eventId = eventId;
86 | }
87 |
88 | /**
89 | * @return the reason
90 | */
91 | public int getReason() {
92 | return reason;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/thread/SynoApiThreadCameraEvent.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.thread;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.*;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.openhab.binding.synologysurveillancestation.handler.SynoCameraHandler;
19 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.CameraEventResponse;
20 | import org.openhab.core.library.types.DecimalType;
21 | import org.openhab.core.library.types.StringType;
22 |
23 | /**
24 | * Thread for getting camera state (enabled, recording)
25 | *
26 | * @author Pavion - Initial contribution
27 | */
28 | @NonNullByDefault
29 | public class SynoApiThreadCameraEvent extends SynoApiThread {
30 |
31 | public SynoApiThreadCameraEvent(SynoCameraHandler handler, int refreshRate) {
32 | super(SynoApiThread.THREAD_CAMERAEVENT, handler, refreshRate);
33 | }
34 |
35 | @Override
36 | public boolean isNeeded() {
37 | for (String channel : CHANNEL_MDPARAM) {
38 | if (getSynoHandler().isLinked(channel)) {
39 | return true;
40 | }
41 | }
42 | return false;
43 | }
44 |
45 | @Override
46 | public boolean refresh() throws Exception {
47 | SynoCameraHandler cameraHandler = getSynoHandler();
48 | String cameraId = cameraHandler.getCameraId();
49 |
50 | CameraEventResponse response = cameraHandler.getSynoWebApiHandler().getApiCameraEvent().getMDParam(cameraId);
51 | if (!response.isSuccess()) {
52 | return false;
53 | }
54 |
55 | cameraHandler.updateState(CHANNEL_MDPARAM_SOURCE, new StringType(response.getSource()));
56 | cameraHandler.updateState(CHANNEL_MDPARAM_SENSITIVITY, new DecimalType(response.getSensitivity().getValue()));
57 | cameraHandler.updateState(CHANNEL_MDPARAM_THRESHOLD, new DecimalType(response.getThreshold().getValue()));
58 | cameraHandler.updateState(CHANNEL_MDPARAM_OBJECTSIZE, new DecimalType(response.getObjectSize().getValue()));
59 | cameraHandler.updateState(CHANNEL_MDPARAM_PERCENTAGE, new DecimalType(response.getPercentage().getValue()));
60 | cameraHandler.updateState(CHANNEL_MDPARAM_SHORTLIVE, new DecimalType(response.getShortLiveSecond().getValue()));
61 |
62 | return true;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/thread/SynoApiThreadSnapshot.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.thread;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.CHANNEL_SNAPSHOT;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.openhab.binding.synologysurveillancestation.handler.SynoCameraHandler;
19 | import org.openhab.binding.synologysurveillancestation.internal.SynoCameraConfig;
20 | import org.openhab.core.library.types.RawType;
21 | import org.openhab.core.thing.Channel;
22 | import org.openhab.core.thing.Thing;
23 | import org.openhab.core.types.UnDefType;
24 | import org.slf4j.Logger;
25 | import org.slf4j.LoggerFactory;
26 |
27 | /**
28 | * Thread for getting snapshots
29 | *
30 | * @author Pavion - Initial contribution
31 | */
32 | @NonNullByDefault
33 | public class SynoApiThreadSnapshot extends SynoApiThread {
34 | private final Logger logger = LoggerFactory.getLogger(SynoApiThreadSnapshot.class);
35 |
36 | public SynoApiThreadSnapshot(SynoCameraHandler handler, int refreshRate) {
37 | super(SynoApiThread.THREAD_SNAPSHOT, handler, refreshRate);
38 | }
39 |
40 | @Override
41 | public boolean isNeeded() {
42 | return (getSynoHandler().isLinked(CHANNEL_SNAPSHOT));
43 | }
44 |
45 | @Override
46 | public boolean refresh() throws Exception {
47 | SynoCameraHandler cameraHandler = getSynoHandler();
48 |
49 | Channel channel = cameraHandler.getThing().getChannel(CHANNEL_SNAPSHOT);
50 | Thing thing = cameraHandler.getThing();
51 | SynoCameraConfig config = thing.getConfiguration().as(SynoCameraConfig.class);
52 | byte[] snapshot = new byte[0];
53 | try {
54 | snapshot = cameraHandler.getSynoWebApiHandler().getApiCamera().getSnapshot(getSynoHandler().getCameraId(),
55 | getRefreshRate(), config.getSnapshotStreamId());
56 | } catch (Exception e) {
57 | logger.error("Unexpected exception while obtaining snapshot, possibly network disconnected", e);
58 | }
59 | if (snapshot.length < 1000) {
60 | getSynoHandler().updateState(channel.getUID(), UnDefType.UNDEF);
61 | return (snapshot.length == 2);
62 | } else {
63 | getSynoHandler().updateState(channel.getUID(), new RawType(snapshot, "image/jpeg"));
64 | return true;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/discovery/SynoDynamicStateDescriptionProvider.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.discovery;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 | import java.util.Locale;
18 | import java.util.Map;
19 | import java.util.concurrent.ConcurrentHashMap;
20 |
21 | import org.eclipse.jdt.annotation.NonNullByDefault;
22 | import org.eclipse.jdt.annotation.Nullable;
23 | import org.openhab.core.thing.Channel;
24 | import org.openhab.core.thing.ChannelUID;
25 | import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
26 | import org.openhab.core.types.StateDescription;
27 | import org.openhab.core.types.StateDescriptionFragmentBuilder;
28 | import org.openhab.core.types.StateOption;
29 | import org.osgi.service.component.ComponentContext;
30 | import org.osgi.service.component.annotations.Activate;
31 | import org.osgi.service.component.annotations.Component;
32 | import org.osgi.service.component.annotations.Deactivate;
33 |
34 | /**
35 | * Dynamic channel state description provider.
36 | * Overrides the state description for the controls, which receive its configuration in the runtime.
37 | *
38 | * @author Nils - Initial contribution
39 | * @author Pavion - Contribution
40 | */
41 | @NonNullByDefault
42 | @Component(service = { DynamicStateDescriptionProvider.class, SynoDynamicStateDescriptionProvider.class })
43 | public class SynoDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider {
44 |
45 | private final Map> channelOptionsMap = new ConcurrentHashMap<>();
46 |
47 | @Activate
48 | public void activate(ComponentContext context) {
49 | }
50 |
51 | public void setStateOptions(ChannelUID channelUID, List options) {
52 | channelOptionsMap.put(channelUID, options);
53 | }
54 |
55 | @Override
56 | public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
57 | @Nullable Locale locale) {
58 | List options = channelOptionsMap.getOrDefault(channel.getUID(), new ArrayList());
59 |
60 | StateDescriptionFragmentBuilder builder = (original == null) ? StateDescriptionFragmentBuilder.create()
61 | : StateDescriptionFragmentBuilder.create(original);
62 | return builder.withOptions(options).build().toStateDescription();
63 | }
64 |
65 | @Deactivate
66 | public void deactivate() {
67 | channelOptionsMap.clear();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApi.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | /**
18 | * The {@link SynoApi} is an interface for Synology API codes
19 | *
20 | * @author Nils - Initial contribution
21 | * @author Pavion - Contribution
22 | */
23 | @NonNullByDefault
24 | public interface SynoApi {
25 |
26 | // API configuration versions
27 | public static final String API_VERSION_01 = "1";
28 | public static final String API_VERSION_02 = "2";
29 | public static final String API_VERSION_03 = "3";
30 | public static final String API_VERSION_04 = "4";
31 | public static final String API_VERSION_05 = "5";
32 | public static final String API_VERSION_06 = "6";
33 | public static final String API_VERSION_07 = "7";
34 | public static final String API_VERSION_08 = "8";
35 | public static final String API_VERSION_09 = "9";
36 |
37 | // API configuration scripts
38 | public static final String API_SCRIPT_AUTH = "/webapi/auth.cgi";
39 | public static final String API_SCRIPT_ENTRY = "/webapi/entry.cgi";
40 | public static final String API_SCRIPT_QUERY = "/webapi/query.cgi";
41 |
42 | // API methods
43 | public static final String METHOD_LOGIN = "Login";
44 | public static final String METHOD_LOGOUT = "Logout";
45 |
46 | public static final String METHOD_LIST = "List";
47 | public static final String METHOD_GETINFO = "GetInfo";
48 | public static final String METHOD_ENABLE = "Enable";
49 | public static final String METHOD_DISABLE = "Disable";
50 | public static final String METHOD_GETSNAPSHOT = "GetSnapshot";
51 | public static final String METHOD_LIVEVIEWPATH = "GetLiveViewPath";
52 |
53 | public static final String METHOD_RECORD = "Record";
54 |
55 | public static final String METHOD_ZOOM = "Zoom";
56 | public static final String METHOD_MOVE = "Move";
57 | public static final String METHOD_LISTPRESET = "ListPreset";
58 | public static final String METHOD_GOPRESET = "GoPreset";
59 | public static final String METHOD_LISTPATROL = "ListPatrol";
60 | public static final String METHOD_RUNPATROL = "RunPatrol";
61 | public static final String METHOD_SWITCH = "Switch";
62 | public static final String METHOD_TRIGGER = "Trigger";
63 |
64 | public static final String METHOD_MOTIONENUM = "MotionEnum";
65 | public static final String METHOD_MDPARAMSAVE = "MDParamSave";
66 |
67 | public static final int CONNECTION_TIMEOUT = 5000;
68 |
69 | public SynoApiConfig getApiConfig();
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/thread/SynoApiThreadLiveUri.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.thread;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.*;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.openhab.binding.synologysurveillancestation.handler.SynoCameraHandler;
19 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
20 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.LiveUriResponse;
22 | import org.openhab.core.library.types.StringType;
23 | import org.openhab.core.thing.Channel;
24 |
25 | /**
26 | * Thread for refreshing live URIs (RTSP or MJPEG over HTTP)
27 | *
28 | * @author Pavion - Initial contribution
29 | */
30 | @NonNullByDefault
31 | public class SynoApiThreadLiveUri extends SynoApiThread {
32 | // private final Logger logger = LoggerFactory.getLogger(SynoApiThreadCamera.class);
33 |
34 | public SynoApiThreadLiveUri(SynoCameraHandler handler, int refreshRate) {
35 | super(SynoApiThread.THREAD_LIVEURI, handler, refreshRate);
36 | }
37 |
38 | @Override
39 | public boolean isNeeded() {
40 | return (getSynoHandler().isLinked(CHANNEL_LIVE_URI_RTSP)
41 | || getSynoHandler().isLinked(CHANNEL_LIVE_URI_MJPEG_HTTP));
42 | }
43 |
44 | @Override
45 | public boolean refresh() throws Exception {
46 | SynoCameraHandler cameraHandler = getSynoHandler();
47 | String cameraId = cameraHandler.getCameraId();
48 |
49 | LiveUriResponse response = cameraHandler.getSynoWebApiHandler().getApiLiveUri().getLiveUriResponse(cameraId);
50 |
51 | if (response.isSuccess()) {
52 | if (cameraHandler.isLinked(CHANNEL_LIVE_URI_RTSP)) {
53 | Channel channel = cameraHandler.getThing().getChannel(CHANNEL_LIVE_URI_RTSP);
54 | String uri = response.getRtsp();
55 | cameraHandler.updateState(channel.getUID(), new StringType(uri));
56 | }
57 |
58 | if (cameraHandler.isLinked(CHANNEL_LIVE_URI_MJPEG_HTTP)) {
59 | Channel channel = cameraHandler.getThing().getChannel(CHANNEL_LIVE_URI_MJPEG_HTTP);
60 | String uri = response.getMjpegHttp();
61 | cameraHandler.updateState(channel.getUID(), new StringType(uri));
62 | }
63 |
64 | return true;
65 | } else if (response.getErrorcode() == 105) {
66 | throw new WebApiException(WebApiAuthErrorCodes.INSUFFICIENT_USER_PRIVILEGE);
67 | }
68 |
69 | return false;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/CameraEventResponseObject.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | import com.google.gson.JsonObject;
18 |
19 | /**
20 | * {@link CameraEventResponseObject} is a JSON extension for response numeric type
21 | *
22 | * @author Pavion - Initial contribution
23 | */
24 | @NonNullByDefault
25 | public class CameraEventResponseObject {
26 |
27 | private boolean camCap = false;
28 | private boolean ssCap = false;
29 | private int value = 0;
30 | private int minValue = 0;
31 | private int maxValue = 99;
32 |
33 | /**
34 | * Constructor
35 | *
36 | * @param object
37 | */
38 | public CameraEventResponseObject(JsonObject object) {
39 | this.camCap = object.get("camCap").getAsBoolean();
40 | this.ssCap = object.get("ssCap").getAsBoolean();
41 | this.value = object.get("value").getAsInt();
42 | try {
43 | this.minValue = object.get("minValue").getAsInt();
44 | this.maxValue = object.get("maxValue").getAsInt();
45 | } catch (Exception ex) {
46 | // Keep default values
47 | }
48 | }
49 |
50 | /**
51 | * @return the camCap
52 | */
53 | public boolean isCamCap() {
54 | return camCap;
55 | }
56 |
57 | /**
58 | * @param camCap the camCap to set
59 | */
60 | public void setCamCap(boolean camCap) {
61 | this.camCap = camCap;
62 | }
63 |
64 | /**
65 | * @return the ssCap
66 | */
67 | public boolean isSsCap() {
68 | return ssCap;
69 | }
70 |
71 | /**
72 | * @param ssCap the ssCap to set
73 | */
74 | public void setSsCap(boolean ssCap) {
75 | this.ssCap = ssCap;
76 | }
77 |
78 | /**
79 | * @return the value
80 | */
81 | public int getValue() {
82 | return value;
83 | }
84 |
85 | /**
86 | * @param value the value to set
87 | */
88 | public void setValue(int value) {
89 | this.value = value;
90 | }
91 |
92 | /**
93 | * @return the minValue
94 | */
95 | public int getMinValue() {
96 | return minValue;
97 | }
98 |
99 | /**
100 | * @param minValue the minValue to set
101 | */
102 | public void setMinValue(int minValue) {
103 | this.minValue = minValue;
104 | }
105 |
106 | /**
107 | * @return the maxValue
108 | */
109 | public int getMaxValue() {
110 | return maxValue;
111 | }
112 |
113 | /**
114 | * @param maxValue the maxValue to set
115 | */
116 | public void setMaxValue(int maxValue) {
117 | this.maxValue = maxValue;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiAuth.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import org.eclipse.jdt.annotation.NonNullByDefault;
19 | import org.eclipse.jetty.client.HttpClient;
20 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
22 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.AuthResponse;
23 |
24 | /**
25 | *
26 | * SYNO.API.Auth
27 | *
28 | * API used to perform session login and logout.
29 | *
30 | * Method:
31 | * - Login
32 | * - Logout
33 | *
34 | * @author Nils - Initial contribution
35 | * @author Pavion - Contribution
36 | *
37 | */
38 | @NonNullByDefault
39 | public class SynoApiAuth extends SynoApiRequest {
40 |
41 | // API configuration
42 | private static final String API_NAME = "SYNO.API.Auth";
43 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_06, API_SCRIPT_AUTH);
44 |
45 | /**
46 | * @param config
47 | */
48 | public SynoApiAuth(SynoConfig config, HttpClient httpClient) {
49 | super(API_CONFIG, config, httpClient);
50 | }
51 |
52 | /**
53 | * Calls the passed method.
54 | *
55 | * @param method
56 | * @return
57 | * @throws WebApiException
58 | */
59 | private AuthResponse call(String method) throws WebApiException {
60 | Map params = new HashMap<>();
61 |
62 | // API parameters
63 | params.put("account", getConfig().getUsername());
64 | params.put("passwd", getConfig().getPassword());
65 |
66 | params.put("session", "SurveillanceStation");
67 | params.put("format", "sid");
68 |
69 | if (getConfig().getUsername().equals("")) {
70 | throw new WebApiException(100, "Empty credentials");
71 | }
72 | return callApi(method, params);
73 | }
74 |
75 | /**
76 | * Create new login session.
77 | *
78 | * @return
79 | * @throws WebApiException
80 | */
81 | public AuthResponse login() throws WebApiException {
82 | return call(METHOD_LOGIN);
83 | }
84 |
85 | /**
86 | * Destroy current login session.
87 | *
88 | * @return
89 | * @throws WebApiException
90 | */
91 | public AuthResponse logout(String sessionId) throws WebApiException {
92 | Map params = new HashMap<>();
93 |
94 | // API parameters
95 | params.put("session", "SurveillanceStation");
96 | params.put("_sid", sessionId);
97 |
98 | return callApi(METHOD_LOGOUT, params);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/resources/OH-INF/thing/bridge.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Represents the API for Synology Surveilance Station.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Synology
18 |
19 |
20 | serial
21 |
22 |
23 |
24 |
25 | Protocol (http, https) for accessing Surveillance Station
26 | http
27 | true
28 |
29 |
30 |
31 | Turn on to allow self-signed or invalid SSL certificates (restart on change required)
32 | false
33 | true
34 |
35 |
36 |
37 | network-address
38 | IP of Surveillance Station
39 | true
40 |
41 |
42 |
43 | Port for accessing Surveillance Station
44 | 5000
45 | true
46 |
47 |
48 |
49 | User name for accessing camera
50 | true
51 |
52 |
53 |
54 | password
55 | Password for accessing camera
56 | true
57 |
58 |
59 |
60 | Refresh rate for station global events in seconds (0 to disable)
61 | 3
62 | true
63 |
64 |
65 |
66 |
67 |
68 | Switch
69 |
70 | Home Mode of your Surveillance Station
71 |
72 |
73 | Number
74 |
75 | Trigger external event 1 to 10
76 |
77 |
78 |
79 | String
80 |
81 | Current session ID (SID)
82 |
83 |
84 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/error/WebApiAuthErrorCodes.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.error;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | /**
18 | * The {@link WebApiAuthErrorCodes} hosts errorCodes for SYNO.API.Auth.
19 | *
20 | * @author Nils - Initial contribution
21 | * @author Pavion - Contribution
22 | */
23 | @NonNullByDefault
24 | public enum WebApiAuthErrorCodes implements ErrorCode {
25 |
26 | NO_ERROR(0, "Success"),
27 | UNKNOWN_ERROR(100, "Unknown error."),
28 | PARAM_NOT_SPECIFIED(101, "The account parameter is not specified."),
29 | API_DOES_NOT_EXIST(102, "Surveillance Station is not running."),
30 | METHOD_NOT_EXIST(103, "Method does not exist"),
31 | API_VERSION_NOT_SUPPORTED(104, "This API version is not supported"),
32 | INSUFFICIENT_USER_PRIVILEGE(105, "Insufficient user privilege"),
33 | CONNECT_TIMEOUT(106, "Connection time out"),
34 | MULTIPLE_LOGIN(107, "Multiple login detected"),
35 | NEED_MANAGER_RIGHTS(117, "Need manager rights for operation"),
36 | UNKNOWN_ERROR_119(119, "Unknown API error 119"),
37 | INVALID_PASSWORD(400, "Invalid password."),
38 | DISABLED_ACCOUNT(401, "Guest or disabled account."),
39 | PERMISSION_DENIED(402, "Permission denied."),
40 | ONE_TIME_PASSWD_NOT_SCECIFIED(403, "One time password not specified."),
41 | ONE_TIME_PASSWD_AUTH_FAILED(404, "One time password authenticate failed."),
42 | APPORTAL_INCORRECT(405, "Appportal incorrect."),
43 | OTP_CODE_ENDFORCED(406, "OTP code enforced."),
44 | MAX_TRIES(407, "Max Tries (if auto blocking is set to true)."),
45 | PASSWD_EXP_CAN_NOT_CHANGE(408, "Password Expired Can not Change."),
46 | PASSWD_EXPIRED(409, "Password Expired."),
47 | PASSWD_MUST_CHANGE(410, "Password must change (when first time use or after reset password by admin)."),
48 | ACCOUNT_LOCKED(411, "Account Locked (when account max try exceed)."),
49 | MISSING_LICENSE(412, "Need to add license."),
50 | PLATFORM_MAX_REACHED(413, "Reach the maximum of platform."),
51 | EVENT_NOT_EXIST(414, "Some events not exist."),
52 | MESSAGE_CONNECT_ERROR(415, "message connect failed"),
53 | TEST_CONNETCION_ERROR(417, "Test Connection Error."),
54 | VISUAL_NAME_REPITITION(419, "Visualstation name repetition."),
55 | TOO_MANY_ITEMS(439, "Too many items selected.");
56 |
57 | private final int code;
58 | private final String msg;
59 |
60 | WebApiAuthErrorCodes(int code, String msg) {
61 | this.code = code;
62 | this.msg = msg;
63 | }
64 |
65 | @Override
66 | public int getCode() {
67 | return this.code;
68 | }
69 |
70 | @Override
71 | public String getMsg() {
72 | return this.msg;
73 | }
74 |
75 | /**
76 | *
77 | * @param code
78 | * @return
79 | */
80 | public static ErrorCode getByCode(int code) {
81 | return ErrorCode.lookup(code);
82 | }
83 |
84 | @Override
85 | public String toString() {
86 | return this.name() + " | ErrorCode: " + this.getCode() + " - " + this.getMsg();
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/CameraEventResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | import com.google.gson.JsonObject;
18 |
19 | /**
20 | * {@link CameraEventResponse} is a response for camera information
21 | *
22 | * @author Pavion - Initial contribution
23 | */
24 | @NonNullByDefault
25 | public class CameraEventResponse extends SimpleResponse {
26 |
27 | /**
28 | * @param jsonResponse
29 | */
30 | public CameraEventResponse(String jsonResponse) {
31 | super(jsonResponse);
32 | }
33 |
34 | /**
35 | * Returns motion detection parameter as Json Object
36 | *
37 | * @return Motion detection parameter as Json Object
38 | */
39 | private JsonObject getMDParam() {
40 | return getData().getAsJsonObject("MDParam");
41 | }
42 |
43 | /**
44 | * What is it?
45 | *
46 | * @return
47 | */
48 | @SuppressWarnings("unused")
49 | private JsonObject getPDParam() {
50 | return getData().getAsJsonObject("PDParam");
51 | }
52 |
53 | /**
54 | * Get MD object size
55 | *
56 | * @return
57 | */
58 | public CameraEventResponseObject getObjectSize() {
59 | return new CameraEventResponseObject(getMDParam().getAsJsonObject("objectSize"));
60 | }
61 |
62 | /**
63 | * Get MD sensitivity
64 | *
65 | * @return
66 | */
67 | public CameraEventResponseObject getSensitivity() {
68 | return new CameraEventResponseObject(getMDParam().getAsJsonObject("sensitivity"));
69 | }
70 |
71 | /**
72 | * Get MD threshold
73 | *
74 | * @return
75 | */
76 | public CameraEventResponseObject getThreshold() {
77 | return new CameraEventResponseObject(getMDParam().getAsJsonObject("threshold"));
78 | }
79 |
80 | /**
81 | * Get MD history
82 | *
83 | * @return
84 | */
85 | public CameraEventResponseObject getHistory() {
86 | return new CameraEventResponseObject(getMDParam().getAsJsonObject("history"));
87 | }
88 |
89 | /**
90 | * Get MD shortLiveSecond
91 | *
92 | * @return
93 | */
94 | public CameraEventResponseObject getShortLiveSecond() {
95 | return new CameraEventResponseObject(getMDParam().getAsJsonObject("shortLiveSecond"));
96 | }
97 |
98 | /**
99 | * Get MD percentage
100 | *
101 | * @return
102 | */
103 | public CameraEventResponseObject getPercentage() {
104 | return new CameraEventResponseObject(getMDParam().getAsJsonObject("percentage"));
105 | }
106 |
107 | /**
108 | * Get MD object size
109 | *
110 | * @return
111 | */
112 | public String getSource() {
113 | return getMDParam().get("source").getAsString();
114 | }
115 |
116 | /**
117 | * Get MD keep parameter
118 | *
119 | * @return
120 | */
121 | public boolean getKeep() {
122 | return getMDParam().get("keep").getAsBoolean();
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/EventResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import org.eclipse.jdt.annotation.NonNullByDefault;
19 | import org.openhab.binding.synologysurveillancestation.internal.webapi.SynoEvent;
20 |
21 | import com.google.gson.JsonArray;
22 | import com.google.gson.JsonElement;
23 | import com.google.gson.JsonObject;
24 |
25 | /**
26 | * {@link EventResponse} is a response with current events
27 | *
28 | * @author Pavion - Initial contribution
29 | */
30 | @NonNullByDefault
31 | public class EventResponse extends SimpleResponse {
32 | private static final int EVENT_POLL_OVERHEAD = 30;
33 |
34 | private Map synoEvents = new HashMap<>();
35 | private long timestamp = 0;
36 |
37 | /**
38 | * Constructs SynoEvents from JSON string.
39 | *
40 | * @param jsonResponse
41 | */
42 | public EventResponse(String jsonResponse) {
43 | super(jsonResponse);
44 | if (isSuccess()) {
45 | JsonArray events = getData().getAsJsonArray("events");
46 | timestamp = getData().getAsJsonObject().get("timestamp").getAsLong() - EVENT_POLL_OVERHEAD;
47 | for (JsonElement event : events) {
48 | if (event.isJsonObject()) {
49 | JsonObject cam = event.getAsJsonObject();
50 | int reason = cam.get("reason").getAsInt();
51 | if (!hasEvent(reason)) {
52 | long starttime = cam.get("startTime").getAsLong();
53 | long eventId = cam.get("eventId").getAsLong();
54 | boolean eventCompleted = cam.get("is_complete").getAsBoolean();
55 | synoEvents.put(reason, new SynoEvent(eventId, eventCompleted, reason));
56 | if (!eventCompleted && starttime < timestamp) {
57 | timestamp = starttime;
58 | }
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
65 | /**
66 | * How many events were returned?
67 | *
68 | * @return
69 | */
70 | public boolean isEmpty() {
71 | return synoEvents.isEmpty();
72 | }
73 |
74 | /**
75 | * Get the first (latest) event (if available)
76 | *
77 | * @return
78 | */
79 | public SynoEvent getFirst() {
80 | if (synoEvents.isEmpty()) {
81 | return new SynoEvent(0);
82 | } else {
83 | return synoEvents.getOrDefault(0, new SynoEvent(0));
84 | }
85 | }
86 |
87 | /**
88 | * @return if the event with specified reason exists
89 | */
90 | public boolean hasEvent(int eventReason) {
91 | return synoEvents.containsKey(eventReason);
92 | }
93 |
94 | /**
95 | * @return the event with specified reason
96 | */
97 | public SynoEvent getEvent(int eventReason) {
98 | return synoEvents.getOrDefault(eventReason, new SynoEvent(0));
99 | }
100 |
101 | /**
102 | * @return the timestamp
103 | */
104 | public long getTimestamp() {
105 | return timestamp;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/SynoCameraConfig.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal;
14 |
15 | import java.util.Objects;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.eclipse.jdt.annotation.Nullable;
19 |
20 | /**
21 | * The {@link SynoCameraConfig} is class for handling the camera Thing configuration
22 | *
23 | * @author Pavion - Initial contribution
24 | */
25 | @NonNullByDefault
26 | public class SynoCameraConfig {
27 | private int refreshRateSnapshot = 10;
28 | private int refreshRateEvents = 3;
29 | private int refreshRateMdParam = 0;
30 | private int snapshotStreamId = 1;
31 |
32 | /**
33 | * @return refreshRateSnapshot the refreshRateSnapshot to set
34 | */
35 | public int getRefreshRateSnapshot() {
36 | return refreshRateSnapshot;
37 | }
38 |
39 | /**
40 | * @param refreshRateSnapshot the refreshRateSnapshot to set
41 | */
42 | public void setRefreshRateSnapshot(int refreshRateSnapshot) {
43 | this.refreshRateSnapshot = refreshRateSnapshot;
44 | }
45 |
46 | /**
47 | * @return refreshRateEvents
48 | */
49 | public int getRefreshRateEvents() {
50 | return refreshRateEvents;
51 | }
52 |
53 | /**
54 | * @param refreshRateEvents the refreshRateEvents to set
55 | */
56 | public void setRefreshRateEvents(int refreshRateEvents) {
57 | this.refreshRateEvents = refreshRateEvents;
58 | }
59 |
60 | /**
61 | * @param refreshRateMdParam the refreshRateMdParam to set
62 | */
63 | public void setRefreshRateMdParam(int refreshRateMdParam) {
64 | this.refreshRateMdParam = refreshRateMdParam;
65 | }
66 |
67 | /**
68 | * @return refreshRateMdParam
69 | */
70 | public int getRefreshRateMdParam() {
71 | return refreshRateMdParam;
72 | }
73 |
74 | /**
75 | * @param refreshRateMdParam the refreshRateMdParam to set
76 | */
77 | public void setSnapshotStreamId(int snapshotStreamId) {
78 | this.snapshotStreamId = snapshotStreamId;
79 | }
80 |
81 | /**
82 | * @return refreshRateMdParam
83 | */
84 | public int getSnapshotStreamId() {
85 | return snapshotStreamId;
86 | }
87 |
88 | @Override
89 | public int hashCode() {
90 | return Objects.hash(refreshRateEvents, refreshRateMdParam, refreshRateSnapshot, snapshotStreamId);
91 | }
92 |
93 | @Override
94 | public boolean equals(@Nullable Object obj) {
95 | if (this == obj) {
96 | return true;
97 | }
98 | if (obj == null) {
99 | return false;
100 | }
101 | if (getClass() != obj.getClass()) {
102 | return false;
103 | }
104 | SynoCameraConfig other = (SynoCameraConfig) obj;
105 | return refreshRateEvents == other.refreshRateEvents && refreshRateMdParam == other.refreshRateMdParam
106 | && refreshRateSnapshot == other.refreshRateSnapshot && snapshotStreamId == other.snapshotStreamId;
107 | }
108 |
109 | @Override
110 | public String toString() {
111 | return "SynoCameraConfig [refreshRateSnapshot=" + refreshRateSnapshot + ", refreshRateEvents="
112 | + refreshRateEvents + ", refreshRateMdParam=" + refreshRateMdParam + ", snapshotStreamId="
113 | + snapshotStreamId + "]";
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/SynoApiResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 |
17 | import com.google.gson.JsonArray;
18 | import com.google.gson.JsonObject;
19 | import com.google.gson.JsonParser;
20 | import com.google.gson.JsonSyntaxException;
21 |
22 | /**
23 | * {@link SynoApiResponse} is an abstract class for an API response
24 | *
25 | * @author Nils - Initial contribution
26 | * @author Pavion - Contribution
27 | */
28 | @NonNullByDefault
29 | public abstract class SynoApiResponse {
30 |
31 | public static final String PROP_VENDOR = "vendor";
32 | public static final String PROP_MODEL = "model";
33 | public static final String PROP_DEVICETYPE = "deviceType";
34 | public static final String PROP_HOST = "host";
35 | public static final String PROP_RESOLUTION = "resolution";
36 | public static final String PROP_TYPE = "type";
37 | // public static final String PROP_CAMERANUMBER = "cameraNumber";
38 |
39 | // PTZ capabilities
40 | public static final String PROP_PTZ = "ptz";
41 | public static final String PROP_PTZ_PAN = "ptz_pan";
42 | public static final String PROP_PTZ_TILT = "ptz_tilt";
43 | public static final String PROP_PTZ_ZOOM = "ptz_zoom";
44 | public static final String PROP_PTZ_HOME = "ptz_home";
45 | public static final String PROP_PTZ_ABS = "ptz_abs";
46 | public static final String PROP_PTZ_FOCUS = "ptz_focus";
47 | public static final String PROP_PTZ_AUTOFOCUS = "ptz_autofocus";
48 | public static final String PROP_PTZ_IRIS = "ptz_iris";
49 | public static final String PROP_PTZ_SPEED = "ptz_speed";
50 | public static final String PROP_PTZ_ZOOM_SPEED = "ptz_zoom_speed";
51 |
52 | private JsonObject jsonResponse = new JsonParser().parse("{\"data\":{},\"success\":false}").getAsJsonObject();
53 |
54 | public SynoApiResponse() {
55 | }
56 |
57 | /**
58 | * @param jsonResponse
59 | */
60 | public SynoApiResponse(String jsonResponse) {
61 | try {
62 | JsonObject json = new JsonParser().parse(jsonResponse).getAsJsonObject();
63 | this.jsonResponse = json;
64 | } catch (JsonSyntaxException e) {
65 | // keep default value
66 | }
67 | }
68 |
69 | /**
70 | * @return
71 | */
72 | public JsonObject getData() {
73 | JsonObject ret = jsonResponse.getAsJsonObject("data");
74 | if (ret == null) {
75 | return new JsonObject();
76 | }
77 | return ret;
78 | }
79 |
80 | /**
81 | *
82 | * @return
83 | */
84 | public JsonArray getDataAsJsonArray() {
85 | JsonArray ret = jsonResponse.getAsJsonArray("data");
86 | if (ret == null) {
87 | return new JsonArray();
88 | }
89 | return ret;
90 | }
91 |
92 | /**
93 | * @return
94 | */
95 | public boolean isSuccess() {
96 | if (jsonResponse.has("success")) {
97 | return jsonResponse.get("success").getAsBoolean();
98 | }
99 | return false;
100 | }
101 |
102 | /**
103 | * @return
104 | */
105 | public int getErrorcode() {
106 | if (jsonResponse.has("error")) {
107 | if (jsonResponse.getAsJsonObject("error").has("code")) {
108 | return jsonResponse.getAsJsonObject("error").get("code").getAsInt();
109 | }
110 | }
111 | return 0;
112 | }
113 |
114 | @Override
115 | public String toString() {
116 | return jsonResponse.toString();
117 | }
118 |
119 | /**
120 | * @param hexValue
121 | * @param bitNumber
122 | * @return
123 | */
124 | protected boolean isBitSet(String hexValue, int bitNumber) {
125 | int val = Integer.valueOf(hexValue, 16);
126 | return (val & (1 << bitNumber)) != 0;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/discovery/BridgeMdnsDiscoveryService.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.discovery;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.Set;
18 |
19 | import javax.jmdns.ServiceInfo;
20 |
21 | import org.eclipse.jdt.annotation.NonNullByDefault;
22 | import org.eclipse.jdt.annotation.Nullable;
23 | import org.openhab.binding.synologysurveillancestation.SynoBindingConstants;
24 | import org.openhab.core.config.discovery.DiscoveryResult;
25 | import org.openhab.core.config.discovery.DiscoveryResultBuilder;
26 | import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
27 | import org.openhab.core.thing.ThingTypeUID;
28 | import org.openhab.core.thing.ThingUID;
29 | import org.osgi.service.component.ComponentContext;
30 | import org.osgi.service.component.annotations.Activate;
31 | import org.osgi.service.component.annotations.Component;
32 |
33 | /**
34 | * The {@link BridgeMdnsDiscoveryService} is a class for discovering the DiskStation via mDNS service
35 | *
36 | * @author Pavion - Initial contribution
37 | */
38 | @Component(service = MDNSDiscoveryParticipant.class, immediate = false, configurationPid = "binding.synologysurveillancestation")
39 | @NonNullByDefault
40 | public class BridgeMdnsDiscoveryService implements MDNSDiscoveryParticipant {
41 |
42 | // private final Logger logger = LoggerFactory.getLogger(BridgeMdnsDiscoveryService.class);
43 |
44 | @Activate
45 | public void activate(ComponentContext context) {
46 | }
47 |
48 | @Override
49 | public Set getSupportedThingTypeUIDs() {
50 | return SynoBindingConstants.SUPPORTED_BRIDGE_TYPES;
51 | }
52 |
53 | @Override
54 | public String getServiceType() {
55 | return "_http._tcp.local.";
56 | }
57 |
58 | @Override
59 | @Nullable
60 | public DiscoveryResult createResult(ServiceInfo service) {
61 | ThingUID uid = getThingUID(service);
62 | if (uid != null) {
63 | if (service.getHostAddresses() != null && service.getHostAddresses().length > 0
64 | && !service.getHostAddresses()[0].isEmpty()) {
65 | String name = service.getName();
66 | String ip = service.getHostAddresses()[0];
67 | String model = service.getPropertyString("model");
68 | String serial = service.getPropertyString("serial");
69 | String port = service.getPropertyString("admin_port");
70 |
71 | if (name != null && ip != null && model != null && serial != null && port != null) {
72 | String label = String.format("%s (%s)", name, model);
73 | Map properties = new HashMap<>();
74 | properties.put(SynoBindingConstants.ACCEPT_SSL, false);
75 | properties.put(SynoBindingConstants.PROTOCOL, "http");
76 | properties.put(SynoBindingConstants.PORT, port);
77 | properties.put(SynoBindingConstants.HOST, ip);
78 | // properties.put(SynoBindingConstants.USER_NAME, "");
79 | // properties.put(SynoBindingConstants.PASSWORD, "");
80 | properties.put(SynoBindingConstants.SERIAL, serial.toLowerCase());
81 |
82 | DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties)
83 | .withRepresentationProperty(SynoBindingConstants.SERIAL).withLabel(label).build();
84 | return result;
85 | }
86 | }
87 | }
88 | return null;
89 | }
90 |
91 | @Override
92 | @Nullable
93 | public ThingUID getThingUID(ServiceInfo service) {
94 | String vendor = service.getPropertyString("vendor");
95 | String serial = service.getPropertyString("serial");
96 | if (vendor != null && serial != null) {
97 | if (vendor.startsWith("Synology")) {
98 | return new ThingUID(SynoBindingConstants.THING_TYPE_STATION, serial.toLowerCase());
99 | }
100 | }
101 | return null;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/thread/SynoApiThreadCamera.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.thread;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.*;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.openhab.binding.synologysurveillancestation.handler.SynoCameraHandler;
19 | import org.openhab.binding.synologysurveillancestation.internal.SynoCameraConfig;
20 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
22 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.CameraResponse;
23 | import org.openhab.core.library.types.OnOffType;
24 | import org.openhab.core.library.types.StringType;
25 | import org.openhab.core.thing.Channel;
26 |
27 | /**
28 | * Thread for getting camera state (enabled, recording)
29 | *
30 | * @author Pavion - Initial contribution
31 | */
32 | @NonNullByDefault
33 | public class SynoApiThreadCamera extends SynoApiThread {
34 | // private final Logger logger = LoggerFactory.getLogger(SynoApiThreadCamera.class);
35 |
36 | public SynoApiThreadCamera(SynoCameraHandler handler, int refreshRate) {
37 | super(SynoApiThread.THREAD_CAMERA, handler, refreshRate);
38 | }
39 |
40 | @Override
41 | public boolean isNeeded() {
42 | boolean ret = getSynoHandler().isLinked(CHANNEL_ENABLE) || getSynoHandler().isLinked(CHANNEL_RECORD)
43 | || getSynoHandler().isLinked(CHANNEL_SNAPSHOT_URI_DYNAMIC);
44 | if (getSynoHandler().isPtz()) {
45 | ret = ret || getSynoHandler().isLinked(CHANNEL_MOVEPRESET) || getSynoHandler().isLinked(CHANNEL_RUNPATROL);
46 | }
47 | return ret;
48 | }
49 |
50 | @Override
51 | public boolean refresh() throws Exception {
52 | boolean ret = true;
53 |
54 | SynoCameraHandler cameraHandler = getSynoHandler();
55 | String cameraId = cameraHandler.getCameraId();
56 |
57 | if (cameraHandler.isLinked(CHANNEL_SNAPSHOT_URI_DYNAMIC)) {
58 | Channel channel = cameraHandler.getThing().getChannel(CHANNEL_SNAPSHOT_URI_DYNAMIC);
59 | SynoCameraConfig config = cameraHandler.getThing().getConfiguration().as(SynoCameraConfig.class);
60 | String path = cameraHandler.getSynoWebApiHandler().getApiCamera().getSnapshotUri(cameraId,
61 | config.getSnapshotStreamId());
62 | path += "×tamp=" + String.valueOf(System.currentTimeMillis());
63 | cameraHandler.updateState(channel.getUID(), new StringType(path));
64 | }
65 |
66 | if (cameraHandler.isPtz()) {
67 | if (cameraHandler.isLinked(CHANNEL_MOVEPRESET)) {
68 | cameraHandler.updatePresets();
69 | }
70 | if (cameraHandler.isLinked(CHANNEL_RUNPATROL)) {
71 | cameraHandler.updatePatrols();
72 | }
73 | }
74 |
75 | if (cameraHandler.isLinked(CHANNEL_ENABLE) || cameraHandler.isLinked(CHANNEL_RECORD)) {
76 | CameraResponse response = cameraHandler.getSynoWebApiHandler().getApiCamera().getInfo(cameraId);
77 | if (response.isSuccess()) {
78 | if (cameraHandler.isLinked(CHANNEL_ENABLE)) {
79 | Channel channel = cameraHandler.getThing().getChannel(CHANNEL_ENABLE);
80 | cameraHandler.updateState(channel.getUID(),
81 | response.isEnabled(cameraId) ? OnOffType.ON : OnOffType.OFF);
82 | }
83 | if (cameraHandler.isLinked(CHANNEL_RECORD)) {
84 | Channel channel = cameraHandler.getThing().getChannel(CHANNEL_RECORD);
85 | cameraHandler.updateState(channel.getUID(),
86 | response.isRecording(cameraId) ? OnOffType.ON : OnOffType.OFF);
87 | }
88 |
89 | ret &= true;
90 |
91 | } else if (response.getErrorcode() == 105) {
92 | throw new WebApiException(WebApiAuthErrorCodes.INSUFFICIENT_USER_PRIVILEGE);
93 | } else {
94 | ret &= false;
95 | }
96 | }
97 |
98 | return ret;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/SynoConfig.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal;
14 |
15 | import org.eclipse.jdt.annotation.NonNullByDefault;
16 | import org.eclipse.jdt.annotation.Nullable;
17 |
18 | /**
19 | * The {@link SynoConfig} is class for handling the binding configuration
20 | *
21 | * @author Nils - Initial contribution
22 | * @author Pavion - Contribution
23 | */
24 | @NonNullByDefault
25 | public class SynoConfig {
26 | private String protocol = "http";
27 | private boolean acceptSsl = false;
28 | private String host = "";
29 | private int port = 5000;
30 | private String username = "";
31 | private String password = "";
32 | private int refreshRateEvents = 5;
33 |
34 | /**
35 | * Returns the protocol.
36 | *
37 | * @return the protocol
38 | */
39 | public String getProtocol() {
40 | return protocol;
41 | }
42 |
43 | /**
44 | * Returns accept SSL state
45 | *
46 | * @return true if to accept everything, false by default
47 | */
48 | public boolean isAcceptSsl() {
49 | return acceptSsl;
50 | }
51 |
52 | /**
53 | * Returns the host name Surveillance Station.
54 | *
55 | * @return the host address
56 | */
57 | public String getHost() {
58 | return host;
59 | }
60 |
61 | /**
62 | * Returns the port.
63 | *
64 | * @return the port
65 | */
66 | public int getPort() {
67 | return port;
68 | }
69 |
70 | /**
71 | * Returns the username.
72 | *
73 | * @return the username
74 | */
75 | public String getUsername() {
76 | return username;
77 | }
78 |
79 | /**
80 | * Returns the password.
81 | *
82 | * @return the password
83 | */
84 | public String getPassword() {
85 | return password;
86 | }
87 |
88 | /**
89 | * @return the refreshRateEvents
90 | */
91 | public int getRefreshRateEvents() {
92 | return refreshRateEvents;
93 | }
94 |
95 | @Override
96 | public String toString() {
97 | return "Config [protocol=" + protocol + ", acceptSsl=" + acceptSsl + ", host=" + host + ", port=" + port
98 | + ", username=" + username + ", password=" + "********, refreshRateEvents="
99 | + String.valueOf(refreshRateEvents) + "]";
100 | }
101 |
102 | @Override
103 | public boolean equals(@Nullable Object obj) {
104 | if (obj == null) {
105 | return false;
106 | }
107 | if (!(obj instanceof SynoConfig)) {
108 | return false;
109 | }
110 | SynoConfig cfg = (SynoConfig) obj;
111 | return cfg.getHost().equals(getHost()) && cfg.getPassword().equals(getPassword())
112 | && cfg.isAcceptSsl() == isAcceptSsl() && cfg.getProtocol().equals(getProtocol())
113 | && cfg.getPort() == port && cfg.getUsername().equals(getUsername())
114 | && cfg.getRefreshRateEvents() == refreshRateEvents;
115 | }
116 |
117 | /**
118 | * If but refresh is changed
119 | *
120 | * @param obj
121 | * @return
122 | */
123 | public boolean equalsButForRefresh(@Nullable Object obj) {
124 | if (obj == null) {
125 | return false;
126 | }
127 | if (!(obj instanceof SynoConfig)) {
128 | return false;
129 | }
130 | SynoConfig cfg = (SynoConfig) obj;
131 | return cfg.getHost().equals(getHost()) && cfg.getPassword().equals(getPassword())
132 | && cfg.isAcceptSsl() == isAcceptSsl() && cfg.getProtocol().equals(getProtocol())
133 | && cfg.getPort() == port && cfg.getUsername().equals(getUsername());
134 | }
135 |
136 | /**
137 | * @param protocol the protocol to set
138 | */
139 | public void setProtocol(String protocol) {
140 | this.protocol = protocol;
141 | }
142 |
143 | /**
144 | * @param acceptSsl the acceptSsl to set
145 | */
146 | public void setAcceptSsl(boolean acceptSsl) {
147 | this.acceptSsl = acceptSsl;
148 | }
149 |
150 | /**
151 | * @param host the host to set
152 | */
153 | public void setHost(String host) {
154 | this.host = host;
155 | }
156 |
157 | /**
158 | * @param port the port to set
159 | */
160 | public void setPort(int port) {
161 | this.port = port;
162 | }
163 |
164 | /**
165 | * @param username the username to set
166 | */
167 | public void setUsername(String username) {
168 | this.username = username;
169 | }
170 |
171 | /**
172 | * @param password the password to set
173 | */
174 | public void setPassword(String password) {
175 | this.password = password;
176 | }
177 |
178 | /**
179 | * @param refreshRateEvents the refreshRateEvents to set
180 | */
181 | public void setRefreshRateEvents(int refreshRateEvents) {
182 | this.refreshRateEvents = refreshRateEvents;
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiCameraEvent.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import org.eclipse.jdt.annotation.NonNullByDefault;
19 | import org.eclipse.jetty.client.HttpClient;
20 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
22 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.CameraEventResponse;
23 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.SimpleResponse;
24 |
25 | /**
26 | * SYNO.SurveillanceStation.Camera.Event
27 | *
28 | * Event Detection related WebAPI. e.g. Enumerate detection parameters or long polling for alarm status or save
29 | * detection parameters.
30 | *
31 | * @author Pavion - Initial contribution
32 | *
33 | */
34 | @NonNullByDefault
35 | public class SynoApiCameraEvent extends SynoApiRequest {
36 | // private final Logger logger = LoggerFactory.getLogger(SynoApiCameraEvent.class);
37 |
38 | // API configuration
39 | private static final String API_NAME = "SYNO.SurveillanceStation.Camera.Event";
40 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_01, API_SCRIPT_ENTRY);
41 |
42 | /**
43 | * @param config
44 | */
45 | public SynoApiCameraEvent(SynoConfig config, HttpClient httpClient) {
46 | super(API_CONFIG, config, httpClient);
47 | }
48 |
49 | /**
50 | * Get motion detection parameter
51 | *
52 | * @param cameraId
53 | * @return
54 | * @throws WebApiException
55 | */
56 | public CameraEventResponse getMDParam(String cameraId) throws WebApiException {
57 | Map params = new HashMap<>();
58 | params.put("camId", cameraId);
59 | return callApi(METHOD_MOTIONENUM, params);
60 | }
61 |
62 | public SimpleResponse setSource(String cameraId, String source) throws WebApiException {
63 | if (!("-1".equals(source) || "0".equals(source) || "1".equals(source))) {
64 | return new SimpleResponse("{\"data\":{},\"success\":false}");
65 | }
66 | Map params = new HashMap<>();
67 | params.put("camId", cameraId);
68 | params.put("keep", "true");
69 | params.put("source", source);
70 | return callApi(METHOD_MDPARAMSAVE, params);
71 | }
72 |
73 | // Warning, absolute values are used
74 | public SimpleResponse setSensitivity(String cameraId, int val) throws WebApiException {
75 | if (val < 1 || val > 99) {
76 | return new SimpleResponse("{\"data\":{},\"success\":false}");
77 | }
78 | Map params = new HashMap<>();
79 | params.put("camId", cameraId);
80 | params.put("keep", "true");
81 | params.put("sensitivity", String.valueOf(val));
82 | return callApi(METHOD_MDPARAMSAVE, params);
83 | }
84 |
85 | public SimpleResponse setThreshold(String cameraId, int val) throws WebApiException {
86 | if (val < 1 || val > 99) {
87 | return new SimpleResponse("{\"data\":{},\"success\":false}");
88 | }
89 | Map params = new HashMap<>();
90 | params.put("camId", cameraId);
91 | params.put("keep", "true");
92 | params.put("threshold", String.valueOf(val));
93 | return callApi(METHOD_MDPARAMSAVE, params);
94 | }
95 |
96 | public SimpleResponse setObjectSize(String cameraId, int val) throws WebApiException {
97 | if (val < 1 || val > 99) {
98 | return new SimpleResponse("{\"data\":{},\"success\":false}");
99 | }
100 | Map params = new HashMap<>();
101 | params.put("camId", cameraId);
102 | params.put("keep", "true");
103 | params.put("objectSize", String.valueOf(val));
104 | return callApi(METHOD_MDPARAMSAVE, params);
105 | }
106 |
107 | public SimpleResponse setPercentage(String cameraId, int val) throws WebApiException {
108 | if (val < 1 || val > 99) {
109 | return new SimpleResponse("{\"data\":{},\"success\":false}");
110 | }
111 | Map params = new HashMap<>();
112 | params.put("camId", cameraId);
113 | params.put("keep", "true");
114 | params.put("percentage", String.valueOf(val));
115 | return callApi(METHOD_MDPARAMSAVE, params);
116 | }
117 |
118 | public SimpleResponse setShortLiveSecond(String cameraId, int val) throws WebApiException {
119 | if (val < 0 || val > 10) {
120 | return new SimpleResponse("{\"data\":{},\"success\":false}");
121 | }
122 | Map params = new HashMap<>();
123 | params.put("camId", cameraId);
124 | params.put("keep", "true");
125 | params.put("shortLiveSecond", String.valueOf(val));
126 | return callApi(METHOD_MDPARAMSAVE, params);
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/thread/SynoApiThreadEvent.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.thread;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.*;
16 |
17 | import java.time.ZonedDateTime;
18 | import java.util.HashMap;
19 | import java.util.Map;
20 |
21 | import org.eclipse.jdt.annotation.NonNullByDefault;
22 | import org.openhab.binding.synologysurveillancestation.handler.SynoCameraHandler;
23 | import org.openhab.binding.synologysurveillancestation.internal.webapi.SynoEvent;
24 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
25 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
26 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.EventResponse;
27 | import org.openhab.core.library.types.OnOffType;
28 | import org.openhab.core.thing.Channel;
29 |
30 | /**
31 | * Thread for getting camera events (motion, alarm)
32 | *
33 | * @author Pavion - Initial contribution
34 | */
35 | @NonNullByDefault
36 | public class SynoApiThreadEvent extends SynoApiThread {
37 | // private final Logger logger = LoggerFactory.getLogger(SynoApiThreadEvent.class);
38 |
39 | private long lastEventTime;
40 | private Map events = new HashMap<>();
41 |
42 | public SynoApiThreadEvent(SynoCameraHandler handler, int refreshRate) {
43 | super(SynoApiThread.THREAD_EVENT, handler, refreshRate);
44 | lastEventTime = ZonedDateTime.now().minusSeconds(refreshRate * 2).toEpochSecond();
45 | events.put(CHANNEL_EVENT_MOTION, new SynoEvent(SynoEvent.EVENT_REASON_MOTION));
46 | events.put(CHANNEL_EVENT_ALARM, new SynoEvent(SynoEvent.EVENT_REASON_ALARM));
47 | events.put(CHANNEL_EVENT_MANUAL, new SynoEvent(SynoEvent.EVENT_REASON_MANUAL));
48 | events.put(CHANNEL_EVENT_CONTINUOUS, new SynoEvent(SynoEvent.EVENT_REASON_CONTINUOUS));
49 | events.put(CHANNEL_EVENT_EXTERNAL, new SynoEvent(SynoEvent.EVENT_REASON_EXTERNAL));
50 | events.put(CHANNEL_EVENT_ACTIONRULE, new SynoEvent(SynoEvent.EVENT_REASON_ACTIONRULE));
51 | }
52 |
53 | @Override
54 | public boolean isNeeded() {
55 | return (getSynoHandler().isLinked(CHANNEL_EVENT_MOTION) || getSynoHandler().isLinked(CHANNEL_EVENT_ALARM)
56 | || getSynoHandler().isLinked(CHANNEL_EVENT_MANUAL)
57 | || getSynoHandler().isLinked(CHANNEL_EVENT_CONTINUOUS)
58 | || getSynoHandler().isLinked(CHANNEL_EVENT_EXTERNAL)
59 | || getSynoHandler().isLinked(CHANNEL_EVENT_ACTIONRULE));
60 | }
61 |
62 | @Override
63 | public boolean refresh() throws Exception {
64 | SynoCameraHandler cameraHandler = getSynoHandler();
65 |
66 | for (String eventType : events.keySet()) {
67 | if (getSynoHandler().isLinked(eventType)) {
68 | Channel channel = cameraHandler.getThing().getChannel(eventType);
69 | SynoEvent event = events.get(eventType);
70 | EventResponse response = cameraHandler.getSynoWebApiHandler().getApiEvent()
71 | .getEventResponse(cameraHandler.getCameraId(), lastEventTime, event.getReason());
72 | if (response.isSuccess()) {
73 | if (!response.isEmpty()) {
74 | SynoEvent responseEvent = response.getFirst();
75 | if (responseEvent.getEventId() != event.getEventId()) {
76 | event.setEventId(responseEvent.getEventId());
77 | event.setEventCompleted(responseEvent.isEventCompleted());
78 | cameraHandler.updateState(channel.getUID(), OnOffType.ON);
79 | if (responseEvent.isEventCompleted()) {
80 | cameraHandler.updateState(channel.getUID(), OnOffType.OFF);
81 | }
82 | } else if (responseEvent.getEventId() == event.getEventId() && responseEvent.isEventCompleted()
83 | && !event.isEventCompleted()) {
84 | event.setEventCompleted(true);
85 | cameraHandler.updateState(channel.getUID(), OnOffType.OFF);
86 | }
87 | } else {
88 | event.setEventCompleted(true);
89 | cameraHandler.updateState(channel.getUID(), OnOffType.OFF);
90 | }
91 | if (response.getTimestamp() > lastEventTime) {
92 | lastEventTime = response.getTimestamp();
93 | }
94 | } else if (response.getErrorcode() == 105) {
95 | throw new WebApiException(WebApiAuthErrorCodes.INSUFFICIENT_USER_PRIVILEGE);
96 | } else {
97 | return false;
98 | }
99 | }
100 | }
101 | return true;
102 | }
103 |
104 | /**
105 | * @return the events
106 | */
107 | public Map getEvents() {
108 | return events;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/SynoBindingConstants.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation;
14 |
15 | import java.util.Collections;
16 | import java.util.Set;
17 | import java.util.stream.Collectors;
18 | import java.util.stream.Stream;
19 |
20 | import org.eclipse.jdt.annotation.NonNullByDefault;
21 | import org.openhab.core.thing.ThingTypeUID;
22 |
23 | /**
24 | * The {@link SynoBindingConstants} class defines common constants, which are
25 | * used across the whole binding.
26 | *
27 | * @author Nils - Initial contribution
28 | * @author Pavion - Contribution
29 | */
30 | @NonNullByDefault
31 | public class SynoBindingConstants {
32 |
33 | public static final String BINDING_ID = "synologysurveillancestation";
34 |
35 | public static final String DEVICE_ID = "deviceID";
36 |
37 | // List of all Thing Type UIDs
38 | public static final ThingTypeUID THING_TYPE_CAMERA = new ThingTypeUID(BINDING_ID, "camera");
39 | public static final ThingTypeUID THING_TYPE_STATION = new ThingTypeUID(BINDING_ID, "station");
40 |
41 | public static final Set SUPPORTED_BRIDGE_TYPES = Collections.singleton(THING_TYPE_STATION);
42 | public static final Set SUPPORTED_CAMERA_TYPES = Collections.singleton(THING_TYPE_CAMERA);
43 | public static final Set SUPPORTED_THING_TYPES = Collections
44 | .unmodifiableSet(Stream.of(THING_TYPE_CAMERA, THING_TYPE_STATION).collect(Collectors.toSet()));
45 |
46 | /* List of all config properties */
47 | public static final String PROTOCOL = "protocol";
48 | public static final String ACCEPT_SSL = "acceptSsl";
49 | public static final String HOST = "host";
50 | public static final String PORT = "port";
51 | public static final String USER_NAME = "username";
52 | public static final String PASSWORD = "password";
53 | public static final String SERIAL = "serial";
54 | public static final String SESSION_ID = "sessionID";
55 |
56 | // List of all Bridge Channels
57 | public static final String CHANNEL_HOMEMODE = "homemode";
58 | public static final String CHANNEL_EVENT_TRIGGER = "eventtrigger";
59 | public static final String CHANNEL_SID = "sid";
60 |
61 | // List of all Channel ids
62 | public static final String CHANNEL_SNAPSHOT_URI_DYNAMIC = "common#snapshot-uri-dynamic";
63 | public static final String CHANNEL_SNAPSHOT_URI_STATIC = "common#snapshot-uri-static";
64 | public static final String CHANNEL_LIVE_URI_RTSP = "common#live-uri-rtsp";
65 | public static final String CHANNEL_LIVE_URI_MJPEG_HTTP = "common#live-uri-mjpeg-http";
66 |
67 | public static final Set STATIC_CHANNELS = Collections
68 | .unmodifiableSet(Stream.of(CHANNEL_SNAPSHOT_URI_STATIC, CHANNEL_LIVE_URI_RTSP, CHANNEL_LIVE_URI_MJPEG_HTTP)
69 | .collect(Collectors.toSet()));
70 |
71 | public static final String CHANNEL_SNAPSHOT = "common#snapshot";
72 | public static final String CHANNEL_RECORD = "common#record";
73 | public static final String CHANNEL_ENABLE = "common#enable";
74 |
75 | // List of all PTZ channels
76 | public static final String CHANNEL_ZOOM = "ptz#zoom";
77 | public static final String CHANNEL_MOVE = "ptz#move";
78 | public static final String CHANNEL_MOVEPRESET = "ptz#movepreset";
79 | public static final String CHANNEL_RUNPATROL = "ptz#runpatrol";
80 | public static final Set CHANNEL_PTZ = Collections.unmodifiableSet(
81 | Stream.of(CHANNEL_ZOOM, CHANNEL_MOVE, CHANNEL_MOVEPRESET, CHANNEL_RUNPATROL).collect(Collectors.toSet()));
82 |
83 | // List of all move commands
84 | public static final String MOVE_COMMAND_EMPTY = "";
85 | public static final String MOVE_COMMAND_START = "Start";
86 | public static final String MOVE_COMMAND_STOP = "Stop";
87 |
88 | // List of all event types (as in thing.xml)
89 | public static final String CHANNEL_EVENT_MOTION = "event#motion";
90 | public static final String CHANNEL_EVENT_ALARM = "event#alarm";
91 | public static final String CHANNEL_EVENT_MANUAL = "event#manual";
92 | public static final String CHANNEL_EVENT_EXTERNAL = "event#external";
93 | public static final String CHANNEL_EVENT_CONTINUOUS = "event#continuous";
94 | public static final String CHANNEL_EVENT_ACTIONRULE = "event#actionrule";
95 | public static final Set CHANNEL_EVENT = Collections.unmodifiableSet(
96 | Stream.of(CHANNEL_EVENT_MOTION, CHANNEL_EVENT_ALARM, CHANNEL_EVENT_MANUAL, CHANNEL_EVENT_CONTINUOUS,
97 | CHANNEL_EVENT_EXTERNAL, CHANNEL_EVENT_ACTIONRULE).collect(Collectors.toSet()));
98 |
99 | // List of all MD parameters
100 | public static final String CHANNEL_MDPARAM_SOURCE = "md-param#md-param-source";
101 | public static final String CHANNEL_MDPARAM_SENSITIVITY = "md-param#md-param-sensitivity";
102 | public static final String CHANNEL_MDPARAM_THRESHOLD = "md-param#md-param-threshold";
103 | public static final String CHANNEL_MDPARAM_OBJECTSIZE = "md-param#md-param-objectsize";
104 | public static final String CHANNEL_MDPARAM_PERCENTAGE = "md-param#md-param-percentage";
105 | public static final String CHANNEL_MDPARAM_SHORTLIVE = "md-param#md-param-shortlive";
106 | public static final Set CHANNEL_MDPARAM = Collections.unmodifiableSet(Stream
107 | .of(CHANNEL_MDPARAM_SOURCE, CHANNEL_MDPARAM_SENSITIVITY, CHANNEL_MDPARAM_THRESHOLD,
108 | CHANNEL_MDPARAM_OBJECTSIZE, CHANNEL_MDPARAM_PERCENTAGE, CHANNEL_MDPARAM_SHORTLIVE)
109 | .collect(Collectors.toSet()));
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/discovery/CameraDiscoveryService.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.discovery;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.THING_TYPE_CAMERA;
16 |
17 | import java.util.Map;
18 | import java.util.Set;
19 |
20 | import org.eclipse.jdt.annotation.NonNullByDefault;
21 | import org.eclipse.jdt.annotation.Nullable;
22 | import org.openhab.binding.synologysurveillancestation.SynoBindingConstants;
23 | import org.openhab.binding.synologysurveillancestation.handler.SynoBridgeHandler;
24 | import org.openhab.binding.synologysurveillancestation.internal.webapi.SynoWebApiHandler;
25 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
26 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
27 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.CameraResponse;
28 | import org.openhab.core.config.discovery.AbstractDiscoveryService;
29 | import org.openhab.core.config.discovery.DiscoveryResult;
30 | import org.openhab.core.config.discovery.DiscoveryResultBuilder;
31 | import org.openhab.core.thing.ThingStatus;
32 | import org.openhab.core.thing.ThingTypeUID;
33 | import org.openhab.core.thing.ThingUID;
34 | import org.slf4j.Logger;
35 | import org.slf4j.LoggerFactory;
36 |
37 | import com.google.gson.JsonArray;
38 | import com.google.gson.JsonElement;
39 | import com.google.gson.JsonObject;
40 |
41 | /**
42 | * The {@link CameraDiscoveryService} is a service for discovering your cameras through Synology API
43 | *
44 | * @author Nils - Initial contribution
45 | * @author Pavion - Contribution
46 | */
47 | @NonNullByDefault
48 | public class CameraDiscoveryService extends AbstractDiscoveryService {
49 |
50 | private final Logger logger = LoggerFactory.getLogger(CameraDiscoveryService.class);
51 |
52 | @Nullable
53 | private SynoBridgeHandler bridgeHandler = null;
54 |
55 | /**
56 | * Maximum time to search for devices in seconds.
57 | */
58 | private static final int SEARCH_TIME = 20;
59 |
60 | public CameraDiscoveryService() {
61 | super(SynoBindingConstants.SUPPORTED_CAMERA_TYPES, SEARCH_TIME);
62 | }
63 |
64 | public CameraDiscoveryService(SynoBridgeHandler bridgeHandler) throws IllegalArgumentException {
65 | super(SEARCH_TIME);
66 | this.bridgeHandler = bridgeHandler;
67 | }
68 |
69 | /**
70 | * Public method for triggering camera discovery
71 | */
72 | public void discoverCameras() {
73 | startScan();
74 | }
75 |
76 | @Override
77 | public Set getSupportedThingTypes() {
78 | return SynoBindingConstants.SUPPORTED_THING_TYPES;
79 | }
80 |
81 | @Override
82 | protected void startScan() {
83 | if (bridgeHandler == null) {
84 | return;
85 | }
86 | // Trigger no scan if offline
87 | if (bridgeHandler.getThing().getStatus() != ThingStatus.ONLINE) {
88 | return;
89 | }
90 |
91 | try {
92 | SynoWebApiHandler apiHandler = bridgeHandler.getSynoWebApiHandler();
93 |
94 | CameraResponse response = apiHandler.getApiCamera().listCameras();
95 |
96 | if (response.isSuccess()) {
97 | JsonArray cameras = response.getCameras();
98 |
99 | ThingUID bridgeUID = bridgeHandler.getThing().getUID();
100 |
101 | if (cameras != null) {
102 | for (JsonElement camera : cameras) {
103 |
104 | if (camera.isJsonObject()) {
105 | JsonObject cam = camera.getAsJsonObject();
106 |
107 | String cameraId = cam.get("id").getAsString();
108 |
109 | CameraResponse cameraDetails = apiHandler.getApiCamera().getInfo(cameraId);
110 |
111 | ThingUID thingUID = new ThingUID(THING_TYPE_CAMERA, bridgeUID, cameraId);
112 |
113 | Map properties = cameraDetails.getCameraProperties(cameraId);
114 |
115 | DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
116 | .withProperties(properties).withBridge(bridgeHandler.getThing().getUID())
117 | .withLabel(cam.get("name").getAsString()).build();
118 |
119 | thingDiscovered(discoveryResult);
120 |
121 | logger.debug("Discovered a camera thing with ID '{}'", cameraId);
122 | }
123 | }
124 | }
125 | }
126 |
127 | } catch (WebApiException e) {
128 | if (e.getCause() instanceof javax.net.ssl.SSLHandshakeException
129 | || e.getCause() instanceof java.io.EOFException
130 | || e.getCause() instanceof java.util.concurrent.ExecutionException) {
131 | logger.error("Possible SSL certificate issue, please consider using http or enabling SSL bypass");
132 | } else if (e.getErrorCode() == 102) {
133 | logger.error("Discovery Thread; Surveillance Station is disabled or not installed");
134 | } else if (e.getErrorCode() == WebApiAuthErrorCodes.INSUFFICIENT_USER_PRIVILEGE.getCode()) {
135 | logger.debug("Discovery Thread; Wrong/expired credentials");
136 | try {
137 | bridgeHandler.reconnect(false);
138 | } catch (WebApiException ee) {
139 | logger.error("Discovery Thread; Attempt to reconnect failed");
140 | }
141 | } else {
142 | logger.error("Discovery Thread; Unexpected error: {} - {}", e.getErrorCode(), e.getMessage());
143 | }
144 | } catch (Exception npe) {
145 | logger.error("Error in WebApiException", npe);
146 | }
147 | }
148 |
149 | @Override
150 | protected void startBackgroundDiscovery() {
151 | startScan();
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/response/CameraResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.response;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.DEVICE_ID;
16 |
17 | import java.util.LinkedHashMap;
18 | import java.util.Map;
19 |
20 | import org.eclipse.jdt.annotation.NonNullByDefault;
21 | import org.eclipse.jdt.annotation.Nullable;
22 |
23 | import com.google.gson.JsonArray;
24 | import com.google.gson.JsonElement;
25 | import com.google.gson.JsonObject;
26 |
27 | /**
28 | * {@link CameraResponse} is a response for camera information
29 | *
30 | * @author Nils - Initial contribution
31 | * @author Pavion - Contribution
32 | */
33 | @NonNullByDefault
34 | public class CameraResponse extends SimpleResponse {
35 |
36 | // bits for PTZ capability
37 | // 0x001: Pan
38 | private static final int BIT_PTZ_PAN = 0;
39 | // 0x002: Tilt
40 | private static final int BIT_PTZ_TILT = 1;
41 | // 0x004: Zoom
42 | private static final int BIT_PTZ_ZOOM = 3;
43 | // 0x008: Home
44 | private static final int BIT_PTZ_HOME = 4;
45 | // 0x010: Abs position
46 | private static final int BIT_PTZ_ABS = 5;
47 | // 0x020: Focus
48 | private static final int BIT_PTZ_FOCUS = 6;
49 | // 0x040: Auto focus
50 | private static final int BIT_PTZ_AUTOFOCUS = 7;
51 | // 0x080: Iris
52 | private static final int BIT_PTZ_IRIS = 8;
53 | // 0x100: Ptz speed
54 | private static final int BIT_PTZ_SPEED = 9;
55 | // 0x200: Zoom speed
56 | private static final int BIT_PTZ_ZOOM_SPEED = 10;
57 |
58 | /**
59 | * @param jsonResponse
60 | */
61 | public CameraResponse(String jsonResponse) {
62 | super(jsonResponse);
63 | }
64 |
65 | @Nullable
66 | public JsonArray getCameras() {
67 | return getData().getAsJsonArray("cameras");
68 | }
69 |
70 | /**
71 | * If the camera is enabled
72 | *
73 | * @param cameraId
74 | */
75 | public boolean isEnabled(String cameraId) {
76 | for (JsonElement jcamera : getCameras()) {
77 | if (jcamera.isJsonObject()) {
78 | JsonObject camera = jcamera.getAsJsonObject();
79 | if (camera.get("id").getAsString().equals(cameraId)) {
80 | return camera.get("enabled").getAsBoolean();
81 | }
82 | }
83 | }
84 | return false;
85 | }
86 |
87 | /**
88 | * If the camera is recording
89 | *
90 | * @param cameraId
91 | */
92 | public boolean isRecording(String cameraId) {
93 | for (JsonElement jcamera : getCameras()) {
94 | if (jcamera.isJsonObject()) {
95 | JsonObject camera = jcamera.getAsJsonObject();
96 | if (camera.get("id").getAsString().equals(cameraId)) {
97 | return (camera.get("recStatus").getAsInt() > 0);
98 | }
99 | }
100 | }
101 | return false;
102 | }
103 |
104 | /**
105 | * Creates all relevant properties from response as key/value map.
106 | *
107 | * @param cameraId
108 | * @return
109 | */
110 | public Map getCameraProperties(String cameraId) {
111 | JsonArray cameras = this.getCameras().getAsJsonArray();
112 |
113 | for (JsonElement camera : cameras) {
114 | if (camera.isJsonObject()) {
115 | JsonObject cam = camera.getAsJsonObject();
116 |
117 | String id = cam.get("id").getAsString();
118 |
119 | if (cameraId.equals(id)) {
120 | return createProperties(cam, cameraId);
121 | }
122 | }
123 | }
124 | return new LinkedHashMap<>();
125 | }
126 |
127 | /**
128 | * Creates the thing properties from response details.
129 | *
130 | * @param cam
131 | * @param cameraId
132 | * @return
133 | */
134 | private Map createProperties(JsonObject cam, String cameraId) {
135 | Map properties = new LinkedHashMap<>();
136 |
137 | properties.put(DEVICE_ID, cameraId);
138 | properties.put(SynoApiResponse.PROP_VENDOR, cam.get(SynoApiResponse.PROP_VENDOR).getAsString());
139 | properties.put(SynoApiResponse.PROP_MODEL, cam.get(SynoApiResponse.PROP_MODEL).getAsString());
140 | properties.put(SynoApiResponse.PROP_DEVICETYPE, cam.get(SynoApiResponse.PROP_DEVICETYPE).getAsString());
141 | properties.put(SynoApiResponse.PROP_HOST, cam.get(SynoApiResponse.PROP_HOST).getAsString());
142 | properties.put(SynoApiResponse.PROP_RESOLUTION, cam.get(SynoApiResponse.PROP_RESOLUTION).getAsString());
143 | properties.put(SynoApiResponse.PROP_TYPE, cam.get(SynoApiResponse.PROP_TYPE).getAsString());
144 |
145 | // check PTZ capabilities
146 | int ptzCap = cam.get("ptzCap").getAsInt();
147 | properties.put(SynoApiResponse.PROP_PTZ, (ptzCap > 0) ? "true" : "false");
148 |
149 | if (ptzCap > 0) {
150 | properties.put(SynoApiResponse.PROP_PTZ_PAN, isBitSetAsString(ptzCap, BIT_PTZ_PAN));
151 | properties.put(SynoApiResponse.PROP_PTZ_TILT, isBitSetAsString(ptzCap, BIT_PTZ_TILT));
152 | properties.put(SynoApiResponse.PROP_PTZ_ZOOM, isBitSetAsString(ptzCap, BIT_PTZ_ZOOM));
153 | properties.put(SynoApiResponse.PROP_PTZ_HOME, isBitSetAsString(ptzCap, BIT_PTZ_HOME));
154 | properties.put(SynoApiResponse.PROP_PTZ_ABS, isBitSetAsString(ptzCap, BIT_PTZ_ABS));
155 | properties.put(SynoApiResponse.PROP_PTZ_FOCUS, isBitSetAsString(ptzCap, BIT_PTZ_FOCUS));
156 | properties.put(SynoApiResponse.PROP_PTZ_AUTOFOCUS, isBitSetAsString(ptzCap, BIT_PTZ_AUTOFOCUS));
157 | properties.put(SynoApiResponse.PROP_PTZ_IRIS, isBitSetAsString(ptzCap, BIT_PTZ_IRIS));
158 | properties.put(SynoApiResponse.PROP_PTZ_SPEED, isBitSetAsString(ptzCap, BIT_PTZ_SPEED));
159 | properties.put(SynoApiResponse.PROP_PTZ_ZOOM_SPEED, isBitSetAsString(ptzCap, BIT_PTZ_ZOOM_SPEED));
160 | }
161 |
162 | return properties;
163 | }
164 |
165 | /**
166 | * @param ptzCap
167 | * @param bit
168 | * @return
169 | */
170 | private String isBitSetAsString(int hexValue, int bit) {
171 | return Boolean.toString(isBitSet(String.valueOf(hexValue), bit));
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/SynoHandlerFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.*;
16 |
17 | import java.util.HashMap;
18 | import java.util.Hashtable;
19 | import java.util.Map;
20 |
21 | import org.eclipse.jdt.annotation.Nullable;
22 | import org.eclipse.jetty.client.HttpClient;
23 | import org.eclipse.jetty.util.ssl.SslContextFactory;
24 | import org.openhab.binding.synologysurveillancestation.handler.SynoBridgeHandler;
25 | import org.openhab.binding.synologysurveillancestation.handler.SynoCameraHandler;
26 | import org.openhab.binding.synologysurveillancestation.internal.discovery.CameraDiscoveryService;
27 | import org.openhab.binding.synologysurveillancestation.internal.discovery.SynoDynamicStateDescriptionProvider;
28 | import org.openhab.core.config.core.Configuration;
29 | import org.openhab.core.config.discovery.DiscoveryService;
30 | import org.openhab.core.io.net.http.HttpClientFactory;
31 | import org.openhab.core.thing.Bridge;
32 | import org.openhab.core.thing.Thing;
33 | import org.openhab.core.thing.ThingTypeUID;
34 | import org.openhab.core.thing.ThingUID;
35 | import org.openhab.core.thing.binding.BaseThingHandlerFactory;
36 | import org.openhab.core.thing.binding.ThingHandler;
37 | import org.openhab.core.thing.binding.ThingHandlerFactory;
38 | import org.osgi.framework.ServiceRegistration;
39 | import org.osgi.service.component.ComponentContext;
40 | import org.osgi.service.component.annotations.Activate;
41 | import org.osgi.service.component.annotations.Component;
42 | import org.osgi.service.component.annotations.Deactivate;
43 | import org.osgi.service.component.annotations.Reference;
44 | import org.slf4j.Logger;
45 | import org.slf4j.LoggerFactory;
46 |
47 | /**
48 | * The {@link SynoHandlerFactory} is responsible for creating things and thing
49 | * handlers.
50 | *
51 | * @author Nils - Initial contribution
52 | * @author Pavion - Contribution
53 | */
54 | @Component(service = ThingHandlerFactory.class, configurationPid = "binding.synologysurveillancestation")
55 | // @NonNullByDefault
56 | public class SynoHandlerFactory extends BaseThingHandlerFactory {
57 |
58 | private final Logger logger = LoggerFactory.getLogger(SynoHandlerFactory.class);
59 | private Map> discoveryServiceRegs = new HashMap<>();
60 | private HttpClient httpClient;
61 | private boolean acceptSsl = false;
62 | private SynoBridgeHandler bridgeHandler = null;
63 |
64 | private SynoDynamicStateDescriptionProvider stateDescriptionProvider;
65 |
66 | @Reference
67 | protected void setHttpClientFactory(HttpClientFactory httpClientFactory) {
68 | this.httpClient = httpClientFactory.getCommonHttpClient();
69 | }
70 |
71 | protected void unsetHttpClientFactory(HttpClientFactory httpClientFactory) {
72 | if (this.acceptSsl) {
73 | try {
74 | this.httpClient.stop();
75 | } catch (Exception e) {
76 | logger.error("Couldn't stop trusting HttpServer, sorry");
77 | }
78 | }
79 | this.httpClient = null;
80 | }
81 |
82 | @Override
83 | @Activate
84 | protected void activate(ComponentContext componentContext) {
85 | super.activate(componentContext);
86 | }
87 |
88 | @Override
89 | @Deactivate
90 | protected void deactivate(ComponentContext componentContext) {
91 | super.deactivate(componentContext);
92 | }
93 |
94 | @Override
95 | public boolean supportsThingType(ThingTypeUID thingTypeUID) {
96 | return SUPPORTED_THING_TYPES.contains(thingTypeUID);
97 | }
98 |
99 | @Override
100 | public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration,
101 | @Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) {
102 | if (SUPPORTED_BRIDGE_TYPES.contains(thingTypeUID)) {
103 | return super.createThing(thingTypeUID, configuration, thingUID, null);
104 | } else if (SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
105 | ThingUID myUID = thingUID;
106 | if (myUID == null) {
107 | myUID = new ThingUID(thingTypeUID, "camera");
108 | }
109 | return super.createThing(thingTypeUID, configuration, myUID, bridgeUID);
110 | }
111 | throw new IllegalArgumentException("The thing type " + thingTypeUID + " is not supported by this binding.");
112 | }
113 |
114 | @Override
115 | protected @Nullable ThingHandler createHandler(Thing thing) {
116 | ThingTypeUID thingTypeUID = thing.getThingTypeUID();
117 |
118 | if (thingTypeUID.equals(THING_TYPE_STATION)) {
119 | SynoConfig config = thing.getConfiguration().as(SynoConfig.class);
120 | if (config.isAcceptSsl()) {
121 | SslContextFactory sslContextFactory = new SslContextFactory(true);
122 | sslContextFactory.setTrustAll(true);
123 | sslContextFactory.setEndpointIdentificationAlgorithm(null);
124 | HttpClient client = new HttpClient(sslContextFactory);
125 | try {
126 | client.start();
127 | this.httpClient = client;
128 | logger.debug("Trusting HttpServer started");
129 | this.acceptSsl = true;
130 | } catch (Exception e) {
131 | logger.error("Trusting HttpServer failed");
132 | this.acceptSsl = false;
133 | }
134 | }
135 | bridgeHandler = new SynoBridgeHandler((Bridge) thing, httpClient);
136 | CameraDiscoveryService discoveryService = new CameraDiscoveryService(bridgeHandler);
137 | bridgeHandler.setDiscovery(discoveryService);
138 | this.discoveryServiceRegs.put(thing.getUID(), bundleContext.registerService(
139 | DiscoveryService.class.getName(), discoveryService, new Hashtable()));
140 |
141 | return bridgeHandler;
142 |
143 | } else if (thingTypeUID.equals(THING_TYPE_CAMERA) && bridgeHandler != null) {
144 | return new SynoCameraHandler(thing, stateDescriptionProvider);
145 | }
146 | return null;
147 | }
148 |
149 | @Override
150 | protected void removeHandler(ThingHandler handler) {
151 | if (handler.getThing().getThingTypeUID().equals(THING_TYPE_STATION)) {
152 | ServiceRegistration> serviceReg = this.discoveryServiceRegs.get(handler.getThing().getUID());
153 | if (serviceReg != null) {
154 | serviceReg.unregister();
155 | discoveryServiceRegs.remove(handler.getThing().getUID());
156 | bridgeHandler = null;
157 | }
158 | }
159 | super.removeHandler(handler);
160 | }
161 |
162 | @Reference
163 | protected void setDynamicStateDescriptionProvider(SynoDynamicStateDescriptionProvider stateDescriptionProvider) {
164 | this.stateDescriptionProvider = stateDescriptionProvider;
165 | }
166 |
167 | protected void unsetDynamicStateDescriptionProvider(SynoDynamicStateDescriptionProvider stateDescriptionProvider) {
168 | this.stateDescriptionProvider = null;
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/SynoWebApiHandler.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi;
14 |
15 | import java.util.HashMap;
16 |
17 | import org.eclipse.jdt.annotation.NonNullByDefault;
18 | import org.eclipse.jetty.client.HttpClient;
19 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
20 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
21 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiAuth;
22 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiCamera;
23 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiCameraEvent;
24 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiEvent;
25 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiExternalEvent;
26 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiExternalRecording;
27 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiHomeMode;
28 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiInfo;
29 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiLiveUri;
30 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiPTZ;
31 | import org.openhab.binding.synologysurveillancestation.internal.webapi.request.SynoApiRequest;
32 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.AuthResponse;
33 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.SimpleResponse;
34 |
35 | /**
36 | * The {@link SynoWebApiHandler} is a facade for Synology Surveillance Station Web API.
37 | *
38 | * @author Nils - Initial contribution
39 | * @author Pavion - Contribution
40 | */
41 | @NonNullByDefault
42 | public class SynoWebApiHandler implements SynoWebApi {
43 |
44 | private SynoConfig config;
45 | private String sessionID = "";
46 |
47 | private final HashMap, SynoApiRequest>> api = new HashMap<>();
48 |
49 | /**
50 | * @param config
51 | */
52 | public SynoWebApiHandler(SynoConfig config, HttpClient httpClient) {
53 | this.config = config;
54 | api.put(SynoApiAuth.class, new SynoApiAuth(config, httpClient));
55 | api.put(SynoApiInfo.class, new SynoApiInfo(config, httpClient));
56 | api.put(SynoApiCamera.class, new SynoApiCamera(config, httpClient));
57 | api.put(SynoApiEvent.class, new SynoApiEvent(config, httpClient));
58 | api.put(SynoApiHomeMode.class, new SynoApiHomeMode(config, httpClient));
59 | api.put(SynoApiExternalRecording.class, new SynoApiExternalRecording(config, httpClient));
60 | api.put(SynoApiPTZ.class, new SynoApiPTZ(config, httpClient));
61 | api.put(SynoApiLiveUri.class, new SynoApiLiveUri(config, httpClient));
62 | api.put(SynoApiExternalEvent.class, new SynoApiExternalEvent(config, httpClient));
63 | api.put(SynoApiCameraEvent.class, new SynoApiCameraEvent(config, httpClient));
64 | }
65 |
66 | /**
67 | * @return
68 | */
69 | public SynoConfig getConfig() {
70 | return config;
71 | }
72 |
73 | /**
74 | * @return
75 | */
76 | public void setConfig(SynoConfig config) {
77 | this.config = config;
78 | for (SynoApiRequest> r : api.values()) {
79 | r.setConfig(config);
80 | }
81 | }
82 |
83 | /**
84 | * @return
85 | */
86 | public void setSessionID(String sessionID) {
87 | this.sessionID = sessionID;
88 | for (SynoApiRequest> r : api.values()) {
89 | r.setSessionId(sessionID);
90 | }
91 | }
92 |
93 | /**
94 | * @return
95 | */
96 | public String getSessionID() {
97 | return sessionID;
98 | }
99 |
100 | /*
101 | * (non-Javadoc)
102 | *
103 | * @see org.openhab.binding.synologysurveillancestation.internal.webapi.SynoWebApi#connect()
104 | */
105 | @Override
106 | public boolean connect(boolean forceLogout) throws WebApiException {
107 | AuthResponse response = getApiAuth().login();
108 | if (response.isSuccess()) {
109 | String sid = response.getSid();
110 | setSessionID(sid);
111 | return true;
112 | } else {
113 | throw new WebApiException(WebApiAuthErrorCodes.getByCode(response.getErrorcode()));
114 | }
115 | }
116 |
117 | /*
118 | * (non-Javadoc)
119 | *
120 | * @see org.eclipse.smarthome.binding.synologysurveillancestation.internal.webapi.SynoWebApi#disconnect()
121 | */
122 | @Override
123 | public SimpleResponse disconnect() throws WebApiException {
124 | SimpleResponse response = getApiAuth().logout(sessionID);
125 | setSessionID("");
126 |
127 | if (response.isSuccess()) {
128 | return response;
129 | } else {
130 | throw new WebApiException(WebApiAuthErrorCodes.getByCode(response.getErrorcode()));
131 | }
132 | }
133 |
134 | @Override
135 | public boolean isConnected() {
136 | return (!this.sessionID.isBlank());
137 | }
138 |
139 | /**
140 | * @return the apiCameraEvent
141 | */
142 | public SynoApiCameraEvent getApiCameraEvent() {
143 | return getApi(SynoApiCameraEvent.class);
144 | }
145 |
146 | /**
147 | * @return the apiPTZ
148 | */
149 | public SynoApiPTZ getApiPTZ() {
150 | return getApi(SynoApiPTZ.class);
151 | }
152 |
153 | /**
154 | * @return the apiCamera
155 | */
156 | public SynoApiCamera getApiCamera() {
157 | return getApi(SynoApiCamera.class);
158 | }
159 |
160 | /**
161 | * @return the apiExternalRecording
162 | */
163 | public SynoApiExternalRecording getApiExternalRecording() {
164 | return getApi(SynoApiExternalRecording.class);
165 | }
166 |
167 | /**
168 | * @return the apiInfo
169 | */
170 | public SynoApiInfo getApiInfo() {
171 | return getApi(SynoApiInfo.class);
172 | }
173 |
174 | /**
175 | * @return the apiEvent
176 | */
177 | public SynoApiEvent getApiEvent() {
178 | return getApi(SynoApiEvent.class);
179 | }
180 |
181 | /**
182 | * @return the apiHomeMode
183 | */
184 | public SynoApiHomeMode getApiHomeMode() {
185 | return getApi(SynoApiHomeMode.class);
186 | }
187 |
188 | /**
189 | * @return the apiLiveUri
190 | */
191 | public SynoApiLiveUri getApiLiveUri() {
192 | return getApi(SynoApiLiveUri.class);
193 | }
194 |
195 | /**
196 | * @return the apiExternalEvent
197 | */
198 | public SynoApiExternalEvent getApiExternalEvent() {
199 | return getApi(SynoApiExternalEvent.class);
200 | }
201 |
202 | /**
203 | * @return the apiLiveUri
204 | */
205 | public SynoApiAuth getApiAuth() {
206 | return getApi(SynoApiAuth.class);
207 | }
208 |
209 | /**
210 | * Generic getter
211 | *
212 | * @param cl
213 | * @return
214 | */
215 | @SuppressWarnings("unchecked")
216 | private > T getApi(Class cl) {
217 | return (T) api.get(cl);
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiRequest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.io.IOException;
16 | import java.io.UnsupportedEncodingException;
17 | import java.lang.reflect.Constructor;
18 | import java.lang.reflect.InvocationTargetException;
19 | import java.lang.reflect.ParameterizedType;
20 | import java.net.MalformedURLException;
21 | import java.net.URI;
22 | import java.net.URISyntaxException;
23 | import java.util.HashMap;
24 | import java.util.Map;
25 | import java.util.concurrent.ExecutionException;
26 | import java.util.concurrent.TimeoutException;
27 |
28 | import org.eclipse.jdt.annotation.NonNullByDefault;
29 | import org.eclipse.jetty.client.HttpClient;
30 | import org.eclipse.jetty.client.api.ContentResponse;
31 | import org.eclipse.jetty.client.api.Request;
32 | import org.eclipse.jetty.util.URIUtil;
33 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
34 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
35 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.SynoApiResponse;
36 | import org.slf4j.Logger;
37 | import org.slf4j.LoggerFactory;
38 |
39 | /**
40 | * API request
41 | *
42 | * @param
43 | *
44 | * @author Nils - Initial contribution
45 | * @author Pavion - Contribution
46 | */
47 | @NonNullByDefault
48 | public abstract class SynoApiRequest implements SynoApi {
49 | private final Logger logger = LoggerFactory.getLogger(SynoApiRequest.class);
50 |
51 | protected static final String API_TRUE = Boolean.TRUE.toString();
52 | protected static final String API_FALSE = Boolean.FALSE.toString();
53 |
54 | private final SynoApiConfig apiConfig;
55 | private final HttpClient httpClient;
56 | private SynoConfig config;
57 | private String sessionId = "";
58 |
59 | final Class typeParameterClass;
60 |
61 | /**
62 | * @param apiConfig
63 | * @param config
64 | * @param sessionId
65 | */
66 | @SuppressWarnings({ "unchecked", "rawtypes" })
67 | public SynoApiRequest(SynoApiConfig apiConfig, SynoConfig config, HttpClient httpClient) {
68 | super();
69 |
70 | this.typeParameterClass = ((Class) ((ParameterizedType) getClass().getGenericSuperclass())
71 | .getActualTypeArguments()[0]);
72 |
73 | this.httpClient = httpClient;
74 | this.apiConfig = apiConfig;
75 | this.config = config;
76 | }
77 |
78 | /*
79 | * (non-Javadoc)
80 | *
81 | * @see org.openhab.binding.synologysurveillancestation.internal.webapi.SynoApi#getApiConfig()
82 | */
83 | @Override
84 | public SynoApiConfig getApiConfig() {
85 | return apiConfig;
86 | }
87 |
88 | /**
89 | * @return
90 | */
91 | protected SynoConfig getConfig() {
92 | return config;
93 | }
94 |
95 | /**
96 | * @return
97 | */
98 | public void setConfig(SynoConfig config) {
99 | this.config = config;
100 | }
101 |
102 | /**
103 | *
104 | * @param sessionId
105 | */
106 | public void setSessionId(String sessionId) {
107 | this.sessionId = sessionId;
108 | }
109 |
110 | /**
111 | * @return
112 | */
113 | protected String getSessionId() {
114 | return sessionId;
115 | }
116 |
117 | /**
118 | * Creates an URIBuilder to build the url.
119 | *
120 | * @return
121 | */
122 | protected URI getWebApiUrlBuilder() throws URISyntaxException {
123 | StringBuilder sb = URIUtil.newURIBuilder(getConfig().getProtocol(), getConfig().getHost(),
124 | getConfig().getPort());
125 | URI uri = new URI(sb.toString());
126 | uri = URIUtil.addPath(uri, apiConfig.getScriptpath());
127 | return uri;
128 | }
129 |
130 | /**
131 | * Calls the method.
132 | *
133 | * @param method
134 | * @return
135 | * @throws WebApiException
136 | */
137 | protected T callApi(String method) throws WebApiException {
138 | return callApi(method, new HashMap<>());
139 | }
140 |
141 | /**
142 | * Calls the method with the passed parameters.
143 | *
144 | * @param method
145 | * @param params
146 | * @return
147 | * @throws WebApiException
148 | */
149 | protected T callApi(String method, Map params) throws WebApiException {
150 | Request request = getWebApiUrl(method, params);
151 | return callWebApi(request);
152 | }
153 |
154 | /**
155 | * Builds the url for api.
156 | *
157 | * @param method
158 | * @param params
159 | * @return
160 | * @throws MalformedURLException
161 | * @throws URISyntaxException
162 | */
163 | protected Request getWebApiUrl(String method, Map params) throws WebApiException {
164 | try {
165 | URI uri = getWebApiUrlBuilder();
166 |
167 | Request request = httpClient.newRequest(uri);
168 |
169 | // API data
170 | request.param("api", apiConfig.getName());
171 | request.param("version", apiConfig.getVersion());
172 |
173 | // API method
174 | request.param("method", method);
175 |
176 | // API session
177 | request.param("_sid", getSessionId());
178 |
179 | if (!params.isEmpty()) {
180 | for (String key : params.keySet()) {
181 | request.param(key, params.get(key));
182 | }
183 | }
184 |
185 | return request;
186 |
187 | } catch (URISyntaxException | UnsupportedOperationException e) {
188 | throw new WebApiException(e);
189 | }
190 | }
191 |
192 | /**
193 | * E
194 | *
195 | * @param apiurl
196 | * @return
197 | * @throws WebApiException
198 | * @throws URISyntaxException
199 | * @throws UnsupportedOperationException
200 | * @throws IOException
201 | */
202 | protected synchronized T callWebApi(Request request) throws WebApiException {
203 | try {
204 | if (logger.isDebugEnabled()) {
205 | logger.debug("URI: {}", request.getURI().toString());
206 | }
207 | ContentResponse response = request.send();
208 |
209 | if (response.getStatus() == 200) {
210 | byte[] rawResponse = response.getContent();
211 | String encoding = response.getEncoding().replaceAll("\"", "").trim();
212 | String result = new String(rawResponse, encoding);
213 |
214 | if (result.length() > 0) {
215 | if (result.contains("\"success\":true")) {
216 | logger.debug("RESPONSE: {}", result);
217 | } else {
218 | logger.error("RESPONSE: {}", result);
219 | }
220 |
221 | }
222 |
223 | Constructor ctor = typeParameterClass.getConstructor(String.class);
224 |
225 | T vo = ctor.newInstance(new Object[] { result });
226 |
227 | return vo;
228 |
229 | } else {
230 | throw new WebApiException("Error calling Surveillance Station WebApi!");
231 | }
232 |
233 | } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
234 | | NoSuchMethodException | SecurityException | ExecutionException | TimeoutException
235 | | InterruptedException e) {
236 | throw new WebApiException(e);
237 | } catch (UnsupportedEncodingException ee) {
238 | throw new WebApiException(ee);
239 | }
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/thread/SynoApiThread.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.thread;
14 |
15 | import java.util.concurrent.ScheduledExecutorService;
16 | import java.util.concurrent.ScheduledFuture;
17 | import java.util.concurrent.TimeUnit;
18 | import java.util.concurrent.atomic.AtomicBoolean;
19 |
20 | import org.eclipse.jdt.annotation.NonNullByDefault;
21 | import org.eclipse.jdt.annotation.Nullable;
22 | import org.openhab.binding.synologysurveillancestation.handler.SynoBridgeHandler;
23 | import org.openhab.binding.synologysurveillancestation.handler.SynoCameraHandler;
24 | import org.openhab.binding.synologysurveillancestation.handler.SynoHandler;
25 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
26 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
27 | import org.openhab.core.thing.ThingStatus;
28 | import org.openhab.core.thing.ThingStatusDetail;
29 | import org.openhab.core.thing.binding.BaseThingHandler;
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 |
33 | /**
34 | * The {@link SynoApiThread} is an abstract class for thread management (events, snapshot and so on)
35 | *
36 | * @author Pavion - Initial contribution
37 | */
38 | @NonNullByDefault
39 | public abstract class SynoApiThread {
40 | private final Logger logger = LoggerFactory.getLogger(SynoApiThread.class);
41 |
42 | /**
43 | * Thread types. Each thread has its refresh rate and polls the Station for events
44 | */
45 | public static final String THREAD_SNAPSHOT = "Snapshot";
46 | public static final String THREAD_EVENT = "Event";
47 | public static final String THREAD_CAMERA = "Camera";
48 | public static final String THREAD_HOMEMODE = "HomeMode";
49 | public static final String THREAD_LIVEURI = "LiveUri";
50 | public static final String THREAD_CAMERAEVENT = "CameraEvent";
51 |
52 | private final AtomicBoolean refreshInProgress = new AtomicBoolean(false);
53 | private @Nullable ScheduledFuture> future;
54 | private int refreshRate; // Refresh rate in seconds
55 | private final T synoHandler; // Bridge or Camera Thing handler
56 | private final String name; // Thread name / type
57 | private final String deviceId; // Thread name / type
58 |
59 | /**
60 | * Defines a runnable for a refresh job
61 | */
62 | private Runnable runnable = new Runnable() {
63 | @Override
64 | public void run() {
65 | try {
66 | if (refreshInProgress.compareAndSet(false, true)) {
67 | runOnce();
68 | refreshInProgress.set(false);
69 | }
70 | } catch (IllegalStateException e) {
71 | logger.debug("Thread {}: Refreshing Thing failed, handler might be OFFLINE", name);
72 | } catch (Exception e) {
73 | logger.error("Thread {}: Unknown error", name, e);
74 | }
75 | }
76 | };
77 |
78 | /**
79 | * Main constructor
80 | *
81 | * @param threadId ID of this thread for logging purposes
82 | * @param refreshRate Refresh rate of this thread in seconds
83 | * @param handler Camera or bridge handler
84 | */
85 | public SynoApiThread(String name, T synoHandler, int refreshRate) {
86 | this.name = name;
87 | this.synoHandler = synoHandler;
88 | this.refreshRate = refreshRate;
89 | this.deviceId = synoHandler.getThing().getProperties().getOrDefault("deviceID", "Bridge");
90 | }
91 |
92 | /**
93 | * Starts the refresh job
94 | */
95 | public void start() {
96 | if (refreshRate > 0) {
97 | ScheduledExecutorService scheduler = synoHandler.getScheduler();
98 |
99 | if (scheduler != null) {
100 | if (this.name == THREAD_SNAPSHOT) {
101 | future = scheduler.scheduleAtFixedRate(runnable, 0, refreshRate, TimeUnit.SECONDS);
102 | } else {
103 | future = scheduler.scheduleWithFixedDelay(runnable, 0, refreshRate, TimeUnit.SECONDS);
104 | }
105 | }
106 | }
107 | }
108 |
109 | /**
110 | * Stops the refresh job
111 | */
112 | public void stop() {
113 | if (future != null) {
114 | future.cancel(false);
115 | try {
116 | Thread.sleep(1000);
117 | } catch (InterruptedException e) {
118 | }
119 | }
120 | }
121 |
122 | /**
123 | * Abstract dummy for a refresh function
124 | */
125 | public abstract boolean refresh() throws Exception;
126 |
127 | /**
128 | * Run the runnable just once (for manual refresh)
129 | */
130 | public void runOnce() {
131 | if (isNeeded()) {
132 | logger.debug("Thread {} tick", name);
133 | boolean success = false;
134 | try {
135 | success = refresh();
136 | } catch (WebApiException e) {
137 | if (e.getCause() instanceof java.util.concurrent.TimeoutException) {
138 | logger.debug(
139 | "DeviceId: {}; {} API timeout, consider to increase refresh rate ({} s) if seen frequently",
140 | deviceId, name, refreshRate);
141 | success = true;
142 | } else if (e.getErrorCode() == WebApiAuthErrorCodes.INSUFFICIENT_USER_PRIVILEGE.getCode()
143 | || e.getErrorCode() == WebApiAuthErrorCodes.UNKNOWN_ERROR_119.getCode()) {
144 | logger.debug("DeviceId: {}; Thread: {}; SID expired, trying to reconnect", deviceId, name);
145 | try {
146 | getSynoHandler().reconnect(true);
147 | } catch (WebApiException ee) {
148 | logger.error("DeviceId: {}; Thread: {}; Attempt to reconnect failed", deviceId, name);
149 | }
150 | } else {
151 | logger.error(
152 | "DeviceId: {}; Thread: {}; Handler gone offline (Surveillance Station probably disabled)",
153 | deviceId, name);
154 | }
155 | } catch (Exception e) {
156 | logger.error("DeviceId: {}; Thread: {}; Critical error:\n", deviceId, name, e);
157 | }
158 |
159 | updateStatus(success);
160 | }
161 | }
162 |
163 | /**
164 | * Update handler status on runnable feedback
165 | *
166 | * @param success if runnable was successful
167 | */
168 | private void updateStatus(boolean success) {
169 | if (success && !synoHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
170 | if (synoHandler instanceof SynoCameraHandler) {
171 | ((SynoCameraHandler) synoHandler).updateStatus(ThingStatus.ONLINE);
172 | } else if (synoHandler instanceof SynoBridgeHandler) {
173 | ((SynoBridgeHandler) synoHandler).updateStatus(ThingStatus.ONLINE);
174 | }
175 | } else if (!success && synoHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
176 | if (synoHandler instanceof SynoCameraHandler) {
177 | ((SynoCameraHandler) synoHandler).updateStatus(ThingStatus.OFFLINE,
178 | ThingStatusDetail.COMMUNICATION_ERROR, "Thread " + name);
179 | } else if (synoHandler instanceof SynoBridgeHandler) {
180 | ((SynoBridgeHandler) synoHandler).updateStatus(ThingStatus.OFFLINE,
181 | ThingStatusDetail.COMMUNICATION_ERROR, "Thread " + name);
182 | }
183 | }
184 | }
185 |
186 | /**
187 | * @return the refreshRate
188 | */
189 | public int getRefreshRate() {
190 | return refreshRate;
191 | }
192 |
193 | /**
194 | * @param refreshRate The refreshRate to be set
195 | */
196 | public void setRefreshRate(int refreshRate) {
197 | if (this.refreshRate != refreshRate) {
198 | this.refreshRate = refreshRate;
199 | stop();
200 | start();
201 | }
202 | }
203 |
204 | /**
205 | * @return the SynoCameraHandler
206 | */
207 | public T getSynoHandler() {
208 | return synoHandler;
209 | }
210 |
211 | /**
212 | * @return if thread has to be run
213 | */
214 | public abstract boolean isNeeded();
215 | }
216 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiCamera.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import java.io.IOException;
16 | import java.net.URISyntaxException;
17 | import java.util.HashMap;
18 | import java.util.Map;
19 | import java.util.concurrent.ExecutionException;
20 | import java.util.concurrent.TimeUnit;
21 | import java.util.concurrent.TimeoutException;
22 |
23 | import org.eclipse.jdt.annotation.NonNullByDefault;
24 | import org.eclipse.jetty.client.HttpClient;
25 | import org.eclipse.jetty.client.api.ContentResponse;
26 | import org.eclipse.jetty.client.api.Request;
27 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
28 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
29 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
30 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.CameraResponse;
31 | import org.slf4j.Logger;
32 | import org.slf4j.LoggerFactory;
33 |
34 | /**
35 | * SYNO.SurveillanceStation.Camera
36 | *
37 | * This API provides a set of methods to acquire camera-related information and to enable/disable cameras.
38 | *
39 | * Method:
40 | * - Save
41 | * - List
42 | * - GetInfo
43 | * - ListGroup
44 | * - GetSnapshot
45 | * - Enable
46 | * - Disable
47 | * - GetCapabilityByCamId
48 | * - MigrationEnum
49 | * - Migrate
50 | * - CountByCategory
51 | * - RecountEventSize
52 | * - SaveOptimizeParam
53 | * - GetOccupiedSize
54 | * - CheckCamValid
55 | * - MigrationCancel
56 | * - Delete
57 | * - GetLiveViewPath
58 | *
59 | * @author Nils - Initial contribution
60 | * @author Pavion - Contribution
61 | *
62 | */
63 | @NonNullByDefault
64 | public class SynoApiCamera extends SynoApiRequest {
65 | private final Logger logger = LoggerFactory.getLogger(SynoApiCamera.class);
66 |
67 | // API configuration
68 | private static final String API_NAME = "SYNO.SurveillanceStation.Camera";
69 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_08, API_SCRIPT_ENTRY);
70 |
71 | /**
72 | * @param config
73 | */
74 | public SynoApiCamera(SynoConfig config, HttpClient httpClient) {
75 | super(API_CONFIG, config, httpClient);
76 | }
77 |
78 | /**
79 | * Calls the passed method for all cameras.
80 | *
81 | * @param method
82 | * @return
83 | * @throws WebApiException
84 | */
85 | private CameraResponse call(String method) throws WebApiException {
86 | return call(method, "");
87 | }
88 |
89 | /**
90 | * Calls the passed method.
91 | *
92 | * @param method
93 | * @param cameraId
94 | * @return
95 | * @throws WebApiException
96 | */
97 | private CameraResponse call(String method, String cameraId) throws WebApiException {
98 | Map params = new HashMap<>();
99 |
100 | // API parameters
101 | params.put("blFromCamList", API_TRUE);
102 | params.put("privCamType", API_TRUE);
103 | params.put("blIncludeDeletedCam", API_FALSE);
104 | params.put("basic", API_TRUE);
105 | params.put("streamInfo", API_TRUE);
106 | params.put("blPrivilege", API_FALSE);
107 | params.put("cameraIds", cameraId);
108 |
109 | return callApi(method, params);
110 | }
111 |
112 | /**
113 | * Get the up-to-date snapshot of the selected camera in JPEG format.
114 | *
115 | * @throws WebApiException
116 | * @throws IOException
117 | * @throws UnsupportedOperationException
118 | * @throws URISyntaxException
119 | *
120 | */
121 | public byte[] getSnapshot(String cameraId, int timeout, int streamId)
122 | throws IOException, URISyntaxException, WebApiException {
123 | try {
124 | Map params = new HashMap<>();
125 |
126 | // API parameters
127 | params.put("cameraId", cameraId);
128 | params.put("camStm", String.valueOf(streamId));
129 |
130 | Request request = getWebApiUrl(METHOD_GETSNAPSHOT, params);
131 |
132 | long responseTime = System.currentTimeMillis();
133 |
134 | ContentResponse response = request.timeout(timeout, TimeUnit.SECONDS).send();
135 |
136 | responseTime = System.currentTimeMillis() - responseTime;
137 | byte[] ret = new byte[0];
138 | if (response.getStatus() == 200) {
139 | ret = response.getContent();
140 | if (ret.length < 200) {
141 | String error = new String(ret);
142 | if (error.contains("\"success\":false")) {
143 | if (error.contains("{\"code\":400}")) {
144 | logger.debug("Device: {}, API response time: {} ms, execution failed", cameraId,
145 | responseTime);
146 | return new byte[0];
147 | } else if (error.contains("{\"code\":401}")) {
148 | logger.debug("Device: {}, API response time: {} ms, parameter invalid", cameraId,
149 | responseTime);
150 | return new byte[0];
151 | } else if (error.contains("{\"code\":402}")) {
152 | logger.trace("Device: {}, API response time: {} ms, camera disabled", cameraId,
153 | responseTime);
154 | return new byte[2];
155 | } else if (error.contains("{\"code\":407}")) {
156 | logger.debug("Device: {}, API response time: {} ms, CMS closed", cameraId, responseTime);
157 | return new byte[0];
158 | } else {
159 | logger.trace("Device: {}, API response time: {} ms, unexpected response: {}", cameraId,
160 | responseTime, error);
161 | throw new WebApiException(WebApiAuthErrorCodes.INSUFFICIENT_USER_PRIVILEGE);
162 | }
163 | }
164 | }
165 | }
166 | logger.trace("Device: {}, API response time: {} ms, stream id: {}", cameraId, responseTime, streamId);
167 | return ret;
168 | } catch (IllegalArgumentException | SecurityException | ExecutionException | TimeoutException
169 | | InterruptedException e) {
170 | throw new WebApiException(e);
171 | }
172 | }
173 |
174 | /**
175 | * Get snapshot URI of the selected camera
176 | *
177 | * @throws WebApiException
178 | * @throws IOException
179 | * @throws UnsupportedOperationException
180 | * @throws URISyntaxException
181 | *
182 | */
183 | public String getSnapshotUri(String cameraId, int streamId) throws WebApiException {
184 | try {
185 | Map params = new HashMap<>();
186 |
187 | // API parameters
188 | params.put("cameraId", cameraId);
189 | params.put("camStm", String.valueOf(streamId));
190 |
191 | Request request = getWebApiUrl(METHOD_GETSNAPSHOT, params);
192 | return request.getURI().toString();
193 | } catch (Exception e) {
194 | throw new WebApiException(e);
195 | }
196 | }
197 |
198 | /**
199 | * Get the list of all cameras.
200 | *
201 | * @return
202 | * @throws WebApiException
203 | */
204 | public CameraResponse listCameras() throws WebApiException {
205 | CameraResponse response = call(METHOD_LIST);
206 |
207 | if (!response.isSuccess()) {
208 | throw new WebApiException(WebApiAuthErrorCodes.getByCode(response.getErrorcode()));
209 | }
210 |
211 | return response;
212 | }
213 |
214 | /**
215 | * Get specific camera settings.
216 | *
217 | * @return
218 | * @throws WebApiException
219 | */
220 | public CameraResponse getInfo(String cameraId) throws WebApiException {
221 | CameraResponse response = call(METHOD_GETINFO, cameraId);
222 |
223 | if (!response.isSuccess()) {
224 | throw new WebApiException(WebApiAuthErrorCodes.getByCode(response.getErrorcode()));
225 | }
226 |
227 | return response;
228 | }
229 |
230 | /**
231 | * Toggle camera.
232 | *
233 | * @param cameraId
234 | * @return
235 | * @throws WebApiException
236 | */
237 | public CameraResponse toggleCamera(String cameraId, boolean on) throws WebApiException {
238 | Map params = new HashMap<>();
239 | params.put("cameraIds", cameraId);
240 |
241 | return callApi(on ? METHOD_ENABLE : METHOD_DISABLE, params);
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/src/main/resources/OH-INF/thing/camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Synology Surveillance Station Camera
13 | Camera
14 |
15 |
16 |
17 |
18 |
19 |
20 | Events of your camera
21 |
22 |
23 |
24 | Motion detection parameters
25 |
26 |
27 |
28 |
29 |
30 |
31 | Common settings
32 | false
33 |
34 |
35 |
36 | Refresh rate settings
37 | true
38 |
39 |
40 |
41 |
42 | Refresh rate for camera snapshot in seconds (0 to disable)
43 | 10
44 | true
45 |
46 |
47 |
48 | Refresh rate for events in seconds (0 to disable)
49 | 3
50 | true
51 |
52 |
53 |
54 | Refresh rate for motion detection parameter in seconds (0 to disable)
55 | 0
56 | true
57 |
58 |
59 |
60 | Snapshot video stream ID according to Surveillance Station configuration (default: 1 for 'Stream 1')
61 | 1
62 | true
63 |
64 |
65 |
66 |
67 |
68 |
69 | Common channels of your camera
70 |
71 |
72 |
73 |
74 |
75 |
76 | Dynamic URL of the current snapshot (events refresh rate)
77 |
78 |
79 |
80 | Static URL of the current snapshot
81 |
82 |
83 |
84 | Live feed URI (rtsp)
85 |
86 |
87 |
88 | Live feed URI (mjpeg over http)
89 |
90 |
91 |
92 |
93 |
94 |
95 | Pan/Tilt/Zoom channels of your camera
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | Event of your camera
107 |
108 |
109 |
110 | Last motion event
111 |
112 |
113 |
114 | Last alarm event
115 |
116 |
117 |
118 | Last manual event
119 |
120 |
121 |
122 | Last external event
123 |
124 |
125 |
126 | Last continuous event
127 |
128 |
129 |
130 | Last action rule event
131 |
132 |
133 |
134 |
135 |
136 |
137 | Motion detection parameters
138 |
139 |
140 |
141 | Motion detection source. -1:disable, 0:by camera, 1:by Surveillance Station
142 |
143 |
144 |
145 | Motion detection sensitivity (1 to 99)
146 |
147 |
148 |
149 | Motion detection threshold (1 to 99)
150 |
151 |
152 |
153 | Motion detection object size (1 to 99)
154 |
155 |
156 |
157 | Motion detection percentage (1 to 99)
158 |
159 |
160 |
161 | Ignore short-lived motion for duration of (0 to 10) seconds
162 |
163 |
164 |
165 |
166 |
167 | Image
168 |
169 | Current snapshot of your camera
170 |
171 |
172 | Switch
173 |
174 | Enable or disable your camera
175 |
176 |
177 | Switch
178 |
179 | Start manual recording
180 |
181 |
182 | String
183 |
184 | Current snapshot/live URI of your camera
185 |
186 |
187 |
188 | String
189 |
190 | Zooming channel for your camera
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 | String
200 |
201 | Moving channel for your camera
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 | String
215 |
216 | Move the camera lens to a pre-defined preset position.
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 | String
226 |
227 | Force the camera to execute the specific patrol.
228 |
229 |
230 |
231 | Switch
232 |
233 | Event was detected
234 |
235 |
236 |
237 | Switch
238 |
239 | Event was detected
240 |
241 |
242 |
243 | String
244 |
245 | Motion detection source. -1:disable, 0:by camera, 1:by Surveillance Station
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 | Number
256 |
257 | Motion detection parameter
258 |
259 |
260 |
261 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/handler/SynoBridgeHandler.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.handler;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.*;
16 |
17 | import java.util.HashMap;
18 | import java.util.Map;
19 | import java.util.Map.Entry;
20 | import java.util.concurrent.ScheduledExecutorService;
21 | import java.util.concurrent.atomic.AtomicBoolean;
22 |
23 | import org.eclipse.jdt.annotation.NonNullByDefault;
24 | import org.eclipse.jdt.annotation.Nullable;
25 | import org.eclipse.jetty.client.HttpClient;
26 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
27 | import org.openhab.binding.synologysurveillancestation.internal.discovery.CameraDiscoveryService;
28 | import org.openhab.binding.synologysurveillancestation.internal.thread.SynoApiThread;
29 | import org.openhab.binding.synologysurveillancestation.internal.thread.SynoApiThreadHomeMode;
30 | import org.openhab.binding.synologysurveillancestation.internal.webapi.SynoWebApiHandler;
31 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
32 | import org.openhab.core.config.core.Configuration;
33 | import org.openhab.core.library.types.DecimalType;
34 | import org.openhab.core.library.types.StringType;
35 | import org.openhab.core.thing.Bridge;
36 | import org.openhab.core.thing.ChannelUID;
37 | import org.openhab.core.thing.ThingStatus;
38 | import org.openhab.core.thing.ThingStatusDetail;
39 | import org.openhab.core.thing.binding.BaseBridgeHandler;
40 | import org.openhab.core.types.Command;
41 | import org.openhab.core.types.RefreshType;
42 | import org.openhab.core.types.State;
43 | import org.openhab.core.types.UnDefType;
44 | import org.slf4j.Logger;
45 | import org.slf4j.LoggerFactory;
46 |
47 | /**
48 | * The {@link SynoBridgeHandler} is a Bridge handler for the Synology Surveillance Station
49 | *
50 | * @author Nils - Initial contribution
51 | * @author Pavion - Contribution
52 | */
53 | @NonNullByDefault
54 | public class SynoBridgeHandler extends BaseBridgeHandler implements SynoHandler {
55 |
56 | private final Logger logger = LoggerFactory.getLogger(SynoBridgeHandler.class);
57 | private @Nullable CameraDiscoveryService discoveryService;
58 | private final SynoWebApiHandler apiHandler;
59 | private final Map> threads = new HashMap<>();
60 | private final AtomicBoolean refreshInProgress = new AtomicBoolean(false);
61 | private SynoConfig config = new SynoConfig();
62 |
63 | /**
64 | * Defines a runnable for a discovery
65 | */
66 | Runnable runnable = new Runnable() {
67 | @Override
68 | public void run() {
69 | if (discoveryService != null) {
70 | discoveryService.discoverCameras();
71 | }
72 | }
73 | };
74 |
75 | public SynoBridgeHandler(Bridge bridge, HttpClient httpClient) {
76 | super(bridge);
77 | config = getConfigAs(SynoConfig.class);
78 |
79 | apiHandler = new SynoWebApiHandler(config, httpClient);
80 | threads.put(SynoApiThread.THREAD_HOMEMODE, new SynoApiThreadHomeMode(this, config.getRefreshRateEvents()));
81 | try {
82 | reconnect(false);
83 | } catch (WebApiException e) {
84 | }
85 | }
86 |
87 | @Override
88 | @Nullable
89 | public SynoWebApiHandler getSynoWebApiHandler() {
90 | return apiHandler;
91 | }
92 |
93 | @Override
94 | public void handleCommand(ChannelUID channelUID, Command command) {
95 | try {
96 | switch (channelUID.getId()) {
97 | case CHANNEL_HOMEMODE:
98 | if (command.toString().equals("REFRESH")) {
99 | threads.get(SynoApiThread.THREAD_HOMEMODE).runOnce();
100 | } else {
101 | boolean state = command.toString().equals("ON");
102 | apiHandler.getApiHomeMode().setHomeMode(state);
103 | }
104 | break;
105 | case CHANNEL_EVENT_TRIGGER:
106 | if (command.toString().equals("REFRESH")) {
107 | updateState(channelUID, UnDefType.UNDEF);
108 | } else {
109 | int event = Integer.parseInt(command.toString());
110 | boolean ret = false;
111 | if (event >= 1 && event <= 10) {
112 | ret = apiHandler.getApiExternalEvent().triggerEvent(event);
113 | }
114 | updateState(channelUID, ret ? new DecimalType(0) : UnDefType.UNDEF);
115 | }
116 | break;
117 | case CHANNEL_SID:
118 | if (command.toString().equals("REFRESH")) {
119 | updateState(channelUID, new StringType(apiHandler.getSessionID()));
120 | }
121 | break;
122 | }
123 | } catch (Exception e) {
124 | logger.error("handle command: {}::{}", getThing().getLabel(), getThing().getUID());
125 | }
126 | }
127 |
128 | public void setDiscovery(CameraDiscoveryService discoveryService) {
129 | this.discoveryService = discoveryService;
130 | }
131 |
132 | @Override
133 | public boolean reconnect(boolean forceLogout) throws WebApiException {
134 | if (refreshInProgress.compareAndSet(false, true)) {
135 | boolean ret = false;
136 | try {
137 | ret = apiHandler.connect(forceLogout);
138 | try {
139 | Thread.sleep(1000);
140 | } catch (InterruptedException e) {
141 | }
142 | refreshInProgress.set(false);
143 | } catch (WebApiException e) {
144 | refreshInProgress.set(false);
145 | throw e;
146 | }
147 | if (ret && isInitialized()) {
148 | handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SID), RefreshType.REFRESH);
149 | }
150 | return ret;
151 | } else {
152 | logger.debug("Reconnect already in progress...");
153 | return false;
154 | }
155 | }
156 |
157 | @Override
158 | public void initialize() {
159 | try {
160 | if (logger.isDebugEnabled()) {
161 | logger.debug("Initialize thing: {}::{}", getThing().getLabel(), getThing().getUID());
162 | }
163 |
164 | if (!getConfigAs(SynoConfig.class).equals(config)) {
165 | config = getConfigAs(SynoConfig.class);
166 | apiHandler.setConfig(config);
167 | reconnect(false);
168 | }
169 |
170 | // if needed add other infos
171 | // InfoResponse infoResponse = apiHandler.getInfo();
172 | // getThing().setProperty(SynoApiResponse.PROP_CAMERANUMBER,
173 | // infoResponse.getData().get(SynoApiResponse.PROP_CAMERANUMBER).getAsString());
174 |
175 | for (SynoApiThread thread : threads.values()) {
176 | thread.start();
177 | }
178 |
179 | updateStatus(ThingStatus.ONLINE);
180 | handleCommand(new ChannelUID(getThing().getUID(), CHANNEL_SID), RefreshType.REFRESH);
181 |
182 | // Trigger discovery of cameras
183 | scheduler.submit(runnable);
184 |
185 | } catch (WebApiException e) {
186 | if (e.getCause() instanceof java.util.concurrent.TimeoutException) {
187 | updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection timeout");
188 | } else if (e.getErrorCode() == 400) {
189 | updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
190 | "Please add or check your credentials");
191 | } else {
192 | updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
193 | "Errorcode: " + e.getErrorCode());
194 | }
195 | }
196 | }
197 |
198 | @Override
199 | public void dispose() {
200 | for (SynoApiThread thread : threads.values()) {
201 | thread.stop();
202 | }
203 | try {
204 | apiHandler.disconnect();
205 | } catch (WebApiException e) {
206 | logger.error("Error disconnecting: ", e);
207 | }
208 | }
209 |
210 | @Override
211 | public void handleConfigurationUpdate(Map configurationParameters) {
212 | Configuration configuration = editConfiguration();
213 |
214 | for (Entry configurationParameter : configurationParameters.entrySet()) {
215 | configuration.put(configurationParameter.getKey(), configurationParameter.getValue());
216 | }
217 | SynoConfig oldConfig = thing.getConfiguration().as(SynoConfig.class);
218 | SynoConfig newConfig = configuration.as(SynoConfig.class);
219 |
220 | if (!oldConfig.equals(newConfig)) {
221 | if (oldConfig.equalsButForRefresh(newConfig)) {
222 | updateConfiguration(configuration);
223 | threads.get(SynoApiThread.THREAD_HOMEMODE).setRefreshRate(newConfig.getRefreshRateEvents());
224 | } else {
225 | super.handleConfigurationUpdate(configurationParameters);
226 | }
227 | }
228 | }
229 |
230 | @Override
231 | public boolean isLinked(String channelId) {
232 | return super.isLinked(channelId);
233 | }
234 |
235 | @Override
236 | public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
237 | super.updateStatus(status, statusDetail, description);
238 | }
239 |
240 | @Override
241 | public void updateStatus(ThingStatus status) {
242 | super.updateStatus(status);
243 | }
244 |
245 | @Override
246 | public void updateState(ChannelUID channelUID, State state) {
247 | super.updateState(channelUID, state);
248 | }
249 |
250 | /**
251 | * @return service scheduler of this Thing
252 | */
253 | @Override
254 | public ScheduledExecutorService getScheduler() {
255 | return scheduler;
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Synology Surveillance Station Binding
2 |
3 | This binding connects openHAB with your surveillance cameras running on Synology© DiskStation using Synology Surveillance Station API. This binding should work with any DiskStation capable of running Surveillance Station as well as with any supported camera.
4 |
5 | # Table of contents
6 |
7 | - [Synology Surveillance Station Binding](#synology-surveillance-station-binding)
8 | - [Table of contents](#table-of-contents)
9 | - [Disclaimer](#disclaimer)
10 | - [Installation and upgrade](#installation-and-upgrade)
11 | - [Supported Things](#supported-things)
12 | - [Discovery](#discovery)
13 | - [Configuration](#configuration)
14 | - [Channels](#channels)
15 | - [File based configuration](#file-based-configuration)
16 | - [.things](#things)
17 | - [.items](#items)
18 | - [.sitemap](#sitemap)
19 | - [Transformation](#transformation)
20 | - [.items](#items-1)
21 | - [transform/liveuri.js](#transformliveurijs)
22 | - [Support](#support)
23 | ***
24 |
25 | ## Disclaimer
26 |
27 | This binding is currently under development. Your help and testing would be greatly appreciated but there is no stability or functionality warranty.
28 |
29 | ## Installation and upgrade
30 |
31 | For an installation the [latest release](https://github.com/nibi79/synologysurveillancestation/releases) should be copied into the /addons folder of your openHAB installation.
32 | For an upgrade the existing file should be overwritten. On major or structural changes existing things might have to be deleted and recreated, existing channels might be kept. For further information please read release notes of a corresponding release.
33 |
34 | ## Supported Things
35 |
36 | Currently following Things are supported:
37 |
38 | - **Bridge** Thing representing the Synology DiskStation / Surveillance Station
39 | - One or many Things for supported **Cameras**
40 |
41 | ## Discovery
42 |
43 | If your openHAB installation is in the same network as your Synology DiskStation, the discovery will automatically find your Surveillance Station. You can now add its **Bridge** Thing, which will first be _OFFLINE_. You should now configure the **Bridge** and enter your Surveillance Station credentials. For security reasons it's always a better practice to create a separate user for this. After entering correct credentials and updating the **Bridge**, automatic discovery for **cameras** will start. If successful, your **cameras** will be found and can be added without further configuration.
44 |
45 | ## Configuration
46 |
47 | Following options can be set for the **Bridge**:
48 |
49 | - Access protocol of the DiskStation
50 | - Host/IP of the DiskStation
51 | - Port of the DiskStation
52 | - User name for the DiskStation / Surveillance Station
53 | - Password for the DiskStation / Surveillance Station
54 | - Refresh rate for DiskStation events (Home Mode)
55 | - (**advanced**) Enable support for self-signed / invalid SSL certificates (binding or openHAB restart required on change)
56 |
57 | Following options can be set for the **Camera**:
58 |
59 | - Snapshot refresh rate
60 | - Refresh rate for all other **Camera** events and dynamic channels
61 | - Refresh rate for motion detection parameter (defaults to 0 = no autorefresh)
62 |
63 | ## Channels
64 |
65 | Currently following **Channels** are supported on the **Bridge**:
66 |
67 | - Home mode _SWITCH_
68 | - External event trigger _NUMBER_ (1 to 10, write-only)
69 | - Current session ID (SID) _STRING_
70 |
71 | Currently following **Channels** are supported on the **Camera**:
72 |
73 | - Common channels:
74 | - Snapshot _IMAGE_
75 | - Camera recording _SWITCH_
76 | - Enable camera _SWITCH_
77 | - URIs:
78 | - Snapshot static URI _STRING_
79 | - Snapshot dynamic URI (refreshes with event refresh rate) _STRING_
80 | - Snapshot static live feed URI (rtsp) _STRING_
81 | - Snapshot static live feed URI (mjpeg over http) _STRING_
82 | - PTZ (Pan/Tilt/Zoom) for PTZ cameras only:
83 | - Zoom `IN`/`OUT`
84 | - Move `UP`/`DOWN`/`LEFT`/`RIGHT`/`HOME`
85 | - Continuous Move/Zoom with `START_` and `STOP_` (e.g. `START_IN`)
86 | - Move to preset
87 | - Run patrol
88 | - Event channels:
89 | - Motion event _SWITCH_ (read-only)
90 | - Alarm event _SWITCH_ (read-only)
91 | - Manual event _SWITCH_ (read-only)
92 | - Continuous recording event _SWITCH_ (read-only)
93 | - External event _SWITCH_ (read-only)
94 | - Action rule event _SWITCH_ (read-only)
95 | - Motion detection channels (if available):
96 | - Motion detection source _STRING_ (-1:disable, 0:by camera, 1:by Surveillance Station)
97 | - Motion detection sensitivity _NUMBER_ (1 to 99)
98 | - Motion detection threshold _NUMBER_ (1 to 99)
99 | - Motion detection object size _NUMBER_ (1 to 99)
100 | - Motion detection percentage _NUMBER_ (1 to 99)
101 | - Ignore short-lived motion for _NUMBER_ (0 to 10) seconds
102 |
103 | ## File based configuration
104 |
105 | ### .things
106 |
107 | ```
108 | Bridge synologysurveillancestation:station:diskstation "DiskStation" @ "ServerRoom" [ protocol="http", host="192.168.0.1", port="5000", username="my username", password="my password", acceptSsl="false" ] {
109 | Thing camera CameraID "Camera 1" @ "Outside" [ refreshRateEvents=5, refreshRateSnapshot=10, refreshRateMdParam=120, snapshotStreamId=1 ]
110 | }
111 | ```
112 |
113 | or for a self-signed SSL certificate:
114 |
115 | ```
116 | Bridge synologysurveillancestation:station:diskstation "DiskStation" @ "ServerRoom" [ protocol="https", host="192.168.0.1", port="5001", username="my username", password="my password", acceptSsl="true" ] {
117 | Thing camera CameraID "Camera 1" @ "Outside" [ refreshRateEvents=5, refreshRateSnapshot=10, refreshRateMdParam=120, snapshotStreamId=1 ]
118 | }
119 | ```
120 |
121 | Here the **CameraID** is a numeric ID of your surveillance camera in Surveillance Station (e.g. 1) and snapshot stream ID is the ID of the preferred stream in Surveillance Station (e.g. 1 for 'Stream 1')
122 |
123 | ### .items
124 |
125 | ```
126 | Switch Surveillance_HomeMode "Home Mode" {channel="synologysurveillancestation:station:diskstation:homemode"}
127 | Number:Dimensionless Surveillance_Event_Trigger "External event trigger" {channel="synologysurveillancestation:station:diskstation:eventtrigger"}
128 | String Surveillance_SID "Current SID" {channel="synologysurveillancestation:station:diskstation:sid"}
129 |
130 | Image Surveillance_Snapshot "Snapshot" {channel="synologysurveillancestation:camera:diskstation:1:common#snapshot"}
131 |
132 | String Surveillance_Snapshot_Uri_Dynamic "Dynamic snapshot URI" {channel="synologysurveillancestation:camera:diskstation:1:common#snapshot-uri-dynamic"}
133 | String Surveillance_Snapshot_Uri_Static "Static snapshot URI" {channel="synologysurveillancestation:camera:diskstation:1:common#snapshot-uri-static"}
134 | String Surveillance_Snapshot_Live_Uri_Rtsp "Live feed URI (rtsp)" {channel="synologysurveillancestation:camera:diskstation:1:common#live-uri-rtsp"}
135 | String Surveillance_Snapshot_Live_Uri_Mjpeg_Http "Live feed URI (mjpeg over http)" {channel="synologysurveillancestation:camera:diskstation:1:common#live-uri-mjpeg-http"}
136 |
137 | Switch Surveillance_Recording "Camera recording" {channel="synologysurveillancestation:camera:diskstation:1:common#record"}
138 | Switch Surveillance_Enabled "Camera enabled" {channel="synologysurveillancestation:camera:diskstation:1:common#enable"}
139 |
140 | Switch Surveillance_Event_Motion "Camera motion event" {channel="synologysurveillancestation:camera:diskstation:1:event#motion"}
141 | Switch Surveillance_Event_Alarm "Camera alarm event" {channel="synologysurveillancestation:camera:diskstation:1:event#alarm"}
142 | Switch Surveillance_Event_Manual "Camera manual event" {channel="synologysurveillancestation:camera:diskstation:1:event#manual"}
143 | Switch Surveillance_Event_Continuous "Camera continuous recording event" {channel="synologysurveillancestation:camera:diskstation:1:event#continuous"}
144 | Switch Surveillance_Event_External "Camera external event" {channel="synologysurveillancestation:camera:diskstation:1:event#external"}
145 | Switch Surveillance_Event_ActionRule "Camera action rule event" {channel="synologysurveillancestation:camera:diskstation:1:event#actionrule"}
146 |
147 | String Surveillance_Zooming "Camera zooming" {channel="synologysurveillancestation:camera:diskstation:1:ptz#zoom"}
148 | String Surveillance_Moving "Camera moving" {channel="synologysurveillancestation:camera:diskstation:1:ptz#move"}
149 | String Surveillance_Presets "Camera moving to preset" {channel="synologysurveillancestation:camera:diskstation:1:ptz#movepreset"}
150 | String Surveillance_Patrols "Camera run patrol" {channel="synologysurveillancestation:camera:diskstation:1:ptz#runpatrol"}
151 |
152 | String Surveillance_MD_Source "Motion detection source" {channel="synologysurveillancestation:camera:diskstation:1:md-param#md-param-source"}
153 | Number:Dimensionless Surveillance_MD_Sensitivity "Motion detection sensitivity" {channel="synologysurveillancestation:camera:diskstation:1:md-param#md-param-sensitivity"}
154 | Number:Dimensionless Surveillance_MD_Threshold "Motion detection threshold" {channel="synologysurveillancestation:camera:diskstation:1:md-param#md-param-threshold"}
155 | Number:Dimensionless Surveillance_MD_Objectsize "Motion detection objectsize" {channel="synologysurveillancestation:camera:diskstation:1:md-param#md-param-objectsize"}
156 | Number:Dimensionless Surveillance_MD_Percentage "Motion detection percentage" {channel="synologysurveillancestation:camera:diskstation:1:md-param#md-param-percentage"}
157 | Number:Dimensionless Surveillance_MD_Shortlive "Ignore short-lived motion" {channel="synologysurveillancestation:camera:diskstation:1:md-param#md-param-shortlive"}
158 | ```
159 |
160 | Here `:1` is yet again the numeric ID of your surveillance camera from a previous step.
161 |
162 | ### .sitemap
163 |
164 | ```
165 | Switch item=Surveillance_Zooming mappings=[IN="IN", OUT="OUT"]
166 |
167 | // Some cameras like Reolink do not support simple stepping
168 | Switch item=Surveillance_ContinuousZoomingIn mappings=[START_IN="Start ZoomIn", STOP_IN="Stop ZoomIn"]
169 | Switch item=Surveillance_ContinuousZoomingOut mappings=[START_OUT="Start ZoomOut", STOP_OUT="Stop ZoomOut"]
170 |
171 | Switch item=Surveillance_Moving mappings=[UP="UP", DOWN="DOWN", LEFT="LEFT", RIGHT="RIGHT"]
172 |
173 | Selection item=Surveillance_Presets label="Surveillance_Presets Selection"
174 | Switch item=Surveillance_Presets label="Surveillance_Presets Mapping" mappings=[preset1="Preset 1",preset2="Preset 2"]
175 |
176 | Image item=Surveillance_Snapshot_Uri_Static url="[%s]" refresh=5000
177 | Video item=Surveillance_Snapshot_Live_Uri_Mjpeg_Http url="[%s]" encoding="mjpeg"
178 | ```
179 |
180 | ## Transformation
181 |
182 | Existing URIs can also be transformed using JS transformation to build similar URIs. Most requests can be extended or constructed manually using SID (session ID) for authentication by adding `&_sid=your-current-SID` to the query string. Please refer to [Synology Surveillance Station API documentation](https://global.download.synology.com/download/Document/DeveloperGuide/Surveillance_Station_Web_API_v2.8.pdf) for more details.
183 |
184 | Example: SID-based stream URI (over http).
185 |
186 | ### .items
187 |
188 | ```
189 | String Surveillance_Snapshot_Live_Uri_Static "SID-based URI" {channel="synologysurveillancestation:camera:diskstation:1:common#snapshot-uri-static"[profile="transform:JS", function="liveuri.js"]}
190 | ```
191 |
192 | ### transform/liveuri.js
193 |
194 | ```
195 | (function(i) {
196 | return i.replace("entry.cgi", "SurveillanceStation/videoStreaming.cgi").replace(".Camera", ".VideoStream").replace("version=8", "version=1").replace("GetSnapshot", "Stream&format=mjpeg")
197 | })(input)
198 | ```
199 |
200 | Please note, **[Javascript Transformation](https://www.openhab.org/addons/transformations/javascript/)** add-on must be installed for the transformation to work properly.
201 |
202 | ## Support
203 |
204 | If you encounter critical issues with this binding, please consider to:
205 |
206 | - create an [issue](https://github.com/nibi79/synologysurveillancestation/issues) on GitHub
207 | - search [community forum](https://community.openhab.org/t/binding-request-synology-surveillance-station/8200) for answers already given
208 | - or make a new post there, if nothing was found
209 |
210 | In any case please provide some information about your problem:
211 |
212 | - openHAB and binding version
213 | - error description and steps to retrace if applicable
214 | - any related `[WARN]`/`[ERROR]` from openhab.log
215 | - whether it's the binding, bridge, camera or channel related issue
216 |
217 | For the sake of documentation please use English language.
218 |
--------------------------------------------------------------------------------
/src/main/java/org/openhab/binding/synologysurveillancestation/internal/webapi/request/SynoApiPTZ.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010-2024 Contributors to the openHAB project
3 | *
4 | * See the NOTICE file(s) distributed with this work for additional
5 | * information.
6 | *
7 | * This program and the accompanying materials are made available under the
8 | * terms of the Eclipse Public License 2.0 which is available at
9 | * http://www.eclipse.org/legal/epl-2.0
10 | *
11 | * SPDX-License-Identifier: EPL-2.0
12 | */
13 | package org.openhab.binding.synologysurveillancestation.internal.webapi.request;
14 |
15 | import static org.openhab.binding.synologysurveillancestation.SynoBindingConstants.*;
16 |
17 | import java.util.HashMap;
18 | import java.util.Map;
19 |
20 | import org.eclipse.jdt.annotation.NonNullByDefault;
21 | import org.eclipse.jetty.client.HttpClient;
22 | import org.openhab.binding.synologysurveillancestation.internal.SynoConfig;
23 | import org.openhab.binding.synologysurveillancestation.internal.webapi.WebApiException;
24 | import org.openhab.binding.synologysurveillancestation.internal.webapi.error.WebApiAuthErrorCodes;
25 | import org.openhab.binding.synologysurveillancestation.internal.webapi.response.SimpleResponse;
26 |
27 | /**
28 | * SYNO.SurveillanceStation.SynoApiPTZ
29 | *
30 | * This API provides a set of methods to execute PTZ action, and to acquire PTZ related information such as
31 | * patrol list or patrol schedule of a camera.
32 | *
33 | * Method:
34 | * - Move
35 | * - Zoom
36 | * - ListPreset
37 | * - GoPreset
38 | * - ListPatrol
39 | * - RunPatrol
40 | * - Focus
41 | * - Iris
42 | * - AutoFocus
43 | * - AbsPtz
44 | * - Home
45 | * - AutoPan
46 | * - ObjTracking
47 | *
48 | * @author Nils - Initial contribution
49 | * @author Pavion - Contribution
50 | */
51 | @NonNullByDefault
52 | public class SynoApiPTZ extends SynoApiRequest {
53 |
54 | // API configuration
55 | private static final String API_NAME = "SYNO.SurveillanceStation.PTZ";
56 | private static final SynoApiConfig API_CONFIG = new SynoApiConfig(API_NAME, API_VERSION_03, API_SCRIPT_ENTRY);
57 |
58 | /**
59 | * @param config
60 | */
61 | public SynoApiPTZ(SynoConfig config, HttpClient httpClient) {
62 | super(API_CONFIG, config, httpClient);
63 | }
64 |
65 | /**
66 | * Execute the given PTZ method for the passed camera.
67 | *
68 | * @param cameraId
69 | * @param method
70 | * @param command
71 | * @throws WebApiException
72 | */
73 | public void execute(String cameraId, String method, String command) throws WebApiException {
74 | switch (method) {
75 | case CHANNEL_ZOOM:
76 | switch (command) {
77 | case "IN":
78 | zoomIn(cameraId, MOVE_COMMAND_EMPTY);
79 | break;
80 | case "OUT":
81 | zoomOut(cameraId, MOVE_COMMAND_EMPTY);
82 | break;
83 | // START
84 | case "START_IN":
85 | zoomIn(cameraId, MOVE_COMMAND_START);
86 | break;
87 | case "START_OUT":
88 | zoomOut(cameraId, MOVE_COMMAND_START);
89 | break;
90 | // STOP
91 | case "STOP_IN":
92 | zoomIn(cameraId, MOVE_COMMAND_STOP);
93 | break;
94 | case "STOP_OUT":
95 | zoomOut(cameraId, MOVE_COMMAND_STOP);
96 | break;
97 | }
98 | break;
99 | case CHANNEL_MOVE:
100 | switch (command) {
101 | case "UP":
102 | moveUp(cameraId, MOVE_COMMAND_EMPTY);
103 | break;
104 | case "DOWN":
105 | moveDown(cameraId, MOVE_COMMAND_EMPTY);
106 | break;
107 | case "LEFT":
108 | moveLeft(cameraId, MOVE_COMMAND_EMPTY);
109 | break;
110 | case "RIGHT":
111 | moveRight(cameraId, MOVE_COMMAND_EMPTY);
112 | break;
113 | case "HOME":
114 | moveHome(cameraId, MOVE_COMMAND_EMPTY);
115 | break;
116 | // START
117 | case "START_UP":
118 | moveUp(cameraId, MOVE_COMMAND_START);
119 | break;
120 | case "START_DOWN":
121 | moveDown(cameraId, MOVE_COMMAND_START);
122 | break;
123 | case "START_LEFT":
124 | moveLeft(cameraId, MOVE_COMMAND_START);
125 | break;
126 | case "START_RIGHT":
127 | moveRight(cameraId, MOVE_COMMAND_START);
128 | break;
129 | case "START_HOME":
130 | moveHome(cameraId, MOVE_COMMAND_START);
131 | // STOP
132 | case "STOP_UP":
133 | moveUp(cameraId, MOVE_COMMAND_STOP);
134 | break;
135 | case "STOP_DOWN":
136 | moveDown(cameraId, MOVE_COMMAND_STOP);
137 | break;
138 | case "STOP_LEFT":
139 | moveLeft(cameraId, MOVE_COMMAND_STOP);
140 | break;
141 | case "STOP_RIGHT":
142 | moveRight(cameraId, MOVE_COMMAND_STOP);
143 | break;
144 | case "STOP_HOME":
145 | moveHome(cameraId, MOVE_COMMAND_STOP);
146 | }
147 | break;
148 | default:
149 | break;
150 | }
151 | }
152 |
153 | /**
154 | * calls api method 'zoom' with passed control
155 | *
156 | * @param cameraId
157 | * @param control
158 | * @param moveType
159 | * @return
160 | * @throws WebApiException
161 | */
162 | private SimpleResponse callZoom(String cameraId, String control, String moveType) throws WebApiException {
163 | Map params = new HashMap<>();
164 |
165 | // API parameters
166 | params.put("cameraId", cameraId);
167 | params.put("control", control);
168 | if (!moveType.equals(MOVE_COMMAND_EMPTY)) {
169 | Integer version = Integer.parseInt(API_CONFIG.getVersion());
170 | if (version < 3) {
171 | throw new WebApiException(WebApiAuthErrorCodes.API_VERSION_NOT_SUPPORTED);
172 | }
173 | params.put("moveType", moveType);
174 | }
175 |
176 | return callApi(METHOD_ZOOM, params);
177 | }
178 |
179 | /**
180 | * calls api method 'move' with passed direction and speed
181 | *
182 | * @param cameraId
183 | * @param direction
184 | * @param speed
185 | * @param moveType
186 | * @return
187 | * @throws WebApiException
188 | */
189 | private SimpleResponse callMove(String cameraId, String direction, int speed, String moveType)
190 | throws WebApiException {
191 | Map params = new HashMap<>();
192 |
193 | // API Parameters
194 | params.put("cameraId", cameraId);
195 | params.put("direction", direction);
196 | params.put("speed", String.valueOf(speed));
197 | if (!moveType.equals(MOVE_COMMAND_EMPTY)) {
198 | Integer version = Integer.parseInt(API_CONFIG.getVersion());
199 | if (version < 3) {
200 | throw new WebApiException(WebApiAuthErrorCodes.API_VERSION_NOT_SUPPORTED);
201 | }
202 | params.put("moveType", moveType);
203 | }
204 |
205 | return callApi(METHOD_MOVE, params);
206 | }
207 |
208 | /**
209 | * Control the PTZ camera to zoom out.
210 | *
211 | * @param cameraId
212 | * @return
213 | * @throws WebApiException
214 | */
215 | public SimpleResponse zoomOut(String cameraId, String moveType) throws WebApiException {
216 | return callZoom(cameraId, "out", moveType);
217 | }
218 |
219 | /**
220 | * Control the PTZ camera to zoom in.
221 | *
222 | * @param cameraId
223 | * @return
224 | * @throws WebApiException
225 | */
226 | public SimpleResponse zoomIn(String cameraId, String moveType) throws WebApiException {
227 | return callZoom(cameraId, "in", moveType);
228 | }
229 |
230 | /**
231 | * Control the PTZ camera to move its lens up.
232 | *
233 | * @param cameraId
234 | * @return
235 | * @throws WebApiException
236 | */
237 | public SimpleResponse moveUp(String cameraId, String moveType) throws WebApiException {
238 | return callMove(cameraId, "up", 1, moveType);
239 | }
240 |
241 | /**
242 | * Control the PTZ camera to move its lens down.
243 | *
244 | * @param cameraId
245 | * @return
246 | * @throws WebApiException
247 | */
248 | public SimpleResponse moveDown(String cameraId, String moveType) throws WebApiException {
249 | return callMove(cameraId, "down", 1, moveType);
250 | }
251 |
252 | /**
253 | * Control the PTZ camera to move its lens left.
254 | *
255 | * @param cameraId
256 | * @return
257 | * @throws WebApiException
258 | */
259 | public SimpleResponse moveLeft(String cameraId, String moveType) throws WebApiException {
260 | return callMove(cameraId, "left", 1, moveType);
261 | }
262 |
263 | /**
264 | * Control the PTZ camera to move its lens right.
265 | *
266 | * @param cameraId
267 | * @return
268 | * @throws WebApiException
269 | */
270 | public SimpleResponse moveRight(String cameraId, String moveType) throws WebApiException {
271 | return callMove(cameraId, "right", 1, moveType);
272 | }
273 |
274 | /**
275 | * Control the PTZ camera to move to preset HOME.
276 | *
277 | * @param cameraId
278 | * @return
279 | * @throws WebApiException
280 | */
281 | public SimpleResponse moveHome(String cameraId, String moveType) throws WebApiException {
282 | return callMove(cameraId, "home", 1, moveType);
283 | }
284 |
285 | /**
286 | * calls api method 'ListPreset' and list all presets for moving.
287 | *
288 | * @param cameraId
289 | * @return
290 | * @throws WebApiException
291 | */
292 | public SimpleResponse listPresets(String cameraId) throws WebApiException {
293 | Map params = new HashMap<>();
294 |
295 | // API Parameters
296 | params.put("cameraId", cameraId);
297 |
298 | SimpleResponse response = callApi(METHOD_LISTPRESET, params);
299 |
300 | return response;
301 | }
302 |
303 | /**
304 | * calls api method 'GoPreset' and move the camera lens to a pre-defined preset position.
305 | *
306 | * @param cameraId
307 | * @param presetId
308 | * @return
309 | * @throws WebApiException
310 | */
311 | public SimpleResponse goPreset(String cameraId, String presetId) throws WebApiException {
312 | Map params = new HashMap<>();
313 |
314 | // API Parameters
315 | params.put("cameraId", cameraId);
316 | params.put("presetId", presetId);
317 | // params.put("position", ???);
318 | // params.put("speed", ???);
319 | // params.put("type", ???);
320 | // params.put("isPatrol", ???);
321 |
322 | SimpleResponse response = callApi(METHOD_GOPRESET, params);
323 |
324 | return response;
325 | }
326 |
327 | /**
328 | * calls api method 'ListPatrol' and list all patrols.
329 | *
330 | * @param cameraId
331 | * @return
332 | * @throws WebApiException
333 | */
334 | public SimpleResponse listPatrol(String cameraId) throws WebApiException {
335 | Map params = new HashMap<>();
336 |
337 | // API Parameters
338 | params.put("cameraId", cameraId);
339 |
340 | SimpleResponse response = callApi(METHOD_LISTPATROL, params);
341 |
342 | return response;
343 | }
344 |
345 | /**
346 | * calls api method 'RunPatrol' and execute the given patrol.
347 | *
348 | * @param cameraId
349 | * @param patrolId
350 | * @return
351 | * @throws WebApiException
352 | */
353 | public SimpleResponse runPatrol(String cameraId, String patrolId) throws WebApiException {
354 | Map params = new HashMap<>();
355 |
356 | // API Parameters
357 | params.put("cameraId", cameraId);
358 | params.put("patrolId", patrolId);
359 |
360 | SimpleResponse response = callApi(METHOD_RUNPATROL, params);
361 |
362 | return response;
363 | }
364 | }
365 |
--------------------------------------------------------------------------------