diff --git a/configuration/chirpstack-gateway-bridge/chirpstack-gateway-bridge-basicstation-eu868.toml b/configuration/chirpstack-gateway-bridge/chirpstack-gateway-bridge-basicstation-eu868.toml new file mode 100644 index 00000000..9b7b14af --- /dev/null +++ b/configuration/chirpstack-gateway-bridge/chirpstack-gateway-bridge-basicstation-eu868.toml @@ -0,0 +1,21 @@ +# Basic Station configuration for WebSocket gateway connections +[general] + log_level=4 + +[integration.mqtt] + server="tcp://mosquitto:1883" + event_topic="eu868/gateway/{{ .GatewayID }}/event/{{ .EventType }}" + state_topic="eu868/gateway/{{ .GatewayID }}/state/{{ .StateType }}" + command_topic="eu868/gateway/{{ .GatewayID }}/command/#" + json=true + +[backend] + type="basic_station" + [backend.basic_station] + bind=":3001" + tls_cert="" + tls_key="" + ca_cert="" + region="EU868" + frequency_min=863000000 + frequency_max=870000000 diff --git a/configuration/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml b/configuration/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml new file mode 100644 index 00000000..899a4687 --- /dev/null +++ b/configuration/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml @@ -0,0 +1,11 @@ +# ChirpStack Gateway Bridge configuration (EU868) +[general] + log_level=4 + +[integration.mqtt] + server="tcp://mosquitto:1883" + event_topic="eu868/gateway/{{ .GatewayID }}/event/{{ .EventType }}" + state_topic="eu868/gateway/{{ .GatewayID }}/state/{{ .StateType }}" + command_topic="eu868/gateway/{{ .GatewayID }}/command/#" + json=true + client_id="chirpstack-gateway-bridge" diff --git a/configuration/chirpstack/chirpstack.toml b/configuration/chirpstack/chirpstack.toml new file mode 100644 index 00000000..96b44e5b --- /dev/null +++ b/configuration/chirpstack/chirpstack.toml @@ -0,0 +1,26 @@ +[logging] + level="info" + +[postgresql] + dsn="postgres://chirpstack:chirpstack@postgres/chirpstack?sslmode=disable" + +[redis] + url="redis://redis:6379" + +[mqtt] + server="tcp://mosquitto:1883" + +[network] + net_id="000000" + enabled_regions=["eu868"] + +[api] + bind="0.0.0.0:8080" + secret="you-must-change-this-secret-value" + +[gateway] + client_cert_lifetime="90d" + +[monitoring] + # Prometheus metrics + prometheus_bind="0.0.0.0:9090" diff --git a/configuration/mosquitto/config/mosquitto.conf b/configuration/mosquitto/config/mosquitto.conf new file mode 100644 index 00000000..68931726 --- /dev/null +++ b/configuration/mosquitto/config/mosquitto.conf @@ -0,0 +1,16 @@ +listener 1883 +allow_anonymous true +persistence true +persistence_location /mosquitto/data/ +log_dest file /mosquitto/log/mosquitto.log + +# Bridge to EMQX for upstream integration +connection bridge-emqx +address emqx_emqx_1:1883 +topic eu868/# out 1 +topic application/# in 1 +bridge_protocol_version mqttv311 +cleansession true +try_private false +notifications false +remote_clientid chirpstack-bridge diff --git a/configuration/postgresql/initdb/01-chirpstack.sql b/configuration/postgresql/initdb/01-chirpstack.sql new file mode 100644 index 00000000..c65d492b --- /dev/null +++ b/configuration/postgresql/initdb/01-chirpstack.sql @@ -0,0 +1,2 @@ +-- Initialize ChirpStack database +CREATE DATABASE IF NOT EXISTS chirpstack; diff --git a/configuration/the-things-stack/config/ttn-lw-stack-docker.yml b/configuration/the-things-stack/config/ttn-lw-stack-docker.yml new file mode 100644 index 00000000..04423b03 --- /dev/null +++ b/configuration/the-things-stack/config/ttn-lw-stack-docker.yml @@ -0,0 +1,44 @@ +is: + database: + uri: postgres://root:root@tts-postgres:5432/ttn_lorawan?sslmode=disable + email: + sender-name: "The Things Stack" + sender-address: "noreply@digitribe.fr" + network: + name: "Smart City LoRaWAN" + console-url: "https://tts.digitribe.fr/console" + identity-server-url: "https://tts.digitribe.fr/oauth" + +redis: + address: tts-redis:6379 + +metrics: + enabled: true + +console: + base-url: "https://tts.digitribe.fr/console" + +http: + cookie: + block-key: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" + hash-key: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" + +gateway-server: + mqtt: + listen: ":1883" + public-address: "tts.digitribe.fr:1883" + +network-server: + net-id: "000000" + band: + name: "EU868" + +join-server: + default: + join-eui-prefix: "0000000000000000" + +tenant-id: "smart-city" + +blob: + local-directory: /srv/ttn-lorawan/public/blob + base-url: "https://tts.digitribe.fr/blob" diff --git a/docker-compose.chirpstack.yml b/docker-compose.chirpstack.yml index 60433cf9..967ef99d 100644 --- a/docker-compose.chirpstack.yml +++ b/docker-compose.chirpstack.yml @@ -33,9 +33,7 @@ services: - "traefik.http.services.chirpstack.loadbalancer.server.port=8080" networks: - traefik-public - - chirpstack-internal - smartcity-shared - chirpstack-gateway-bridge: image: chirpstack/chirpstack-gateway-bridge:4 restart: unless-stopped @@ -50,8 +48,7 @@ services: depends_on: - mosquitto networks: - - chirpstack-internal - + - smartcity-shared chirpstack-gateway-bridge-basicstation: image: chirpstack/chirpstack-gateway-bridge:4 restart: unless-stopped @@ -68,8 +65,7 @@ services: - mosquitto networks: - traefik-public - - chirpstack-internal - + - smartcity-shared chirpstack-rest-api: image: chirpstack/chirpstack-rest-api:4 restart: unless-stopped @@ -84,8 +80,7 @@ services: - chirpstack networks: - traefik-public - - chirpstack-internal - + - smartcity-shared postgres: image: postgres:14-alpine restart: unless-stopped @@ -97,8 +92,7 @@ services: - POSTGRES_PASSWORD=chirpstack - POSTGRES_DB=chirpstack networks: - - chirpstack-internal - + - smartcity-shared redis: image: redis:7-alpine restart: unless-stopped @@ -106,8 +100,7 @@ services: volumes: - chirpstack-redisdata:/data networks: - - chirpstack-internal - + - smartcity-shared mosquitto: image: eclipse-mosquitto:2 restart: unless-stopped @@ -116,9 +109,7 @@ services: - chirpstack-mosquitto-data:/mosquitto/data - chirpstack-mosquitto-log:/mosquitto/log networks: - - chirpstack-internal - smartcity-shared - volumes: chirpstack-postgresqldata: chirpstack-redisdata: @@ -130,5 +121,3 @@ networks: external: true smartcity-shared: external: true - chirpstack-internal: - driver: bridge diff --git a/docker-compose.the-things-stack.yml b/docker-compose.the-things-stack.yml index c9339361..f496cb35 100644 --- a/docker-compose.the-things-stack.yml +++ b/docker-compose.the-things-stack.yml @@ -20,8 +20,7 @@ services: volumes: - tts-postgres-data:/var/lib/postgresql/data networks: - - tts-internal - + - smartcity-shared tts-redis: image: redis:7 command: redis-server --appendonly yes @@ -29,8 +28,7 @@ services: volumes: - tts-redis-data:/data networks: - - tts-internal - + - smartcity-shared tts-stack: image: thethingsnetwork/lorawan-stack:latest entrypoint: ttn-lw-stack -c /config/ttn-lw-stack-docker.yml @@ -47,7 +45,7 @@ services: TTN_LW_REDIS_ADDRESS: tts-redis:6379 TTN_LW_IS_DATABASE_URI: postgres://root:***@tts-postgres:5432/ttn_lorawan?sslmode=disable ports: - - "1700:1700/udp" + - "1701:1700/udp" # ChirpStack uses 1700 labels: - "traefik.enable=true" # Console web @@ -62,9 +60,7 @@ services: - "traefik.http.services.tts-api.loadbalancer.server.port=1884" networks: - traefik-public - - tts-internal - smartcity-shared - volumes: tts-postgres-data: tts-redis-data: @@ -74,5 +70,3 @@ networks: external: true smartcity-shared: external: true - tts-internal: - driver: bridge diff --git a/simulator.py b/simulator.py index d6aec1a6..eeaa4cae 100644 --- a/simulator.py +++ b/simulator.py @@ -798,31 +798,17 @@ def _get_or_token() -> str: return "" def _or_put(asset_id: str, payload: dict) -> bool: - """PUT update sur un asset OpenRemote (avec version).""" + """PUT update sur un asset OpenRemote (sans If-Match pour éviter 403).""" token = _get_or_token() if not token: return False try: - # Récupérer la version actuelle de l'asset - get_url = f"{OR_URL}/api/{OR_REALM}/asset/{asset_id}" - get_req = urllib.request.Request(get_url, headers={"Authorization": f"Bearer {token}"}) - version = 1 - try: - with urllib.request.urlopen(get_req, timeout=5) as resp: - asset_data = json.loads(resp.read().decode()) - version = asset_data.get("version", 1) - except: - pass # Si GET échoue, utiliser version=1 - - # Ajouter la version au payload - payload["version"] = version body = json.dumps(payload).encode() url = f"{OR_URL}/api/{OR_REALM}/asset/{asset_id}" req = urllib.request.Request(url, data=body, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", - "If-Match": str(version), }, method="PUT") with http_request_duration.labels(broker="openremote", method="PUT").time():