HA 中遇到的deConz问题

前不久发现升级HA之后,deConz不能用了。所有的智能电器都趴窝了。昨天升级了HA,发现deConz又可以了用了。但是调节彩灯不如以前方便了。就找了一个HUE Essential的手机APP来控制。据说是HUE是支持deConz桥接的。但是API一直无法链接。后来看了这个帖子 https://community.hueessentials.com/t/connect-app-to-deconz-running-on-hassio-home-assistant/58/17 加上chatGPT 才搞定。

关键问题是

1 deConz的REST API 端口默认是 40850 是默认关闭的。必须修改配置(比如yaml)文件打开。可以通过这个地址验证 http://Your-HA-IP:40850/api 如果打开了,可以看到json的响应。
比如

[{"error":{"address":"/","description":"unauthorized user","type":1}}]

2 和HUE配对的时候,要通过Phoscon的设置,允许配对。具体位置是HA的Phoscon,选择settings,选择 Advanced,然后找到 connect 3rd party apps. 打开后有1分钟时间让你配对。

配对成功后就可以使用HUE Esseintial来控制灯泡了。

迁移服务商

前一段发现博客无法访问了,一直没有空去研究。这两天休假,终于迁移了到新的服务器上。旧的服务商用了19年了,实在不咋地,终于放弃了。
新的服务器使用容器。倒腾了一天,基本搞定,发帖纪念一下。

这两天遇到很奇怪的事情

收了一台2011 late的macbook pro (65 EUR). 安装high sierra到ssd的时候,发现装不上,重新启动后又总是回到安装程序,进不了系统,因为这块ssd以前装过open core legacy patch 怀疑是EFI分区损坏了,重新格式化,始终装不上。

无论使用USB的high sierra 还是 EL Captian,都是同样的问题。使用El captian安装到最后报告失败。

检查分区发现,硬盘上缺少了apple_boot分区。问了一下chatgpt,说这个是保存Recovery HD的分区,和启动有关。可是为啥会缺少呢,不知原因。

然后把 硬盘插到macpro上,要求安装el captian到此硬盘,一切顺利,没有问题。检查分区,发现一切正常。 3个分区都在。

然后装回到macbook pro上,重新启动,发现不能启动,显示文件夹图标。估计硬盘记录和硬件不符。然后插入High sierra 的usb启动,问题一样。唯一的进步是,能识别出硬盘上安装的分区了。

现在我怀疑是hight sierra的安装程序存在某种缺陷。

后来想到了个办法,先使用原来的硬盘启动,然后挂上usb的ssd,然后运行high sierra的安装程序,把系统安装在usb的ssd上。安装成功了。

从github迁移到Azure

有个php项目,开始时是保存在github的。现在要求迁移到azure。在azure使用的monorepo,就是很多repo在一个repo内。发现不行。
因为在php的composer.json里面,repostiory定义的是每个外部依赖的repo,只能定义一个repo,而不能是monorepo中的一个子repo。研究了很久不得法。放弃。
后来同时建议单独在azure创建一个repo,使之独立于monorepo。而且这个repo只作为保存代码之用,不要使用odj或者pipeline。

这样问题得以解决。



重装El Captian

由于系统盘损坏,需要要重装El Captian. 关键是如何制作一个启动U盘
教程在此 

mkdir -p /mnt/OSX_InstallESD /mnt/OSX_BaseSystem /mnt/usbstick

# convert installer disk image to raw format
dmg2img "Install OS X .app/Contents/SharedSupport/InstallESD.dmg" InstallESD.img
kpartx -a InstallESD.img
mount /dev/mapper/loop0p2 /mnt/OSX_InstallESD

# convert base system disk image to raw format
dmg2img /mnt/OSX_InstallESD/BaseSystem.dmg BaseSystem.img
kpartx -a BaseSystem.img
mount /dev/mapper/loop1p1 /mnt/OSX_BaseSystem

# partition the USB flash drive, /dev/sdX
sgdisk -o /dev/sdX
sgdisk -n 1:0:0 -t 1:AF00 -c 1:"disk image" -A 1:set:2 /dev/sdX
mkfs.hfsplus -v "OS X Base System" /dev/sdX1
mount /dev/sdX1 /mnt/usbstick

# copy installer files
rsync -aAEHW /mnt/OSX_BaseSystem/ /mnt/usbstick/
rm -f /mnt/usbstick/System/Installation/Packages
rsync -aAEHW /mnt/OSX_InstallESD/Packages /mnt/usbstick/System/Installation/
rsync -aAEHW /mnt/OSX_InstallESD/BaseSystem.chunklist /mnt/usbstick/
rsync -aAEHW /mnt/OSX_InstallESD/BaseSystem.dmg /mnt/usbstick/
sync

出处 https://askubuntu.com/questions/743075/how-to-make-a-bootable-osx-el-capitan-usb

然后需要一块能点亮的启动A卡。原装的我拆下了,还好没丢,重新装上引导系统的选择从U盘启动。

其他启动的按键组合可以参考这里: https://www.makeuseof.com/tag/boot-mode-startup-keys-mac/

然后需要转一些必要的软件,比如chromim浏览器,plex 都是一些需要特别老的版本。 好在有github都能找到。

初识DDD 之CQRS

最近在学习实践DDD,接触到一个新的概念CQRS。以前以为就是读写分离,是基础设施层数据库层面的事情。学习以后才发现,并不只是这样,更应该是App应用层面的事。

缘起从github上找了一个DDD的symfony的例子,想参考一下,用于重构目前的项目。看了介绍和代码以后,发现了他的作者的西班牙语的油管视频。结合自动字幕翻译,觉得还不错,然后看了一下他们写的书。原版要60欧,于是在github上下载了一本电子版 Domian Driven Desgin in PHP。以下是阅读的笔记。

MVC模式的缺点

MVC用了那么久了,最大问题是controller过于庞大。违反了分离原则(Separation of Concerns)。补救的办法是使用依赖倒置。由于controller包含了过多的领域内容。所以首先拿领域模型下手。很多领域模型依赖底层的基础架构,比如数据库。所以按照Bob大叔(Robert C. Martin)的依赖倒置原则,高级模块(领域)不该依赖低级模块(基础设施),而且应该让它依赖抽象,即提出Repository。然后六角形架构被提出了。所有才有后来的 CQRS Event Sourcing

初识DDD 之CQRS

写模 型 Write model.

也叫Command model, 1

被改造为基于事件的写模型。本来直接写入数据库动作,改为发布一个写入事件。

record -> apply -> publish

而且写入事件直接找领域模型里定义。

读模型 Read model

被简化为从ElasticSerach这样的缓存读取。

同步

即然读写分开了,那么两个模型如何实现同步呢。引入了新概念Projection (投射器?)。基本上就是监听事件。当符合的事件被监听到了,就会调用投射器的投射动作。

投射的过程很可能是异步的,就是说会有延迟。为了解决延迟,在Repo里的save方法内,创建一个transaction(过程),把存储,事件放在一起。然后用投射器投射保存的事件。

最终解决了读模型和写模型之间的一致性(consistencies)问题。

Evnet Sourcing 事件溯源

就是可以通过记录完整的event可以还原一个状态。有点像数据库的bin log。

Code Review 收获

今天请Ben对项目的重构Branch进行了Review。收获是很多的。

传入参数和返回值,尽量使用Interface

当使用match分支处理情况的时候,也要和switch类似,加上处理默认default的情况

避免使用动态生成的方法和类

动态的方法和类就是指名称包含变量的。比如

setFirstName(), setLastName()

这样的方法,为了偷懒,我使用set加上一个变量的名字的方法,比如 set$var()

这样的方法或者类不利于静态检查,应该避免使用。

Domain领域应该和外界尽可能的隔绝。

Domain内不该知道什么是REST,DB,gRPC等具体实现的技术概念

应该只使用传入的Interface的公开方法

简化复杂的DI的声明

使用静态分析工具 PhpStan

使用docker volumn共享卷以及同步

启动的时候,报错,说一个卷没有。需要手动创建。然后在docker compose里面看到,在多个容器中都使用了一个卷名。命令行中有提示,如何手动创建一个卷。

尝试创建后发现是空的,再看它还有一个docker-sync配置。然后安装docker-sync工具。制定后启动,然后它就留在后台运行了。

再启动容积,这次卷就被挂上了。

docker volume 命令

docker-sync 工具

在消息队列中使用Symfony的序列化

PHP中,如果需要把内存中的变量进行保存,以便后续继续使用,那么就要把数据序列化。

PHP提供了 serialize()  和 unserialize()  这两个原生方法来完成。但是这样有个限制,就是要求写入和读取的时候使用同样的方法。就是说只能使用PHP原生的反序列化函数来读取PHP原生序列化的数据。 有时候我们写入是PHP,读取的时候可能是别语言,所以需要一种更加通用的格式。

比如,在项目的写入队列的过程中,我们打算用Symfony提供的序列化器代替PHP原生的。我们选择序列化的格式为JSON。但是出现了问题,发现某些属性的数据丢失了。原来需要对序列化的对象进行处理。

要让消息能被JSON序列化需要确认以下

  • 消息对象需要实现一下特定接口 (非必要)
  • 消息对象能被正确encode和decode
  • 消息对象的构造函数必须为public,且只使用标量值。如果必须要使用类,需要确保这些类实现了stringable接口。

消息需要实现特定接口

SyncCommandInterface::class,
AsyncCommandInterface::class,
EventInterface::class,
QueryInterface::class,

具体措施

消息的构造函数必须为public,且使用仅使用标量作为参数

为什么消息对象的构造函数必须为public,为啥必须用标量参数。

我猜想,因为当消息被反序列化的时候,系统只会用最简单的new方法来重新创建消息实例。

  • public类的属性

使用readonly修饰属性防止意外修改

使用标量值

对象必须实现stringable

减少复杂性。比如不使用实体对象entity,而使用id. 这点在Symfony的文档内也有介绍。

参考